2

我已经在 android 应用程序中实现 VoIP 呼叫功能有一段时间了,大部分工作已经完成,但是当用户通过应用程序向另一个用户拨打电话时,大部分时间都是同一个人从它所在的地方接听电话初始化。

例如,有两个用户ABCCBA。当 ABC 呼叫 CBA 时,它是正确完成的,但是当情况相反时,即当 CBA 呼叫 ABC 时,在 Linphone 的传入方法中,它接收远程用户名为 CBA,这意味着 CBA 正在调用本身。

如何解决这个问题呢?我是否以错误的方式应用方法?

我已经完全使用了 Linphone Calling SDK for Android,甚至没有改变它的方法。

下面是接收来电的 LinphoneService 的代码。

public final class LinphoneService extends Service {
    /* Listener needs to be implemented in the Service as it calls
     * setLatestEventInfo and startActivity() which needs a context.
     */
    public static final String START_LINPHONE_LOGS = " ==== Phone information dump ====";

    private static LinphoneService instance;

    private final static int NOTIF_ID = 1;
    private final static int INCALL_NOTIF_ID = 2;
    private final static int MESSAGE_NOTIF_ID = 3;
    private final static int SAS_NOTIF_ID = 6;

    public static boolean isReady() {
        return instance != null && instance.mTestDelayElapsed;
    }

    /**
     * @throws RuntimeException service not instantiated
     */
    public static LinphoneService instance() {
        if (isReady()) return instance;

        throw new RuntimeException("LinphoneService not instantiated yet");
    }

    public Handler mHandler = new Handler();

    //  private boolean mTestDelayElapsed; // add a timer for testing
    private boolean mTestDelayElapsed = true; // no timer
    private NotificationManager mNM;


    private String mNotificationTitle;
    private boolean mDisableRegistrationStatus;
    private LinphoneCoreListenerBase mListener;
    public static int notifcationsPriority = (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41) ? Notification.PRIORITY_MIN : 0);
    private WindowManager mWindowManager;

    private Application.ActivityLifecycleCallbacks activityCallbacks;


    /*Believe me or not, but knowing the application visibility state on Android is a nightmare.
    After two days of hard work I ended with the following class, that does the job more or less reliabily.
    */
    class ActivityMonitor implements Application.ActivityLifecycleCallbacks {
        private ArrayList<Activity> activities = new ArrayList<Activity>();
        private boolean mActive = false;
        private int mRunningActivities = 0;

        class InactivityChecker implements Runnable {
            private boolean isCanceled;

            public void cancel() {
                isCanceled = true;
            }

            @Override
            public void run() {
                synchronized (LinphoneService.this) {
                    if (!isCanceled) {
                        if (LinphoneService.ActivityMonitor.this.mRunningActivities == 0 && mActive) {
                            mActive = false;
                            LinphoneService.this.onBackgroundMode();
                        }
                    }
                }
            }
        }

        ;

        private LinphoneService.ActivityMonitor.InactivityChecker mLastChecker;

        @Override
        public synchronized void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            Log.i("Activity created:" + activity);
            if (!activities.contains(activity))
                activities.add(activity);
        }

        @Override
        public void onActivityStarted(Activity activity) {
            Log.i("Activity started:" + activity);
        }

        @Override
        public synchronized void onActivityResumed(Activity activity) {
            Log.i("Activity resumed:" + activity);
            if (activities.contains(activity)) {
                mRunningActivities++;
                Log.i("runningActivities=" + mRunningActivities);
                checkActivity();
            }

        }

        @Override
        public synchronized void onActivityPaused(Activity activity) {
            Log.i("Activity paused:" + activity);
            if (activities.contains(activity)) {
                mRunningActivities--;
                Log.i("runningActivities=" + mRunningActivities);
                checkActivity();
            }

        }

        @Override
        public void onActivityStopped(Activity activity) {
            Log.i("Activity stopped:" + activity);
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public synchronized void onActivityDestroyed(Activity activity) {
            Log.i("Activity destroyed:" + activity);
            if (activities.contains(activity)) {
                activities.remove(activity);
            }
        }

        void startInactivityChecker() {
            if (mLastChecker != null) mLastChecker.cancel();
            LinphoneService.this.mHandler.postDelayed(
                    (mLastChecker = new LinphoneService.ActivityMonitor.InactivityChecker()), 2000);
        }

        void checkActivity() {

            if (mRunningActivities == 0) {
                if (mActive) startInactivityChecker();
            } else if (mRunningActivities > 0) {
                if (!mActive) {
                    mActive = true;
                    LinphoneService.this.onForegroundMode();
                }
                if (mLastChecker != null) {
                    mLastChecker.cancel();
                    mLastChecker = null;
                }
            }
        }
    }

    protected void onBackgroundMode() {
        Log.i("App has entered background mode");
        if (LinphonePreferences.instance() != null && LinphonePreferences.instance().isFriendlistsubscriptionEnabled()) {
            if (LinphoneManager.isInstanciated())
                LinphoneManager.getInstance().subscribeFriendList(false);
        }
    }

    protected void onForegroundMode() {
        Log.i("App has left background mode");
        if (LinphonePreferences.instance() != null && LinphonePreferences.instance().isFriendlistsubscriptionEnabled()) {
            if (LinphoneManager.isInstanciated())
                LinphoneManager.getInstance().subscribeFriendList(true);
        }
    }

    private void setupActivityMonitor() {
        if (activityCallbacks != null) return;
        getApplication().registerActivityLifecycleCallbacks(activityCallbacks = new LinphoneService.ActivityMonitor());
    }



    @SuppressWarnings("unchecked")
    @Override
    public void onCreate() {
        super.onCreate();

        setupActivityMonitor();
        // In case restart after a crash. Main in LinphoneActivity
        mNotificationTitle = getString(R.string.service_name);

        // Needed in order for the two next calls to succeed, libraries must have been loaded first
        LinphonePreferences.instance().setContext(getBaseContext());
        LinphoneCoreFactory.instance().setLogCollectionPath(getFilesDir().getAbsolutePath());
        boolean isDebugEnabled = LinphonePreferences.instance().isDebugEnabled();
        LinphoneCoreFactory.instance().enableLogCollection(isDebugEnabled);
        LinphoneCoreFactory.instance().setDebugMode(isDebugEnabled, getString(R.string.app_name));

        // Dump some debugging information to the logs
        Log.i(START_LINPHONE_LOGS);
        dumpDeviceInformation();
        dumpInstalledLinphoneInformation();

        //Disable service notification for Android O
        if ((Version.sdkAboveOrEqual(Version.API26_O_80))) {
            LinphonePreferences.instance().setServiceNotificationVisibility(false);
            mDisableRegistrationStatus = true;
        }

        mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNM.cancel(INCALL_NOTIF_ID); // in case of crash the icon is not removed



        if (!LinphoneManager.isInstanciated())
        LinphoneManager.createAndStart(LinphoneService.this);

        instance = this; // instance is ready once linphone manager has been created
        incomingReceivedActivityName = LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived();

        android.util.Log.d("linphoneCalling", "Service Strated");
        LinphoneManager.getLc().addListener(mListener = new LinphoneCoreListenerBase() {
            @Override
            public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) {
                if (instance == null) {
                    Log.i("Service not ready, discarding call state change to ", state.toString());
                    return;
                }

                if (state == LinphoneCall.State.IncomingReceived) {
                    android.util.Log.d("ServiceStarted", "IncomingCall  Status");
                    onIncomingReceived2(call);
                }

                if (state == State.CallEnd || state == State.CallReleased || state == State.Error) {
                    if (LinphoneManager.isInstanciated() && LinphoneManager.getLc() != null && LinphoneManager.getLc().getCallsNb() == 0) {

                    }
                }

                if (state == State.CallEnd && call.getCallLog().getStatus() == CallStatus.Missed) {
                    int missedCallCount = LinphoneManager.getLcIfManagerNotDestroyedOrNull().getMissedCallsCount();
                    String body;
                    if (missedCallCount > 1) {
                        body = getString(R.string.missed_calls_notif_body).replace("%i", String.valueOf(missedCallCount));
                    } else {
                        LinphoneAddress address = call.getRemoteAddress();
                        body = address.getDisplayName();
                        if (body == null) {
                            body = address.asStringUriOnly();
                        }

                    }

                }

                if (state == State.StreamsRunning) {
                    // Workaround bug current call seems to be updated after state changed to streams running
                    if (getResources().getBoolean(R.bool.enable_call_notification))
                        refreshIncallIcon(call);
                } else {
                    if (getResources().getBoolean(R.bool.enable_call_notification))
                        refreshIncallIcon(LinphoneManager.getLc().getCurrentCall());
                }
            }

            @Override
            public void globalState(LinphoneCore lc, LinphoneCore.GlobalState state, String message) {

            }

            @Override
            public void registrationState(LinphoneCore lc, LinphoneProxyConfig cfg, LinphoneCore.RegistrationState state, String smessage) {
            }
        });


        try {
            mStartForeground = getClass().getMethod("startForeground", mStartFgSign);
            mStopForeground = getClass().getMethod("stopForeground", mStopFgSign);
        } catch (NoSuchMethodException e) {
            Log.e(e, "Couldn't find startForeground or stopForeground");
        }



    /*  if (displayServiceNotification()) {
            startForegroundCompat(NOTIF_ID, mNotif);
        }*/

        if (!mTestDelayElapsed) {
            // Only used when testing. Simulates a 5 seconds delay for launching service
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mTestDelayElapsed = true;
                }
            }, 5000);
        }

        //make sure the application will at least wakes up every 10 mn

        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    }




    private enum IncallIconState {INCALL, PAUSE, VIDEO, IDLE}

    private LinphoneService.IncallIconState mCurrentIncallIconState = LinphoneService.IncallIconState.IDLE;


    public void refreshIncallIcon(LinphoneCall currentCall) {
        LinphoneCore lc = LinphoneManager.getLc();

    }


    public void removeSasNotification() {
        mNM.cancel(SAS_NOTIF_ID);
    }

    private static final Class<?>[] mSetFgSign = new Class[]{boolean.class};
    private static final Class<?>[] mStartFgSign = new Class[]{
            int.class, Notification.class};
    private static final Class<?>[] mStopFgSign = new Class[]{boolean.class};

    private Method mSetForeground;
    private Method mStartForeground;
    private Method mStopForeground;
    private Object[] mSetForegroundArgs = new Object[1];
    private Object[] mStartForegroundArgs = new Object[2];
    private Object[] mStopForegroundArgs = new Object[1];
    private String incomingReceivedActivityName;

    void invokeMethod(Method method, Object[] args) {
        try {
            method.invoke(this, args);
        } catch (InvocationTargetException e) {
            // Should not happen.
            Log.w(e, "Unable to invoke method");
        } catch (IllegalAccessException e) {
            // Should not happen.
            Log.w(e, "Unable to invoke method");
        }
    }

    /**
     * This is a wrapper around the new startForeground method, using the older
     * APIs if it is not available.
     */
    void startForegroundCompat(int id, Notification notification) {
        // If we have the new startForeground API, then use it.
        if (mStartForeground != null) {
            mStartForegroundArgs[0] = Integer.valueOf(id);
            mStartForegroundArgs[1] = notification;
            invokeMethod(mStartForeground, mStartForegroundArgs);
            return;
        }

        // Fall back on the old API.
        if (mSetForeground != null) {
            mSetForegroundArgs[0] = Boolean.TRUE;
            invokeMethod(mSetForeground, mSetForegroundArgs);
            // continue
        }

        notifyWrapper(id, notification);
    }

    /**
     * This is a wrapper around the new stopForeground method, using the older
     * APIs if it is not available.
     */
    void stopForegroundCompat(int id) {
        // If we have the new stopForeground API, then use it.
        if (mStopForeground != null) {
            mStopForegroundArgs[0] = Boolean.TRUE;
            invokeMethod(mStopForeground, mStopForegroundArgs);
            return;
        }

        // Fall back on the old API.  Note to cancel BEFORE changing the
        // foreground state, since we could be killed at that point.
        mNM.cancel(id);
        if (mSetForeground != null) {
            mSetForegroundArgs[0] = Boolean.FALSE;
            invokeMethod(mSetForeground, mSetForegroundArgs);
        }
    }

    private void dumpDeviceInformation() {
        StringBuilder sb = new StringBuilder();
        sb.append("DEVICE=").append(Build.DEVICE).append("\n");
        sb.append("MODEL=").append(Build.MODEL).append("\n");
        sb.append("MANUFACTURER=").append(Build.MANUFACTURER).append("\n");
        sb.append("SDK=").append(Build.VERSION.SDK_INT).append("\n");
        sb.append("Supported ABIs=");
        for (String abi : Version.getCpuAbis()) {
            sb.append(abi + ", ");
        }
        sb.append("\n");
        Log.i(sb.toString());
    }

    private void dumpInstalledLinphoneInformation() {
        PackageInfo info = null;
        try {
            info = getPackageManager().getPackageInfo(getPackageName(), 0);
        } catch (NameNotFoundException nnfe) {
        }

        if (info != null) {
            Log.i("Linphone version is ", info.versionName + " (" + info.versionCode + ")");
        } else {
            Log.i("Linphone version is unknown");
        }
    }


    /**
     * Wrap notifier to avoid setting the linphone icons while the service
     * is stopping. When the (rare) bug is triggered, the linphone icon is
     * present despite the service is not running. To trigger it one could
     * stop linphone as soon as it is started. Transport configured with TLS.
     */
    private synchronized void notifyWrapper(int id, Notification notification) {
        if (instance != null && notification != null) {
            mNM.notify(id, notification);
        } else {
            Log.i("Service not ready, discarding notification");
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    @Override
    public void onTaskRemoved(Intent rootIntent) {
        android.util.Log.d("TaskRemoved","True");
        if (getResources().getBoolean(R.bool.kill_service_with_task_manager)) {
            Log.d("Task removed, stop service");

            // If push is enabled, don't unregister account, otherwise do unregister
            if (LinphonePreferences.instance().isPushNotificationEnabled()) {
                LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
                if (lc != null) lc.setNetworkReachable(false);
            }
           stopSelf();
        }
        super.onTaskRemoved(rootIntent);
    }

    @Override
    public synchronized void onDestroy() {

        if (activityCallbacks != null) {
            getApplication().unregisterActivityLifecycleCallbacks(activityCallbacks);
            activityCallbacks = null;
        }

        LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
        if (lc != null) {
            lc.removeListener(mListener);
        }

        instance = null;
        LinphoneManager.destroy();

        // Make sure our notification is gone.
        stopForegroundCompat(NOTIF_ID);
        mNM.cancel(INCALL_NOTIF_ID);
        mNM.cancel(MESSAGE_NOTIF_ID);

        super.onDestroy();
    }


    private void onIncomingReceived2(LinphoneCall call) {
        String name = call.getRemoteAddress().asString().substring(call.getRemoteAddress().asString().indexOf("\"")+1,call.getRemoteAddress().asString().lastIndexOf("\""));
        String myname = MyApplication.getInstance().getPrefManager().getUserName()+" "+MyApplication.getInstance().getPrefManager().getUserLastName();
        android.util.Log.d("ServiceStarted", " Values  "+ name+"   "+myname + call.getUserData());
        if (!name.equals(myname)) {
            Intent intent = new Intent(getApplicationContext(), TwilioIncomingCallActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        }

    }

当有任何传入呼叫时,服务中的侦听器由名称初始化,LinphoneManager.getLc().addListener它会侦听传入呼叫,其中 If 语句被提及为

if (state == LinphoneCall.State.IncomingReceived) {
                    android.util.Log.d("ServiceStarted", "IncomingCall  Status");
                    onIncomingReceived2(call);
                }

现在在代码的底部有一个方法onIncomingReceived2(LinphoneCall call)。因此,当我从默认方法(即call.getRemoteAddress() )检查时,我得到的用户名与调用 Initialized 的用户名相同。

因此,当我拨打电话时,它会在同一设备上收到。如果有人能在这方面提供帮助,我将不胜感激。提前致谢。

4

0 回答 0