1. 程式人生 > >Android 6.0 Phone MO(去電)流程分析(應用層)

Android 6.0 Phone MO(去電)流程分析(應用層)

寫在前面的話

本文主要分析MO(去電)的流程,研究的程式碼是Android 6.0的,目前只關注應用層,以GSM為例。

MO(如果圖片看不清的話,可以右鍵選擇在新標籤中開啟圖片,或者把圖片另存到自己電腦再檢視。)

http://blog.csdn.net/linyongan

步驟1:當用戶點選撥號鍵盤按鈕(DialtactsActivity的floating_action_button),彈出撥號盤,輸入完電話號碼,再點選撥號按鈕,此時打電話的流程開始,因此打電話流程的入口就在DialpadFragment.java(packages\apps\dialer\src\com\android\dialer\dialpad)的onClick()方法

public void onClick(View view) {
   switch (view.getId()) {
      case R.id.dialpad_floating_action_button:
      mHaptic.vibrate();
      handleDialButtonPressed();
      ...
}

步驟2:在handleDialButtonPressed()方法裡,會先判斷使用者是否已輸入號碼,假如號碼為空,則呼叫handleDialButtonClickWithEmptyDigits()方法顯示上一次撥打過的號碼。然後第一次獲取到要撥打的number,在這裡可以對number做一些判斷或者自定義處理。

步驟11,12:在CallIntentProcessor.java的processOutgoingCallIntent()方法裡,呼叫CallsManager.java的startOutgoingCall()方法建立一個Call例項
Call call = callsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
(這就是Call物件的來源),最後傳遞給NewOutgoingCallIntentBroadcaster。

步驟13:在NewOutgoingCallIntentBroadcaster.java的processIntent()方法裡,第二次獲取到要撥打的number,這裡也是對number進行一些定製操作的好地方。在這裡會呼叫isPotentialEmergencyNumber()方法判斷number是否是潛在的緊急號碼,如果是緊急號碼會直接走步驟15。

   /**
    * Processes the supplied intent and starts the outgoing call broadcast process relevant to the
    * intent.
    *
    * This method will handle three kinds of actions:
    *
    * - CALL (intent launched by all third party dialers)
    * - CALL_PRIVILEGED (intent launched by system apps e.g. system Dialer, voice Dialer)
    * - CALL_EMERGENCY (intent launched by lock screen emergency dialer)
    *
    * @return {@link DisconnectCause#NOT_DISCONNECTED} if the call succeeded, and an appropriate
    *         {@link DisconnectCause} if the call did not, describing why it failed.
    */
    int processIntent() {
        ...
        //第二次獲取到number
        String number = PhoneNumberUtils.getNumberFromIntent(intent, mContext);
        //判斷是不是EmergencyNumber
        final boolean isPotentialEmergencyNumber = isPotentialEmergencyNumber(number);
        Log.v(this, "isPotentialEmergencyNumber = %s", isPotentialEmergencyNumber);

        // True for certain types of numbers that are not intended to be intercepted or modified
        // by third parties (e.g. emergency numbers).
        boolean callImmediately = false;

        if (Intent.ACTION_CALL.equals(action)) {
            if (isPotentialEmergencyNumber) {
                if (!mIsDefaultOrSystemPhoneApp) {//攔截第三方軟體撥打的緊急號碼
                    Log.w(this, "Cannot call potential emergency number %s with CALL Intent %s "
                            + "unless caller is system or default dialer.", number, intent);
                    launchSystemDialer(intent.getData());//彈出系統撥號盤
                    return DisconnectCause.OUTGOING_CANCELED;
                } else {
                    callImmediately = true;//緊急電話的標誌
                }
            }
        } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
            if (!isPotentialEmergencyNumber) {//攔截在緊急撥號盤撥打的非緊急電話
                Log.w(this, "Cannot call non-potential-emergency number %s with EMERGENCY_CALL "
                        + "Intent %s.", number, intent);
                return DisconnectCause.OUTGOING_CANCELED;
            }
            callImmediately = true; //緊急電話的標誌
        } else {
            Log.w(this, "Unhandled Intent %s. Ignoring and not placing call.", intent);
            return DisconnectCause.INVALID_NUMBER;
        }

        if (callImmediately) {//處理緊急號碼
            Log.i(this, "Placing call immediately instead of waiting for "
                    + " OutgoingCallBroadcastReceiver: %s", intent);
            String scheme = isUriNumber ? PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL;
            boolean speakerphoneOn = mIntent.getBooleanExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
            int videoState = mIntent.getIntExtra(
                    TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
                    VideoProfile.STATE_AUDIO_ONLY);
            mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,
                    speakerphoneOn, videoState);//快速處理緊急電話,但是並不return。

            // Don't return but instead continue and send the ACTION_NEW_OUTGOING_CALL broadcast
            // so that third parties can still inspect (but not intercept) the outgoing call. When
            // the broadcast finally reaches the OutgoingCallBroadcastReceiver, we'll know not to
            // initiate the call again because of the presence of the EXTRA_ALREADY_CALLED extra.
        }

        Log.i(this, "Sending NewOutgoingCallBroadcast for %s", mCall);
        //普通電話走這裡
        if (isSkipSchemaParsing) {
            broadcastIntent(intent, handle.toString(), !callImmediately);
        } else {
            broadcastIntent(intent, number, !callImmediately);
        }
        return DisconnectCause.NOT_DISCONNECTED;
    }

步驟15:在這裡呼叫了CallsManage.java的placeOutgoingCall()方法,在這裡會呼叫TelephonyUtil.java的shouldProcessAsEmergency()方法判斷是不是緊急撥號,在這裡也可以對緊急號碼做定製。

 /**
  * Attempts to issue/connect the specified call.
  *
  * @param handle Handle to connect the call with.
  * @param gatewayInfo Optional gateway information that can be used to route the call to the
  *        actual dialed handle via a gateway provider. May be null.
  * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
  * @param videoState The desired video state for the outgoing call.
  */
  void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
            int videoState) {
        //判斷是不是緊急電話
        boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
                call.getHandle());
        ……
        if (isEmergencyCall) {
            // Emergency -- CreateConnectionProcessor will choose accounts automatically
            call.setTargetPhoneAccount(null);
        }

        if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
            if (!isEmergencyCall) {
                updateLchStatus(call.getTargetPhoneAccount().getId());
            }
            // If the account has been set, proceed to place the outgoing call.
            // Otherwise the connection will be initiated when the account is set by the user.
            call.startCreateConnection(mPhoneAccountRegistrar);
        }    }
}

步驟16: call例項被傳送到這裡,終於派上用場了,進入Call.java的startCreateConnection()方法

 /**
  * Starts the create connection sequence. Upon completion, there should exist an active
  * connection through a connection service (or the call will have failed).
  *
  * @param phoneAccountRegistrar The phone account registrar.
  */
 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
   Preconditions.checkState(mCreateConnectionProcessor == null);
   mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
   phoneAccountRegistrar, mContext);
   mCreateConnectionProcessor.process();
}

步驟17和18:繼續把Call傳遞給CreateConnectionProcessor.java,並且new一個CreateConnectionProcessor例項,呼叫它的process()方法,通過attemptNextPhoneAccount()方法,呼叫到service.createConnection,這個service的型別是ConnectionServiceWrapper,它是IConnectionService的子類

private void attemptNextPhoneAccount() {
   ...
   if (mResponse != null && attempt != null) {
     Log.i(this, "Trying attempt %s", attempt);
     ConnectionServiceWrapper service = mRepository.getService(
                            attempt.connectionManagerPhoneAccount.getComponentName());
    if (service == null) {
        Log.i(this, "Found no connection service for attempt %s", attempt);
        attemptNextPhoneAccount();
    } else {
    mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
    mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
    mCall.setConnectionService(service);
    Log.i(this, "Attempting to call from %s", service.getComponentName());
    service.createConnection(mCall, new Response(service));
    }
  } 
}

這樣的話,Call物件就被傳遞到ConnectionServiceWrapper裡了。

(讀者最好先學習一下AIDL相關知識再繼續閱讀)
步驟20,21,22:這裡呼叫了ConnectionServiceWrapper的父類ServiceBinder的bind()方法,先new一個ServiceConnection物件,然後繫結一個遠端服務端服務。如果繫結成功的話,在ServiceBinder的內部類ServiceBinderConnection的onServiceConnected()方法就被呼叫。
在這裡做了兩件事:
1、步驟23和24:通過setBinder()方法,回撥ConnectionServiceWrapper的setServiceInterface()方法,通過mServiceInterface = IConnectionService.Stub.asInterface(binder);
這行程式碼獲取一個遠端服務端的物件mServiceInterface 。
2、步驟25和26:再通過呼叫handleSuccessfulConnection()方法回撥callback 的onSuccess()方法,也就又回到ConnectionServiceWrapper的createConnection()方法裡。
步驟27:最後通過這一行mServiceInterface.createConnection();
,呼叫ConnectionService.java裡mBinder的createConnection()方法。

private final IBinder mBinder = new IConnectionService.Stub() {
    ...
    @Override
    public void createConnection(PhoneAccountHandle connectionManagerPhoneAccount,
                                 String id,ConnectionRequest request,
                                 boolean isIncoming,boolean isUnknown) {
        SomeArgs args = SomeArgs.obtain();
        args.arg1 = connectionManagerPhoneAccount;
        args.arg2 = id;
        args.arg3 = request;
        args.argi1 = isIncoming ? 1 : 0;
        args.argi2 = isUnknown ? 1 : 0;
        mHandler.obtainMessage(MSG_CREATE_CONNECTION, args).sendToTarget();
    }
    ...
}

步驟28:在這裡把傳進來的引數封裝到Message裡再發送出去,然後在ConnectionService.java裡mHandler的handleMessage()方法裡處理這個Message

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
       switch (msg.what) {
           case MSG_CREATE_CONNECTION: {
               SomeArgs args = (SomeArgs) msg.obj;
               try {
               final PhoneAccountHandle connectionManagerPhoneAccount =
                                (PhoneAccountHandle) args.arg1;
               final String id = (String) args.arg2;
               final ConnectionRequest request = (ConnectionRequest) args.arg3;
               final boolean isIncoming = args.argi1 == 1;
               final boolean isUnknown = args.argi2 == 1;
               if (!mAreAccountsInitialized) {
                   Log.d(this, "Enqueueing pre-init request %s", id);
                   mPreInitializationConnectionRequests.add(new Runnable() {
                   @Override
                   public void run() {
                   createConnection(connectionManagerPhoneAccount,
                                    id,
                                    request,
                                    isIncoming,
                                    isUnknown);
                                }
                   });
                } else {
                   createConnection(connectionManagerPhoneAccount,
                                    id,
                                    request,
                                    isIncoming,
                                    isUnknown);
                   }
                } finally {
                  args.recycle();
                }
            break;
        }
   ...
}

步驟29,30,31:在這裡就把Message裡的資料取出來,然後傳遞到ConnectionService的createConnection()方法裡。接著onCreateOutgoingConnection()會被呼叫到,這個方法被TelephonyConnectionService重寫,TelephonyConnectionService是ConnectionService的例項,所以進入TelephonyConnectionService.java的onCreateOutgoingConnection()方法,在這裡第三次取出number,會再次判斷是不是緊急號碼,如果是的話,會turn on radio關閉飛航模式再撥打緊急電話。phone 物件和connection 物件也是在這時候被建立。

public Connection onCreateOutgoingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            final ConnectionRequest request) {
   ...
   // 判斷是不是緊急號碼
   boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(this, number);
   // Get the right phone object from the account data passed in.
   //建立phone 物件
   final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
   if (phone == null) {
     Log.d(this, "onCreateOutgoingConnection, phone is null");
     return Connection.createFailedConnection(
                DisconnectCauseUtil.toTelecomDisconnectCause(
                android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
   }
   ...
   //建立connection物件
   final TelephonyConnection connection =
                createConnectionFor(phone, null, true /* isOutgoing */, null);
   if (connection == null) {
      return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                    android.telephony.DisconnectCause.OUTGOING_FAILURE,
                    "Invalid phone type"));
   }
   connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
   connection.setInitializing();
   connection.setVideoState(request.getVideoState());

   if (useEmergencyCallHelper) {
       if (mEmergencyCallHelper == null) {
                mEmergencyCallHelper = new EmergencyCallHelper(this);
            }
           //開啟Radio,關閉飛航模式
            mEmergencyCallHelper.startTurnOnRadioSequence(phone,
                    new EmergencyCallHelper.Callback() {
                        @Override
                        public void onComplete(boolean isRadioReady) {
                            if (connection.getState() == Connection.STATE_DISCONNECTED) {
                                // If the connection has already been disconnected, do nothing.
                            } else if (isRadioReady) {
                            //Radio已被開啟,可以撥打緊急電話
                                connection.setInitialized();
                                placeOutgoingConnection(connection, phone, request);
                            } else {
                                Log.d(this, "onCreateOutgoingConnection, failed to turn on radio");
                                connection.setDisconnected(
                                        DisconnectCauseUtil.toTelecomDisconnectCause(
                                                android.telephony.DisconnectCause.POWER_OFF,
                                                "Failed to turn on radio."));
                                connection.destroy();
                            }
                        }
                    });

        } else {
            placeOutgoingConnection(connection, phone, request);
        }   return connection;
}

步驟32:在步驟30createConnection()方法的最後,呼叫了ConnectionServiceAdapter.java的handleCreateConnectionComplete()方法繼續執行了一段流程,在建立Connection完成之後,會把Call的狀態從CONNECTING更新為 DIALING。此段流程就不詳說了。

步驟34~37:緊接著步驟33,最後通過phone.dial進行撥號,之後的流程就進入到Framework層了。
本文就寫到這裡。