how a call be dailed out(Android Telephony basing on M)

来源:互联网 发布:淘宝同行恶意投诉售假 编辑:程序博客网 时间:2024/06/05 19:23


A call from UI to the real module that handle it on Android is not that simple,

Dialer--->Telecom--->Telephony--->RILD--->modem

and above path, there are 3 process simply from Android framework!

even these three are not all directly function call.

Dialer focus on Human–Machine Interaction close part. Telecom concentrates on call management. And Telephony is the core of all telephony related functions on Android framework layer.

Call Define

the calls are all transferred  from UI to the call manager through broadcast. all call types are defined

frameworks/base/core/java/android/content/Intent.java

public static final String ACTION_DIAL = "android.intent.action.DIAL";public static final String ACTION_CALL = "android.intent.action.CALL";public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";...public static final String ACTION_ANSWER = "android.intent.action.ANSWER";...

so when you type some numbers from Dialer and click the call button, an broadcast will be send out.

Telecom

Telecom just waiting for this!

packages/services/Telecomm/AndroidManifest.xml

        <activity android:name="CallActivity"                android:theme="@style/Theme.Telecomm.Transparent"                android:configChanges="orientation|screenSize|keyboardHidden"                android:permission="android.permission.CALL_PHONE"                android:excludeFromRecents="true"                android:process=":ui">            <!-- CALL action intent filters for the various ways of initiating an outgoing call. -->            <intent-filter>                <action android:name="android.intent.action.CALL" />                <category android:name="android.intent.category.DEFAULT" />                <data android:scheme="tel" />            </intent-filter>            <!-- Specify an icon for SIP calls so that quick contacts widget shows a special SIP                 icon for calls to SIP addresses. -->            <intent-filter android:icon="@drawable/ic_launcher_sip_call">                <action android:name="android.intent.action.CALL" />                <category android:name="android.intent.category.DEFAULT" />                <data android:scheme="sip" />            </intent-filter>            <intent-filter>                <action android:name="android.intent.action.CALL" />                <category android:name="android.intent.category.DEFAULT" />                <data android:scheme="voicemail" />            </intent-filter>            <!-- Omit default category below so that all Intents sent to this filter must be                 explicit. -->            <intent-filter>                <action android:name="android.intent.action.CALL" />                <data android:mimeType="vnd.android.cursor.item/phone" />                <data android:mimeType="vnd.android.cursor.item/phone_v2" />                <data android:mimeType="vnd.android.cursor.item/person" />            </intent-filter>        </activity>        <activity-alias android:name="PrivilegedCallActivity"                android:targetActivity="CallActivity"                android:permission="android.permission.CALL_PRIVILEGED"                android:process=":ui">...        <activity-alias android:name="EmergencyCallActivity"                android:targetActivity="CallActivity"                android:permission="android.permission.CALL_PRIVILEGED"                android:process=":ui">...
we see that different call types is handled by associated Activities.

let's focus on the first one here.

packages/services/Telecomm/src/com/android/server/telecom/CallActivity.java

public class CallActivity extends Activity {...    @Override    protected void onCreate(Bundle bundle) {        super.onCreate(bundle);...        Log.d(this, "onCreate: begin");        processIntent(getIntent());        // This activity does not have associated UI, so close.        finish();        Log.d(this, "onCreate: end");     }

that's simple, pure activity and finished just after process intent.

and the processIntent() just works as an capability and permission verifier, then pass the Intent to the next one:

    private boolean sendBroadcastToReceiver(Intent intent) {        intent.putExtra(CallReceiver.KEY_IS_INCOMING_CALL, false);        intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);        intent.setClass(this, CallReceiver.class);        Log.d(this, "Sending broadcast as user to CallReceiver");        sendBroadcastAsUser(intent, UserHandle.OWNER);        return true;    }

packages/services/Telecomm/src/com/android/server/telecom/CallReceiver.java

public class CallReceiver extends BroadcastReceiver {public void onReceive(Context context, Intent intent) {        if (isUnknownCall) {            processUnknownCallIntent(intent);        } else {            processOutgoingCallIntent(context, intent);        }...}static void processOutgoingCallIntent(Context context, Intent intent)        PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(                TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);        Call call = getCallsManager().startOutgoingCall(handle, phoneAccountHandle, clientExtras);            NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(                    context, getCallsManager(), call, intent, isDefaultDialer);            final int result = broadcaster.processIntent();
it is really a BroadcastReceiver for handling both incoming(unknow) and outgoing call.

for out going call, it initialize an Call object by CallsManager and then process Intent again by another new class NewOutgoingCallIntentBroadcaster.

packages/services/Telecomm/src/com/android/server/telecom/NewOutgoingCallIntentBroadcaster.java

int processIntent()if (Intent.ACTION_CALL.equals(action)) {launchSystemDialer(intent.getData());                callImmediately = true;}if (callImmediately) {mCallsManager.placeOutgoingCall(mCall, Uri.fromParts(scheme, number, null), null,speakerphoneOn, videoState);}broadcastIntent(intent, number, !callImmediately);
it verifies if the call needs be handled immediately and call CallsManager function. the broadcastIntent() is for special handle logic, so we can ignore here firstly.

packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)if (neededForceSpeakerOn()) {    call.setStartWithSpeakerphoneOn(true);} else {    call.setStartWithSpeakerphoneOn(speakerphoneOn || mDockManager.isDocked());}boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, call.getHandle());call.startCreateConnection(mPhoneAccountRegistrar);
here it checks speaker status and verify if it should be an emergency call, then call startCreateConnection() of the Call.

packages/services/Telecomm/src/com/android/server/telecom/Call.java

void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,    phoneAccountRegistrar, mContext);mCreateConnectionProcessor.process();
here it again create an new class object to process the connection create logic.

packages/services/Telecomm/src/com/android/server/telecom/CreateConnectionProcessor.java

void process()clearTimeout();mAttemptRecords.add(new CallAttemptRecord(   mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));mAttemptRecordIterator = mAttemptRecords.iterator();attemptNextPhoneAccount();private void attemptNextPhoneAccount()attempt = mAttemptRecordIterator.next();PhoneAccountHandle phoneAccount = attempt.connectionManagerPhoneAccount;ConnectionServiceWrapper service =        mRepository.getService(                phoneAccount.getComponentName(),                phoneAccount.getUserHandle());mCall.setConnectionService(service);setTimeoutIfNeeded(service, attempt);service.createConnection(mCall, new Response(service));
it firstly creates and CallAttemptRecord and collects into its list, then process it by a new class' function ConnectionServiceWrapper.createConnection() as previous logic do.

before go ahead, let's explain how this ConnectionServiceWrapper is created first.

ConnectionServiceWrapper

it is got directly from mRepository. and this object comes from the param of CreateConnectionProcessor class constructor

private final ConnectionServiceRepository mRepository;... CreateConnectionProcessor(            Call call, ConnectionServiceRepository repository, CreateConnectionResponse response,            PhoneAccountRegistrar phoneAccountRegistrar, Context context) {        mCall = call;        mRepository = repository;        mResponse = response;        mPhoneAccountRegistrar = phoneAccountRegistrar;        mContext = context;    }
and we've got that the CreateConnectionProcessor is constructed by Call object as previous code shows. this mRepository is also the member of Call class, which is also be initialized by its constructor:

    Call(            Context context,            ConnectionServiceRepository repository,            Uri handle,            GatewayInfo gatewayInfo,            PhoneAccountHandle connectionManagerPhoneAccountHandle,            PhoneAccountHandle targetPhoneAccountHandle,            boolean isIncoming,            boolean isConference) {        mState = isConference ? CallState.ACTIVE : CallState.NEW;        mContext = context;        mRepository = repository;        setHandle(handle);        setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);        mGatewayInfo = gatewayInfo;        setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);        setTargetPhoneAccount(targetPhoneAccountHandle);        mIsIncoming = isIncoming;        mIsConference = isConference;        maybeLoadCannedSmsResponses();    }
from our earlier analysis, the Call object comes from CallsManager.startOutgoingCall() in CallReceiver class. and the function just looks:

packages/services/Telecomm/src/com/android/server/telecom/CallsManager.java

Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras)    Call call = getNewOutgoingCall(handle);    call.setTargetPhoneAccount(phoneAccountHandle);if (needsAccountSelection) {    call.setState(CallState.PRE_DIAL_WAIT);} else {call.setState(CallState.CONNECTING);}if (!mCalls.contains(call))    addCall(call);private Call getNewOutgoingCall(Uri handle)    return new Call(            mContext,            mConnectionServiceRepository,                                                                     handle,            null /* gatewayInfo */,            null /* connectionManagerPhoneAccount */,                                                         null /* phoneAccountHandle */,                                                                    false /* isIncoming */,            false /* isConference */);
so the mRepository comes from CallsManager! then how it is initialized, still from others?

private final ConnectionServiceRepository mConnectionServiceRepository;CallsManager(Context context, MissedCallNotifier missedCallNotifier,        PhoneAccountRegistrar phoneAccountRegistrar) {    mContext = context;    mPhoneAccountRegistrar = phoneAccountRegistrar;    mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar,            context);
luckly it is created here directly!

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceRepository.java

final class ConnectionServiceRepository        implements ServiceBinder.Listener<ConnectionServiceWrapper> {    private final HashMap<Pair<ComponentName, UserHandle>, ConnectionServiceWrapper> mServiceCache =            new HashMap<>();    private final PhoneAccountRegistrar mPhoneAccountRegistrar;    private final Context mContext;    ConnectionServiceRepository(PhoneAccountRegistrar phoneAccountRegistrar, Context context) {        mPhoneAccountRegistrar = phoneAccountRegistrar;        mContext = context;    }       ConnectionServiceWrapper getService(ComponentName componentName, UserHandle userHandle) {        Pair<ComponentName, UserHandle> cacheKey = Pair.create(componentName, userHandle);        ConnectionServiceWrapper service = mServiceCache.get(cacheKey);        if (service == null) {            service = new ConnectionServiceWrapper(                    componentName,                    this,                    mPhoneAccountRegistrar,                    mContext,                    userHandle);            service.addListener(this);            mServiceCache.put(cacheKey, service);        }           return service;    }
so it is! and the ConnectionServiceWrapper is firstly created and be cached here.

let's back and continue our previous topic.

Cont.

int CreateConnectionProcessor it calls ConnectionServiceWrapper's createconnection()

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

final class ConnectionServiceWrapper extends ServiceBinder {void createConnection(final Call call, final CreateConnectionResponse response)    BindCallback callback = new BindCallback() {        public void onSuccess() {            String callId = mCallIdMapper.getCallId(call);            mPendingResponses.put(callId, response);            mServiceInterface.createConnection(                    call.getConnectionManagerPhoneAccount(),                    callId,                    new ConnectionRequest(                            call.getTargetPhoneAccount(),                            call.getHandle(),                            extras,                            call.getVideoState()),                    call.isIncoming(),                    call.isUnknown());        }        public void onFailure() {            response.handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.ERROR));        }    };    mBinder.bind(callback, call);private Binder2 mBinder = new Binder2();private IConnectionService mServiceInterface;protected void setServiceInterface(IBinder binder) {    if (binder == null) {        handleConnectionServiceDeath();        CallsManager.getInstance().handleConnectionServiceDeath(this);        mServiceInterface = null;    } else {        mServiceInterface = IConnectionService.Stub.asInterface(binder);        addConnectionServiceAdapter(mAdapter);    }
it also create another new class object to do the further handle logic.

firstly new an BinderCallback object and put the logic into its onSucess callback function. then call mBinder's bind() to associate the callback object with the call.

let's see how the Binder2 looks like and how the callback works.

as no code details found in ConnectionServiceWrapper and its parent class is ServiceBinder, we'd go there and have a look

packages/services/Telecomm/src/com/android/server/telecom/ServiceBinder.java

abstract class ServiceBinder { interface BindCallback {        void onSuccess();        void onFailure();    }...private final Set<BindCallback> mCallbacks = new ArraySet<>();...    private void setBinder(IBinder binder)        mBinder = binder;        setServiceInterface(binder);...protected abstract void setServiceInterface(IBinder binder);...    protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,            TelecomSystem.SyncRoot lock, UserHandle userHandle) {        Preconditions.checkState(!TextUtils.isEmpty(serviceAction));        Preconditions.checkNotNull(componentName);        mContext = context;        mLock = lock;        mServiceAction = serviceAction;        mComponentName = componentName;        mUserHandle = userHandle;    }    final class Binder2 {        void bind(BindCallback callback, Call call)            mCallbacks.add(callback);            if (mServiceConnection == null) {                Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);                ServiceConnection connection = new ServiceBinderConnection(call);                mContext.bindService(serviceIntent, connection, bindingFlags);            } else {                handleSuccessfulConnection();            }    }
it is an abstract class and the bind() function in the internal BInder2 class performs an remote service call after add the callback into its list.

the callback must be triggered while the remote service be binded. have a look.

private final class ServiceBinderConnection implements ServiceConnection {public void onServiceConnected(ComponentName componentName, IBinder binder)if (mIsBindingAborted) {handleFailedConnection();return;}setBinder(binder);handleSuccessfulConnection();public void onServiceDisconnected(ComponentName componentName)mServiceConnection = null;handleServiceDisconnected();}...    private void handleSuccessfulConnection() {                                                               for (BindCallback callback : mCallbacks) {                                                                callback.onSuccess();                                                                             }                                                                                                     mCallbacks.clear();                                                                               }
so we get it.

and back to ConnectionServiceWrapper,  mServiceInterface is calling the real and remote createConnection() now.

wait, where this mServiceInterface is initialized?

find the setBinder() in above onServiceConnected()? remember that abstract function "setServiceInterface()"

packages/services/Telecomm/src/com/android/server/telecom/ConnectionServiceWrapper.java

protected void setServiceInterface(IBinder binder) {if (binder == null) {handleConnectionServiceDeath();CallsManager.getInstance().handleConnectionServiceDeath(this);mServiceInterface = null;} else {mServiceInterface = IConnectionService.Stub.asInterface(binder);addConnectionServiceAdapter(mAdapter);}
the call is transferred out of Telecom, but we do not find where the remote service now.

let's find out.

The real ConnectionService

as previous remote service bind from Binder2, it is named by mServiceAction and it is initialized by ServiceBinder constructor.

    protected ServiceBinder(String serviceAction, ComponentName componentName, Context context,            TelecomSystem.SyncRoot lock, UserHandle userHandle) {        Preconditions.checkState(!TextUtils.isEmpty(serviceAction));        Preconditions.checkNotNull(componentName);        mContext = context;        mLock = lock;        mServiceAction = serviceAction;        mComponentName = componentName;        mUserHandle = userHandle;    }    final class Binder2 {        void bind(BindCallback callback, Call call)            mCallbacks.add(callback);            if (mServiceConnection == null) {                Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);                ServiceConnection connection = new ServiceBinderConnection(call);                mContext.bindService(serviceIntent, connection, bindingFlags);            } else {                handleSuccessfulConnection();            }    }
and as ServiceBinder is inherented by ConnectionServiceWrapper, we just check its functions.

ConnectionServiceWrapper(            ComponentName componentName,            ConnectionServiceRepository connectionServiceRepository,            PhoneAccountRegistrar phoneAccountRegistrar,            Context context,            UserHandle userHandle) {        super(ConnectionService.SERVICE_INTERFACE, componentName, context, userHandle);        mConnectionServiceRepository = connectionServiceRepository;        phoneAccountRegistrar.addListener(new PhoneAccountRegistrar.Listener() {            // TODO -- Upon changes to PhoneAccountRegistrar, need to re-wire connections            // To do this, we must proxy remote ConnectionService objects        });          mPhoneAccountRegistrar = phoneAccountRegistrar;    }
the value should be ConnectionService.SERVICE_INTERFACE as it shows.

frameworks/base/telecomm/java/android/telecom/ConnectionService.java

public abstract class ConnectionService extends Service {    public static final String SERVICE_INTERFACE = "android.telecom.ConnectionService";...
by comment of the source code, we learn that AndroidManifest needs to be searched to find the real service:

An abstract service that should be implemented by any apps which can make phone calls (VoIP or otherwise) and want those calls to be integrated into the built-in phone app. Once implemented, the ConnectionService needs two additional steps before it will be integrated into the phone app:

1. Registration in AndroidManifest.xml
 <service android:name="com.example.package.MyConnectionService"
    android:label="@string/some_label_for_my_connection_service"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
  <intent-filter>
   <action android:name="android.telecom.ConnectionService" />
  </intent-filter>
 </service>

and here we go to Telephony directly.

packages/services/Telephony/AndroidManifest.xml

        <service                android:singleUser="true"                android:name="com.android.services.telephony.TelephonyConnectionService"                android:label="@string/pstn_connection_service_label"                android:permission="android.permission.BIND_CONNECTION_SERVICE" >            <intent-filter>                <action android:name="android.telecom.ConnectionService" />            </intent-filter>        </service>
packages/services/Telephony/src/com/android/services/telephony/TelephonyConnectionService.java

public class TelephonyConnectionService extends ConnectionService {...    @Override    public void onCreate() {        super.onCreate();        mExpectedComponentName = new ComponentName(this, this.getClass());        mEmergencyTonePlayer = new EmergencyTonePlayer(this);        TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);    } 
but we do not find the createConnection() method, so we have to back to ConnectionService:

    /**     * This can be used by telecom to either create a new outgoing call or attach to an existing     * incoming call. In either case, telecom will cycle through a set of services and call     * createConnection util a connection service cancels the process or completes it successfully.     */    private void createConnection(            final PhoneAccountHandle callManagerAccount,            final String callId,            final ConnectionRequest request,            boolean isIncoming,            boolean isUnknown) {        Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)                : isIncoming ? onCreateIncomingConnection(callManagerAccount, request)                : onCreateOutgoingConnection(callManagerAccount, request);...        if (connection.getState() != Connection.STATE_DISCONNECTED) {            addConnection(callId, connection);        }...        mAdapter.handleCreateConnectionComplete(                callId,                request,                new ParcelableConnection(                        connection.getState(),                        connection.getConnectionCapabilities(),                        connection.getAddress(),                        connection.getAddressPresentation(),                        connection.getCallerDisplayName(),                        connection.getCallerDisplayNamePresentation(),                        connection.getVideoProvider() == null ?                                null : connection.getVideoProvider().getInterface(),                        connection.getVideoState(),                        connection.isRingbackRequested(),                        connection.getAudioModeIsVoip(),                        connection.getStatusHints(),                        connection.getDisconnectCause(),                        createIdList(connection.getConferenceables())));...
create connection object, add to record list, then complete by mAdapter.

and the create connection is implemented in the TelephonyConnectionService

    @Override    public Connection onCreateOutgoingConnection(            PhoneAccountHandle connectionManagerPhoneAccount,            final ConnectionRequest request) {...        // Get the right phone object from the account data passed in.        if (phone == null) {            phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);        }...        final TelephonyConnection connection =                createConnectionFor(phone, null, true /* isOutgoing */);...        connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);        connection.setInitializing();        connection.setVideoState(request.getVideoState());...        if (useEmergencyCallHelper) {...        } else {            placeOutgoingConnection(connection, phone, request);        }        return connection;    }    private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency) {                                                                                                                        if (Objects.equals(mExpectedComponentName, accountHandle.getComponentName())) {                                                                                                                                  if (accountHandle.getId() != null) {                try {                    int phoneId = SubscriptionController.getInstance().getPhoneId(                            Integer.parseInt(accountHandle.getId()));                    return PhoneFactory.getPhone(phoneId);                } catch (NumberFormatException e) {                    Log.w(this, "Could not get subId from account: " + accountHandle.getId());                                                                                                                               }            }        }        if (isEmergency) {...                                                                                                                                                                  }        return null;    }    private TelephonyConnection createConnectionFor(            Phone phone,            com.android.internal.telephony.Connection originalConnection,            boolean isOutgoing) {        TelephonyConnection returnConnection = null;        int phoneType = phone.getPhoneType();        if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {            returnConnection = new GsmConnection(originalConnection);        } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {            boolean allowMute = allowMute(phone);            returnConnection = new CdmaConnection(                    originalConnection, mEmergencyTonePlayer, allowMute, isOutgoing);        }        if (returnConnection != null) {            // Listen to Telephony specific callbacks from the connection            returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);        }        return returnConnection;    }
and the method placeOutgoingConnection() would transfer the dial task on the target Phone object now(just one notice, the phone object got from PhoneFactory is a PhoneProxy instance):

    private void placeOutgoingConnection(            TelephonyConnection connection, Phone phone, ConnectionRequest request) {        String number = connection.getAddress().getSchemeSpecificPart();        com.android.internal.telephony.Connection originalConnection;        try {            originalConnection = phone.dial(number, request.getVideoState());        } catch (CallStateException e) {            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(                    android.telephony.DisconnectCause.OUTGOING_FAILURE,                    e.getMessage()));            return;        }        if (originalConnection == null) {            int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;            // On GSM phones, null connection means that we dialed an MMI code            if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {...                startActivity(intent);            }            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(                    telephonyDisconnectCause, "Connection is null"));        } else {            connection.setOriginalConnection(originalConnection);        }
frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneProxy.java

    @Override    public Connection dial(String dialString, int videoState) throws CallStateException {        return mActivePhone.dial(dialString, videoState);    }        @Override    public Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {        return mActivePhone.dial(dialString, uusInfo, videoState);    }

The Real Call of Phone

take Gsm phone as example.

frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java

    @Override    public Connection    dial(String dialString, int videoState) throws CallStateException {        return dial(dialString, null, videoState);    }    @Override    public Connection    dial (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {...        return dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY);    }    @Override    protected Connection    dialInternal (String dialString, UUSInfo uusInfo, int videoState)            throws CallStateException {...        if (mmi == null) {            return mCT.dial(newDialString, uusInfo);        } else if (mmi.isTemporaryModeCLIR()) {            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo); ...    GsmCallTracker mCT;    public    GSMPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {        super("GSM", notifier, context, ci, unitTestMode);     ...                                                          mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);        mCT = new GsmCallTracker(this);
frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java

    GsmCallTracker (GSMPhone phone) {        this.mPhone = phone;        mCi = phone.mCi;        mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);        mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);        mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);...    synchronized Connection    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {...        String origNumber = dialString;        dialString = convertNumberIfNecessary(mPhone, dialString);...        mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString),                this, mForegroundCall);...            // Always unmute when initiating a new call            setMute(false);...                if (!mWaitingForHoldRequest.isWaiting()) {                    mCi.dial(PhoneNumberUtils.parseGlobalPrefix(mPendingMO.getAddress()), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));                } else {                    mWaitingForHoldRequest.set(mPendingMO.getAddress(), clirMode, uusInfo, mPhone);                }...        updatePhoneState();        mPhone.notifyPreciseCallStateChanged();        return mPendingMO;    }
it convert dial string if needed, then unmute to make sure there there audio works as expected, send command to the rild and notify the whole system about the call state change.

waiting for the response

            case EVENT_DIAL_CALL_RESULT:                ar = (AsyncResult) msg.obj;                if (ar.exception != null) {                    log("dial call failed!!");                    mHelper.PendingHangupRequestUpdate();                }                operationComplete();...    private void    operationComplete() {        mPendingOperations--;        if (mPendingOperations == 0 && mNeedsPoll) {            mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);            mCi.getCurrentCalls(mLastRelevantPoll);...        switch (msg.what) {            case EVENT_POLL_CALLS_RESULT:                ar = (AsyncResult)msg.obj;                if (msg == mLastRelevantPoll) {                    if (DBG_POLL) log(                            "handle EVENT_POLL_CALLS_RESULT: set needsPoll=F");                    mNeedsPoll = false;                    mLastRelevantPoll = null;                    handlePollCalls((AsyncResult)msg.obj);                }            break;

reference:

1.Android M6.0 source code

2.http://developer.android.com/



0 0
原创粉丝点击