Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.telecom;
     18 
     19 import android.content.Context;
     20 import android.net.Uri;
     21 import android.os.Bundle;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.SystemProperties;
     25 import android.os.Trace;
     26 import android.provider.CallLog.Calls;
     27 import android.telecom.CallAudioState;
     28 import android.telecom.Conference;
     29 import android.telecom.Connection;
     30 import android.telecom.DisconnectCause;
     31 import android.telecom.GatewayInfo;
     32 import android.telecom.ParcelableConference;
     33 import android.telecom.ParcelableConnection;
     34 import android.telecom.PhoneAccount;
     35 import android.telecom.PhoneAccountHandle;
     36 import android.telecom.TelecomManager;
     37 import android.telecom.VideoProfile;
     38 import android.telephony.PhoneNumberUtils;
     39 import android.telephony.TelephonyManager;
     40 import android.text.TextUtils;
     41 
     42 import com.android.internal.annotations.VisibleForTesting;
     43 import com.android.internal.telephony.PhoneConstants;
     44 import com.android.internal.telephony.TelephonyProperties;
     45 import com.android.internal.util.IndentingPrintWriter;
     46 
     47 import java.util.Collection;
     48 import java.util.Collections;
     49 import java.util.HashSet;
     50 import java.util.List;
     51 import java.util.Objects;
     52 import java.util.Set;
     53 import java.util.concurrent.ConcurrentHashMap;
     54 
     55 /**
     56  * Singleton.
     57  *
     58  * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
     59  * access from other packages specifically refraining from passing the CallsManager instance
     60  * beyond the com.android.server.telecom package boundary.
     61  */
     62 @VisibleForTesting
     63 public class CallsManager extends Call.ListenerBase implements VideoProviderProxy.Listener {
     64 
     65     // TODO: Consider renaming this CallsManagerPlugin.
     66     interface CallsManagerListener {
     67         void onCallAdded(Call call);
     68         void onCallRemoved(Call call);
     69         void onCallStateChanged(Call call, int oldState, int newState);
     70         void onConnectionServiceChanged(
     71                 Call call,
     72                 ConnectionServiceWrapper oldService,
     73                 ConnectionServiceWrapper newService);
     74         void onIncomingCallAnswered(Call call);
     75         void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
     76         void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall);
     77         void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState);
     78         void onRingbackRequested(Call call, boolean ringback);
     79         void onIsConferencedChanged(Call call);
     80         void onIsVoipAudioModeChanged(Call call);
     81         void onVideoStateChanged(Call call);
     82         void onCanAddCallChanged(boolean canAddCall);
     83         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
     84     }
     85 
     86     private static final String TAG = "CallsManager";
     87 
     88     private static final int MAXIMUM_LIVE_CALLS = 1;
     89     private static final int MAXIMUM_HOLD_CALLS = 1;
     90     private static final int MAXIMUM_RINGING_CALLS = 1;
     91     private static final int MAXIMUM_OUTGOING_CALLS = 1;
     92     private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
     93 
     94     private static final int[] OUTGOING_CALL_STATES =
     95             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING};
     96 
     97     private static final int[] LIVE_CALL_STATES =
     98             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.ACTIVE};
     99 
    100     /**
    101      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
    102      * calls are added to the map and removed when the calls move to the disconnected state.
    103      *
    104      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    105      * load factor before resizing, 1 means we only expect a single thread to
    106      * access the map so make only a single shard
    107      */
    108     private final Set<Call> mCalls = Collections.newSetFromMap(
    109             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
    110 
    111     private final ConnectionServiceRepository mConnectionServiceRepository;
    112     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
    113     private final InCallController mInCallController;
    114     private final CallAudioManager mCallAudioManager;
    115     private RespondViaSmsManager mRespondViaSmsManager;
    116     private final Ringer mRinger;
    117     private final InCallWakeLockController mInCallWakeLockController;
    118     // For this set initial table size to 16 because we add 13 listeners in
    119     // the CallsManager constructor.
    120     private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
    121             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
    122     private final HeadsetMediaButton mHeadsetMediaButton;
    123     private final WiredHeadsetManager mWiredHeadsetManager;
    124     private final DockManager mDockManager;
    125     private final TtyManager mTtyManager;
    126     private final ProximitySensorManager mProximitySensorManager;
    127     private final PhoneStateBroadcaster mPhoneStateBroadcaster;
    128     private final CallLogManager mCallLogManager;
    129     private final Context mContext;
    130     private final TelecomSystem.SyncRoot mLock;
    131     private final ContactsAsyncHelper mContactsAsyncHelper;
    132     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
    133     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    134     private final MissedCallNotifier mMissedCallNotifier;
    135     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
    136     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
    137     /* Handler tied to thread in which CallManager was initialized. */
    138     private final Handler mHandler = new Handler(Looper.getMainLooper());
    139 
    140     private boolean mCanAddCall = true;
    141 
    142     /**
    143      * The call the user is currently interacting with. This is the call that should have audio
    144      * focus and be visible in the in-call UI.
    145      */
    146     private Call mForegroundCall;
    147 
    148     private Runnable mStopTone;
    149 
    150     /**
    151      * Initializes the required Telecom components.
    152      */
    153     CallsManager(
    154             Context context,
    155             TelecomSystem.SyncRoot lock,
    156             ContactsAsyncHelper contactsAsyncHelper,
    157             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
    158             MissedCallNotifier missedCallNotifier,
    159             PhoneAccountRegistrar phoneAccountRegistrar,
    160             HeadsetMediaButtonFactory headsetMediaButtonFactory,
    161             ProximitySensorManagerFactory proximitySensorManagerFactory,
    162             InCallWakeLockControllerFactory inCallWakeLockControllerFactory) {
    163         mContext = context;
    164         mLock = lock;
    165         mContactsAsyncHelper = contactsAsyncHelper;
    166         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
    167         mPhoneAccountRegistrar = phoneAccountRegistrar;
    168         mMissedCallNotifier = missedCallNotifier;
    169         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
    170         mWiredHeadsetManager = new WiredHeadsetManager(context);
    171         mDockManager = new DockManager(context);
    172         mCallAudioManager = new CallAudioManager(
    173                 context, mLock, statusBarNotifier, mWiredHeadsetManager, mDockManager, this);
    174         InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager, lock);
    175         mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
    176         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
    177         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
    178         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
    179         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
    180         mCallLogManager = new CallLogManager(context);
    181         mInCallController = new InCallController(context, mLock, this);
    182         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
    183         mConnectionServiceRepository =
    184                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
    185         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
    186 
    187         mListeners.add(statusBarNotifier);
    188         mListeners.add(mCallLogManager);
    189         mListeners.add(mPhoneStateBroadcaster);
    190         mListeners.add(mInCallController);
    191         mListeners.add(mRinger);
    192         mListeners.add(new RingbackPlayer(this, playerFactory));
    193         mListeners.add(new InCallToneMonitor(playerFactory, this));
    194         mListeners.add(mCallAudioManager);
    195         mListeners.add(missedCallNotifier);
    196         mListeners.add(mDtmfLocalTonePlayer);
    197         mListeners.add(mHeadsetMediaButton);
    198         mListeners.add(mProximitySensorManager);
    199 
    200         mMissedCallNotifier.updateOnStartup(
    201                 mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory);
    202     }
    203 
    204     public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
    205         if (mRespondViaSmsManager != null) {
    206             mListeners.remove(mRespondViaSmsManager);
    207         }
    208         mRespondViaSmsManager = respondViaSmsManager;
    209         mListeners.add(respondViaSmsManager);
    210     }
    211 
    212     public RespondViaSmsManager getRespondViaSmsManager() {
    213         return mRespondViaSmsManager;
    214     }
    215 
    216     @Override
    217     public void onSuccessfulOutgoingCall(Call call, int callState) {
    218         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
    219 
    220         setCallState(call, callState, "successful outgoing call");
    221         if (!mCalls.contains(call)) {
    222             // Call was not added previously in startOutgoingCall due to it being a potential MMI
    223             // code, so add it now.
    224             addCall(call);
    225         }
    226 
    227         // The call's ConnectionService has been updated.
    228         for (CallsManagerListener listener : mListeners) {
    229             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
    230         }
    231 
    232         markCallAsDialing(call);
    233     }
    234 
    235     @Override
    236     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
    237         Log.v(this, "onFailedOutgoingCall, call: %s", call);
    238 
    239         markCallAsRemoved(call);
    240     }
    241 
    242     @Override
    243     public void onSuccessfulIncomingCall(Call incomingCall) {
    244         Log.d(this, "onSuccessfulIncomingCall");
    245         setCallState(incomingCall, CallState.RINGING, "successful incoming call");
    246 
    247         if (hasMaximumRingingCalls()) {
    248             incomingCall.reject(false, null);
    249             // since the call was not added to the list of calls, we have to call the missed
    250             // call notifier and the call logger manually.
    251             mMissedCallNotifier.showMissedCallNotification(incomingCall);
    252             mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
    253         } else {
    254             addCall(incomingCall);
    255         }
    256     }
    257 
    258     @Override
    259     public void onFailedIncomingCall(Call call) {
    260         setCallState(call, CallState.DISCONNECTED, "failed incoming call");
    261         call.removeListener(this);
    262     }
    263 
    264     @Override
    265     public void onSuccessfulUnknownCall(Call call, int callState) {
    266         setCallState(call, callState, "successful unknown call");
    267         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
    268         addCall(call);
    269     }
    270 
    271     @Override
    272     public void onFailedUnknownCall(Call call) {
    273         Log.i(this, "onFailedUnknownCall for call %s", call);
    274         setCallState(call, CallState.DISCONNECTED, "failed unknown call");
    275         call.removeListener(this);
    276     }
    277 
    278     @Override
    279     public void onRingbackRequested(Call call, boolean ringback) {
    280         for (CallsManagerListener listener : mListeners) {
    281             listener.onRingbackRequested(call, ringback);
    282         }
    283     }
    284 
    285     @Override
    286     public void onPostDialWait(Call call, String remaining) {
    287         mInCallController.onPostDialWait(call, remaining);
    288     }
    289 
    290     @Override
    291     public void onPostDialChar(final Call call, char nextChar) {
    292         if (PhoneNumberUtils.is12Key(nextChar)) {
    293             // Play tone if it is one of the dialpad digits, canceling out the previously queued
    294             // up stopTone runnable since playing a new tone automatically stops the previous tone.
    295             if (mStopTone != null) {
    296                 mHandler.removeCallbacks(mStopTone);
    297             }
    298 
    299             mDtmfLocalTonePlayer.playTone(call, nextChar);
    300 
    301             // TODO: Create a LockedRunnable class that does the synchronization automatically.
    302             mStopTone = new Runnable() {
    303                 @Override
    304                 public void run() {
    305                     synchronized (mLock) {
    306                         // Set a timeout to stop the tone in case there isn't another tone to follow.
    307                         mDtmfLocalTonePlayer.stopTone(call);
    308                     }
    309                 }
    310             };
    311             mHandler.postDelayed(
    312                     mStopTone,
    313                     Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
    314         } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
    315                 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
    316             // Stop the tone if a tone is playing, removing any other stopTone callbacks since
    317             // the previous tone is being stopped anyway.
    318             if (mStopTone != null) {
    319                 mHandler.removeCallbacks(mStopTone);
    320             }
    321             mDtmfLocalTonePlayer.stopTone(call);
    322         } else {
    323             Log.w(this, "onPostDialChar: invalid value %d", nextChar);
    324         }
    325     }
    326 
    327     @Override
    328     public void onParentChanged(Call call) {
    329         // parent-child relationship affects which call should be foreground, so do an update.
    330         updateCallsManagerState();
    331         for (CallsManagerListener listener : mListeners) {
    332             listener.onIsConferencedChanged(call);
    333         }
    334     }
    335 
    336     @Override
    337     public void onChildrenChanged(Call call) {
    338         // parent-child relationship affects which call should be foreground, so do an update.
    339         updateCallsManagerState();
    340         for (CallsManagerListener listener : mListeners) {
    341             listener.onIsConferencedChanged(call);
    342         }
    343     }
    344 
    345     @Override
    346     public void onIsVoipAudioModeChanged(Call call) {
    347         for (CallsManagerListener listener : mListeners) {
    348             listener.onIsVoipAudioModeChanged(call);
    349         }
    350     }
    351 
    352     @Override
    353     public void onVideoStateChanged(Call call) {
    354         for (CallsManagerListener listener : mListeners) {
    355             listener.onVideoStateChanged(call);
    356         }
    357     }
    358 
    359     @Override
    360     public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
    361         mPendingCallsToDisconnect.add(call);
    362         mHandler.postDelayed(new Runnable() {
    363             @Override
    364             public void run() {
    365                 synchronized (mLock) {
    366                     if (mPendingCallsToDisconnect.remove(call)) {
    367                         Log.i(this, "Delayed disconnection of call: %s", call);
    368                         call.disconnect();
    369                     }
    370                 }
    371             }
    372         }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
    373 
    374         return true;
    375     }
    376 
    377     /**
    378      * Handles changes to the {@link Connection.VideoProvider} for a call.  Adds the
    379      * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
    380      * in {@link Call#setVideoProvider(IVideoProvider)}.  This allows the {@link CallsManager} to
    381      * respond to callbacks from the {@link VideoProviderProxy}.
    382      *
    383      * @param call The call.
    384      */
    385     @Override
    386     public void onVideoCallProviderChanged(Call call) {
    387         VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
    388 
    389         if (videoProviderProxy == null) {
    390             return;
    391         }
    392 
    393         videoProviderProxy.addListener(this);
    394     }
    395 
    396     /**
    397      * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
    398      * a call.  Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
    399      * modification request.
    400      *
    401      * @param call The call.
    402      * @param videoProfile The {@link VideoProfile}.
    403      */
    404     @Override
    405     public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
    406         int videoState = videoProfile != null ? videoProfile.getVideoState() :
    407                 VideoProfile.STATE_AUDIO_ONLY;
    408         Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
    409                 .videoStateToString(videoState));
    410 
    411         for (CallsManagerListener listener : mListeners) {
    412             listener.onSessionModifyRequestReceived(call, videoProfile);
    413         }
    414     }
    415 
    416     Collection<Call> getCalls() {
    417         return Collections.unmodifiableCollection(mCalls);
    418     }
    419 
    420     Call getForegroundCall() {
    421         return mForegroundCall;
    422     }
    423 
    424     Ringer getRinger() {
    425         return mRinger;
    426     }
    427 
    428     InCallController getInCallController() {
    429         return mInCallController;
    430     }
    431 
    432     boolean hasEmergencyCall() {
    433         for (Call call : mCalls) {
    434             if (call.isEmergencyCall()) {
    435                 return true;
    436             }
    437         }
    438         return false;
    439     }
    440 
    441     boolean hasVideoCall() {
    442         for (Call call : mCalls) {
    443             if (VideoProfile.isVideo(call.getVideoState())) {
    444                 return true;
    445             }
    446         }
    447         return false;
    448     }
    449 
    450     CallAudioState getAudioState() {
    451         return mCallAudioManager.getCallAudioState();
    452     }
    453 
    454     boolean isTtySupported() {
    455         return mTtyManager.isTtySupported();
    456     }
    457 
    458     int getCurrentTtyMode() {
    459         return mTtyManager.getCurrentTtyMode();
    460     }
    461 
    462     void addListener(CallsManagerListener listener) {
    463         mListeners.add(listener);
    464     }
    465 
    466     void removeListener(CallsManagerListener listener) {
    467         mListeners.remove(listener);
    468     }
    469 
    470     /**
    471      * Starts the process to attach the call to a connection service.
    472      *
    473      * @param phoneAccountHandle The phone account which contains the component name of the
    474      *        connection service to use for this call.
    475      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    476      */
    477     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    478         Log.d(this, "processIncomingCallIntent");
    479         Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
    480         if (handle == null) {
    481             // Required for backwards compatibility
    482             handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
    483         }
    484         Call call = new Call(
    485                 mContext,
    486                 this,
    487                 mLock,
    488                 mConnectionServiceRepository,
    489                 mContactsAsyncHelper,
    490                 mCallerInfoAsyncQueryFactory,
    491                 handle,
    492                 null /* gatewayInfo */,
    493                 null /* connectionManagerPhoneAccount */,
    494                 phoneAccountHandle,
    495                 true /* isIncoming */,
    496                 false /* isConference */);
    497 
    498         call.setIntentExtras(extras);
    499         // TODO: Move this to be a part of addCall()
    500         call.addListener(this);
    501         call.startCreateConnection(mPhoneAccountRegistrar);
    502     }
    503 
    504     void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    505         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
    506         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
    507         Call call = new Call(
    508                 mContext,
    509                 this,
    510                 mLock,
    511                 mConnectionServiceRepository,
    512                 mContactsAsyncHelper,
    513                 mCallerInfoAsyncQueryFactory,
    514                 handle,
    515                 null /* gatewayInfo */,
    516                 null /* connectionManagerPhoneAccount */,
    517                 phoneAccountHandle,
    518                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
    519                 // to the existing connection instead of trying to create a new one.
    520                 true /* isIncoming */,
    521                 false /* isConference */);
    522         call.setIsUnknown(true);
    523         call.setIntentExtras(extras);
    524         call.addListener(this);
    525         call.startCreateConnection(mPhoneAccountRegistrar);
    526     }
    527 
    528     private boolean areHandlesEqual(Uri handle1, Uri handle2) {
    529         if (handle1 == null || handle2 == null) {
    530             return handle1 == handle2;
    531         }
    532 
    533         if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
    534             return false;
    535         }
    536 
    537         final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
    538         final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
    539         return TextUtils.equals(number1, number2);
    540     }
    541 
    542     private Call getNewOutgoingCall(Uri handle) {
    543         // First check to see if we can reuse any of the calls that are waiting to disconnect.
    544         // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
    545         Call reusedCall = null;
    546         for (Call pendingCall : mPendingCallsToDisconnect) {
    547             if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
    548                 mPendingCallsToDisconnect.remove(pendingCall);
    549                 Log.i(this, "Reusing disconnected call %s", pendingCall);
    550                 reusedCall = pendingCall;
    551             } else {
    552                 Log.i(this, "Not reusing disconnected call %s", pendingCall);
    553                 pendingCall.disconnect();
    554             }
    555         }
    556         if (reusedCall != null) {
    557             return reusedCall;
    558         }
    559 
    560         // Create a call with original handle. The handle may be changed when the call is attached
    561         // to a connection service, but in most cases will remain the same.
    562         return new Call(
    563                 mContext,
    564                 this,
    565                 mLock,
    566                 mConnectionServiceRepository,
    567                 mContactsAsyncHelper,
    568                 mCallerInfoAsyncQueryFactory,
    569                 handle,
    570                 null /* gatewayInfo */,
    571                 null /* connectionManagerPhoneAccount */,
    572                 null /* phoneAccountHandle */,
    573                 false /* isIncoming */,
    574                 false /* isConference */);
    575     }
    576 
    577     /**
    578      * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
    579      *
    580      * @param handle Handle to connect the call with.
    581      * @param phoneAccountHandle The phone account which contains the component name of the
    582      *        connection service to use for this call.
    583      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    584      */
    585     Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    586         Call call = getNewOutgoingCall(handle);
    587 
    588         List<PhoneAccountHandle> accounts =
    589                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false);
    590 
    591         Log.v(this, "startOutgoingCall found accounts = " + accounts);
    592 
    593         if (mForegroundCall != null) {
    594             Call ongoingCall = mForegroundCall;
    595             // If there is an ongoing call, use the same phone account to place this new call.
    596             // If the ongoing call is a conference call, we fetch the phone account from the
    597             // child calls because we don't have targetPhoneAccount set on Conference calls.
    598             // TODO: Set targetPhoneAccount for all conference calls (b/23035408).
    599             if (ongoingCall.getTargetPhoneAccount() == null &&
    600                     !ongoingCall.getChildCalls().isEmpty()) {
    601                 ongoingCall = ongoingCall.getChildCalls().get(0);
    602             }
    603             if (ongoingCall.getTargetPhoneAccount() != null) {
    604                 phoneAccountHandle = ongoingCall.getTargetPhoneAccount();
    605             }
    606         }
    607 
    608         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
    609         // as if a phoneAccount was not specified (does the default behavior instead).
    610         // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
    611         if (phoneAccountHandle != null) {
    612             if (!accounts.contains(phoneAccountHandle)) {
    613                 phoneAccountHandle = null;
    614             }
    615         }
    616 
    617         if (phoneAccountHandle == null) {
    618             // No preset account, check if default exists that supports the URI scheme for the
    619             // handle.
    620             phoneAccountHandle =
    621                     mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme());
    622         }
    623 
    624         call.setTargetPhoneAccount(phoneAccountHandle);
    625 
    626         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
    627                 call.getHandle());
    628         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
    629 
    630         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
    631         // a call, or cancel this call altogether.
    632         if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
    633             // just cancel at this point.
    634             Log.i(this, "No remaining room for outgoing call: %s", call);
    635             if (mCalls.contains(call)) {
    636                 // This call can already exist if it is a reused call,
    637                 // See {@link #getNewOutgoingCall}.
    638                 call.disconnect();
    639             }
    640             return null;
    641         }
    642 
    643         boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
    644                 !isEmergencyCall;
    645 
    646         if (needsAccountSelection) {
    647             // This is the state where the user is expected to select an account
    648             call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
    649             // Create our own instance to modify (since extras may be Bundle.EMPTY)
    650             extras = new Bundle(extras);
    651             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
    652         } else {
    653             call.setState(
    654                     CallState.CONNECTING,
    655                     phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
    656         }
    657 
    658         call.setIntentExtras(extras);
    659 
    660         // Do not add the call if it is a potential MMI code.
    661         if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
    662             call.addListener(this);
    663         } else if (!mCalls.contains(call)) {
    664             // We check if mCalls already contains the call because we could potentially be reusing
    665             // a call which was previously added (See {@link #getNewOutgoingCall}).
    666             addCall(call);
    667         }
    668 
    669         return call;
    670     }
    671 
    672     /**
    673      * Attempts to issue/connect the specified call.
    674      *
    675      * @param handle Handle to connect the call with.
    676      * @param gatewayInfo Optional gateway information that can be used to route the call to the
    677      *        actual dialed handle via a gateway provider. May be null.
    678      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
    679      * @param videoState The desired video state for the outgoing call.
    680      */
    681     void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
    682             int videoState) {
    683         if (call == null) {
    684             // don't do anything if the call no longer exists
    685             Log.i(this, "Canceling unknown call.");
    686             return;
    687         }
    688 
    689         final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
    690 
    691         if (gatewayInfo == null) {
    692             Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
    693         } else {
    694             Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
    695                     Log.pii(uriHandle), Log.pii(handle));
    696         }
    697 
    698         call.setHandle(uriHandle);
    699         call.setGatewayInfo(gatewayInfo);
    700         call.setVideoState(videoState);
    701 
    702         if (speakerphoneOn) {
    703             Log.i(this, "%s Starting with speakerphone as requested", call);
    704         } else {
    705             Log.i(this, "%s Starting with speakerphone because car is docked.", call);
    706         }
    707         call.setStartWithSpeakerphoneOn(speakerphoneOn || mDockManager.isDocked());
    708 
    709         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
    710                 call.getHandle());
    711         if (isEmergencyCall) {
    712             // Emergency -- CreateConnectionProcessor will choose accounts automatically
    713             call.setTargetPhoneAccount(null);
    714         }
    715 
    716         if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
    717             // If the account has been set, proceed to place the outgoing call.
    718             // Otherwise the connection will be initiated when the account is set by the user.
    719             call.startCreateConnection(mPhoneAccountRegistrar);
    720         }
    721     }
    722 
    723     /**
    724      * Attempts to start a conference call for the specified call.
    725      *
    726      * @param call The call to conference.
    727      * @param otherCall The other call to conference with.
    728      */
    729     void conference(Call call, Call otherCall) {
    730         call.conferenceWith(otherCall);
    731     }
    732 
    733     /**
    734      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
    735      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
    736      * the user opting to answer said call.
    737      *
    738      * @param call The call to answer.
    739      * @param videoState The video state in which to answer the call.
    740      */
    741     void answerCall(Call call, int videoState) {
    742         if (!mCalls.contains(call)) {
    743             Log.i(this, "Request to answer a non-existent call %s", call);
    744         } else {
    745             // If the foreground call is not the ringing call and it is currently isActive() or
    746             // STATE_DIALING, put it on hold before answering the call.
    747             if (mForegroundCall != null && mForegroundCall != call &&
    748                     (mForegroundCall.isActive() ||
    749                      mForegroundCall.getState() == CallState.DIALING)) {
    750                 if (0 == (mForegroundCall.getConnectionCapabilities()
    751                         & Connection.CAPABILITY_HOLD)) {
    752                     // This call does not support hold.  If it is from a different connection
    753                     // service, then disconnect it, otherwise allow the connection service to
    754                     // figure out the right states.
    755                     if (mForegroundCall.getConnectionService() != call.getConnectionService()) {
    756                         mForegroundCall.disconnect();
    757                     }
    758                 } else {
    759                     Call heldCall = getHeldCall();
    760                     if (heldCall != null) {
    761                         Log.v(this, "Disconnecting held call %s before holding active call.",
    762                                 heldCall);
    763                         heldCall.disconnect();
    764                     }
    765 
    766                     Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
    767                             mForegroundCall, call);
    768                     mForegroundCall.hold();
    769                 }
    770                 // TODO: Wait until we get confirmation of the active call being
    771                 // on-hold before answering the new call.
    772                 // TODO: Import logic from CallManager.acceptCall()
    773             }
    774 
    775             for (CallsManagerListener listener : mListeners) {
    776                 listener.onIncomingCallAnswered(call);
    777             }
    778 
    779             // We do not update the UI until we get confirmation of the answer() through
    780             // {@link #markCallAsActive}.
    781             call.answer(videoState);
    782             if (VideoProfile.isVideo(videoState) &&
    783                 !mWiredHeadsetManager.isPluggedIn() &&
    784                 !mCallAudioManager.isBluetoothDeviceAvailable() &&
    785                 isSpeakerEnabledForVideoCalls()) {
    786                 call.setStartWithSpeakerphoneOn(true);
    787             }
    788         }
    789     }
    790 
    791     private static boolean isSpeakerEnabledForVideoCalls() {
    792         return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
    793                 PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
    794                 PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
    795     }
    796 
    797     /**
    798      * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
    799      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
    800      * the user opting to reject said call.
    801      */
    802     void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
    803         if (!mCalls.contains(call)) {
    804             Log.i(this, "Request to reject a non-existent call %s", call);
    805         } else {
    806             for (CallsManagerListener listener : mListeners) {
    807                 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
    808             }
    809             call.reject(rejectWithMessage, textMessage);
    810         }
    811     }
    812 
    813     /**
    814      * Instructs Telecom to play the specified DTMF tone within the specified call.
    815      *
    816      * @param digit The DTMF digit to play.
    817      */
    818     void playDtmfTone(Call call, char digit) {
    819         if (!mCalls.contains(call)) {
    820             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
    821         } else {
    822             call.playDtmfTone(digit);
    823             mDtmfLocalTonePlayer.playTone(call, digit);
    824         }
    825     }
    826 
    827     /**
    828      * Instructs Telecom to stop the currently playing DTMF tone, if any.
    829      */
    830     void stopDtmfTone(Call call) {
    831         if (!mCalls.contains(call)) {
    832             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
    833         } else {
    834             call.stopDtmfTone();
    835             mDtmfLocalTonePlayer.stopTone(call);
    836         }
    837     }
    838 
    839     /**
    840      * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
    841      */
    842     void postDialContinue(Call call, boolean proceed) {
    843         if (!mCalls.contains(call)) {
    844             Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
    845         } else {
    846             call.postDialContinue(proceed);
    847         }
    848     }
    849 
    850     /**
    851      * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
    852      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
    853      * the user hitting the end-call button.
    854      */
    855     void disconnectCall(Call call) {
    856         Log.v(this, "disconnectCall %s", call);
    857 
    858         if (!mCalls.contains(call)) {
    859             Log.w(this, "Unknown call (%s) asked to disconnect", call);
    860         } else {
    861             mLocallyDisconnectingCalls.add(call);
    862             call.disconnect();
    863         }
    864     }
    865 
    866     /**
    867      * Instructs Telecom to disconnect all calls.
    868      */
    869     void disconnectAllCalls() {
    870         Log.v(this, "disconnectAllCalls");
    871 
    872         for (Call call : mCalls) {
    873             disconnectCall(call);
    874         }
    875     }
    876 
    877 
    878     /**
    879      * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
    880      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
    881      * the user hitting the hold button during an active call.
    882      */
    883     void holdCall(Call call) {
    884         if (!mCalls.contains(call)) {
    885             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
    886         } else {
    887             Log.d(this, "Putting call on hold: (%s)", call);
    888             call.hold();
    889         }
    890     }
    891 
    892     /**
    893      * Instructs Telecom to release the specified call from hold. Intended to be invoked by
    894      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
    895      * by the user hitting the hold button during a held call.
    896      */
    897     void unholdCall(Call call) {
    898         if (!mCalls.contains(call)) {
    899             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
    900         } else {
    901             Log.d(this, "unholding call: (%s)", call);
    902             for (Call c : mCalls) {
    903                 // Only attempt to hold parent calls and not the individual children.
    904                 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
    905                     c.hold();
    906                 }
    907             }
    908             call.unhold();
    909         }
    910     }
    911 
    912     /** Called by the in-call UI to change the mute state. */
    913     void mute(boolean shouldMute) {
    914         mCallAudioManager.mute(shouldMute);
    915     }
    916 
    917     /**
    918       * Called by the in-call UI to change the audio route, for example to change from earpiece to
    919       * speaker phone.
    920       */
    921     void setAudioRoute(int route) {
    922         mCallAudioManager.setAudioRoute(route);
    923     }
    924 
    925     /** Called by the in-call UI to turn the proximity sensor on. */
    926     void turnOnProximitySensor() {
    927         mProximitySensorManager.turnOn();
    928     }
    929 
    930     /**
    931      * Called by the in-call UI to turn the proximity sensor off.
    932      * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
    933      *        the screen will be kept off until the proximity sensor goes negative.
    934      */
    935     void turnOffProximitySensor(boolean screenOnImmediately) {
    936         mProximitySensorManager.turnOff(screenOnImmediately);
    937     }
    938 
    939     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
    940         if (!mCalls.contains(call)) {
    941             Log.i(this, "Attempted to add account to unknown call %s", call);
    942         } else {
    943             // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
    944             // the SELECT_PHONE_ACCOUNT sequence run in parallel, if the user selects an account before the
    945             // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
    946             // respecting a rewritten number or a canceled number. This is unlikely since
    947             // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
    948             // a phone account from the in-call UI.
    949             call.setTargetPhoneAccount(account);
    950 
    951             // Note: emergency calls never go through account selection dialog so they never
    952             // arrive here.
    953             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
    954                 call.startCreateConnection(mPhoneAccountRegistrar);
    955             } else {
    956                 call.disconnect();
    957             }
    958 
    959             if (setDefault) {
    960                 mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account);
    961             }
    962         }
    963     }
    964 
    965     /** Called when the audio state changes. */
    966     void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState) {
    967         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
    968         for (CallsManagerListener listener : mListeners) {
    969             listener.onCallAudioStateChanged(oldAudioState, newAudioState);
    970         }
    971     }
    972 
    973     void markCallAsRinging(Call call) {
    974         setCallState(call, CallState.RINGING, "ringing set explicitly");
    975     }
    976 
    977     void markCallAsDialing(Call call) {
    978         setCallState(call, CallState.DIALING, "dialing set explicitly");
    979         maybeMoveToSpeakerPhone(call);
    980     }
    981 
    982     void markCallAsActive(Call call) {
    983         setCallState(call, CallState.ACTIVE, "active set explicitly");
    984         maybeMoveToSpeakerPhone(call);
    985     }
    986 
    987     void markCallAsOnHold(Call call) {
    988         setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
    989     }
    990 
    991     /**
    992      * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
    993      * last live call, then also disconnect from the in-call controller.
    994      *
    995      * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
    996      */
    997     void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
    998         call.setDisconnectCause(disconnectCause);
    999         setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
   1000     }
   1001 
   1002     /**
   1003      * Removes an existing disconnected call, and notifies the in-call app.
   1004      */
   1005     void markCallAsRemoved(Call call) {
   1006         removeCall(call);
   1007         if (mLocallyDisconnectingCalls.contains(call)) {
   1008             mLocallyDisconnectingCalls.remove(call);
   1009             if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
   1010                 mForegroundCall.unhold();
   1011             }
   1012         }
   1013     }
   1014 
   1015     /**
   1016      * Cleans up any calls currently associated with the specified connection service when the
   1017      * service binder disconnects unexpectedly.
   1018      *
   1019      * @param service The connection service that disconnected.
   1020      */
   1021     void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
   1022         if (service != null) {
   1023             for (Call call : mCalls) {
   1024                 if (call.getConnectionService() == service) {
   1025                     if (call.getState() != CallState.DISCONNECTED) {
   1026                         markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
   1027                     }
   1028                     markCallAsRemoved(call);
   1029                 }
   1030             }
   1031         }
   1032     }
   1033 
   1034     boolean hasAnyCalls() {
   1035         return !mCalls.isEmpty();
   1036     }
   1037 
   1038     boolean hasActiveOrHoldingCall() {
   1039         return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
   1040     }
   1041 
   1042     boolean hasRingingCall() {
   1043         return getFirstCallWithState(CallState.RINGING) != null;
   1044     }
   1045 
   1046     boolean onMediaButton(int type) {
   1047         if (hasAnyCalls()) {
   1048             if (HeadsetMediaButton.SHORT_PRESS == type) {
   1049                 Call ringingCall = getFirstCallWithState(CallState.RINGING);
   1050                 if (ringingCall == null) {
   1051                     mCallAudioManager.toggleMute();
   1052                     return true;
   1053                 } else {
   1054                     ringingCall.answer(ringingCall.getVideoState());
   1055                     return true;
   1056                 }
   1057             } else if (HeadsetMediaButton.LONG_PRESS == type) {
   1058                 Log.d(this, "handleHeadsetHook: longpress -> hangup");
   1059                 Call callToHangup = getFirstCallWithState(
   1060                         CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
   1061                 if (callToHangup != null) {
   1062                     callToHangup.disconnect();
   1063                     return true;
   1064                 }
   1065             }
   1066         }
   1067         return false;
   1068     }
   1069 
   1070     /**
   1071      * Returns true if telecom supports adding another top-level call.
   1072      */
   1073     boolean canAddCall() {
   1074         if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
   1075             return false;
   1076         }
   1077 
   1078         int count = 0;
   1079         for (Call call : mCalls) {
   1080             if (call.isEmergencyCall()) {
   1081                 // We never support add call if one of the calls is an emergency call.
   1082                 return false;
   1083             } else  if (!call.getChildCalls().isEmpty() && !call.can(Connection.CAPABILITY_HOLD)) {
   1084                 // This is to deal with CDMA conference calls. CDMA conference calls do not
   1085                 // allow the addition of another call when it is already in a 3 way conference.
   1086                 // So, we detect that it is a CDMA conference call by checking if the call has
   1087                 // some children and it does not support the CAPABILILTY_HOLD
   1088                 // TODO: This maybe cleaner if the lower layers can explicitly signal to telecom
   1089                 // about this limitation (b/22880180).
   1090                 return false;
   1091             } else if (call.getParentCall() == null) {
   1092                 count++;
   1093             }
   1094 
   1095             // We do not check states for canAddCall. We treat disconnected calls the same
   1096             // and wait until they are removed instead. If we didn't count disconnected calls,
   1097             // we could put InCallServices into a state where they are showing two calls but
   1098             // also support add-call. Technically it's right, but overall looks better (UI-wise)
   1099             // and acts better if we wait until the call is removed.
   1100             if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
   1101                 return false;
   1102             }
   1103         }
   1104         return true;
   1105     }
   1106 
   1107     @VisibleForTesting
   1108     public Call getRingingCall() {
   1109         return getFirstCallWithState(CallState.RINGING);
   1110     }
   1111 
   1112     Call getActiveCall() {
   1113         return getFirstCallWithState(CallState.ACTIVE);
   1114     }
   1115 
   1116     Call getDialingCall() {
   1117         return getFirstCallWithState(CallState.DIALING);
   1118     }
   1119 
   1120     Call getHeldCall() {
   1121         return getFirstCallWithState(CallState.ON_HOLD);
   1122     }
   1123 
   1124     int getNumHeldCalls() {
   1125         int count = 0;
   1126         for (Call call : mCalls) {
   1127             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
   1128                 count++;
   1129             }
   1130         }
   1131         return count;
   1132     }
   1133 
   1134     Call getOutgoingCall() {
   1135         return getFirstCallWithState(OUTGOING_CALL_STATES);
   1136     }
   1137 
   1138     Call getFirstCallWithState(int... states) {
   1139         return getFirstCallWithState(null, states);
   1140     }
   1141 
   1142     /**
   1143      * Returns the first call that it finds with the given states. The states are treated as having
   1144      * priority order so that any call with the first state will be returned before any call with
   1145      * states listed later in the parameter list.
   1146      *
   1147      * @param callToSkip Call that this method should skip while searching
   1148      */
   1149     Call getFirstCallWithState(Call callToSkip, int... states) {
   1150         for (int currentState : states) {
   1151             // check the foreground first
   1152             if (mForegroundCall != null && mForegroundCall.getState() == currentState) {
   1153                 return mForegroundCall;
   1154             }
   1155 
   1156             for (Call call : mCalls) {
   1157                 if (Objects.equals(callToSkip, call)) {
   1158                     continue;
   1159                 }
   1160 
   1161                 // Only operate on top-level calls
   1162                 if (call.getParentCall() != null) {
   1163                     continue;
   1164                 }
   1165 
   1166                 if (currentState == call.getState()) {
   1167                     return call;
   1168                 }
   1169             }
   1170         }
   1171         return null;
   1172     }
   1173 
   1174     Call createConferenceCall(
   1175             PhoneAccountHandle phoneAccount,
   1176             ParcelableConference parcelableConference) {
   1177 
   1178         // If the parceled conference specifies a connect time, use it; otherwise default to 0,
   1179         // which is the default value for new Calls.
   1180         long connectTime =
   1181                 parcelableConference.getConnectTimeMillis() ==
   1182                         Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
   1183                         parcelableConference.getConnectTimeMillis();
   1184 
   1185         Call call = new Call(
   1186                 mContext,
   1187                 this,
   1188                 mLock,
   1189                 mConnectionServiceRepository,
   1190                 mContactsAsyncHelper,
   1191                 mCallerInfoAsyncQueryFactory,
   1192                 null /* handle */,
   1193                 null /* gatewayInfo */,
   1194                 null /* connectionManagerPhoneAccount */,
   1195                 phoneAccount,
   1196                 false /* isIncoming */,
   1197                 true /* isConference */,
   1198                 connectTime);
   1199 
   1200         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
   1201                 "new conference call");
   1202         call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
   1203         call.setVideoState(parcelableConference.getVideoState());
   1204         call.setVideoProvider(parcelableConference.getVideoProvider());
   1205         call.setStatusHints(parcelableConference.getStatusHints());
   1206         call.setExtras(parcelableConference.getExtras());
   1207 
   1208         // TODO: Move this to be a part of addCall()
   1209         call.addListener(this);
   1210         addCall(call);
   1211         return call;
   1212     }
   1213 
   1214     /**
   1215      * @return the call state currently tracked by {@link PhoneStateBroadcaster}
   1216      */
   1217     int getCallState() {
   1218         return mPhoneStateBroadcaster.getCallState();
   1219     }
   1220 
   1221     /**
   1222      * Retrieves the {@link PhoneAccountRegistrar}.
   1223      *
   1224      * @return The {@link PhoneAccountRegistrar}.
   1225      */
   1226     PhoneAccountRegistrar getPhoneAccountRegistrar() {
   1227         return mPhoneAccountRegistrar;
   1228     }
   1229 
   1230     /**
   1231      * Retrieves the {@link MissedCallNotifier}
   1232      * @return The {@link MissedCallNotifier}.
   1233      */
   1234     MissedCallNotifier getMissedCallNotifier() {
   1235         return mMissedCallNotifier;
   1236     }
   1237 
   1238     /**
   1239      * Adds the specified call to the main list of live calls.
   1240      *
   1241      * @param call The call to add.
   1242      */
   1243     private void addCall(Call call) {
   1244         Trace.beginSection("addCall");
   1245         Log.v(this, "addCall(%s)", call);
   1246         call.addListener(this);
   1247         mCalls.add(call);
   1248 
   1249         // TODO: Update mForegroundCall prior to invoking
   1250         // onCallAdded for calls which immediately take the foreground (like the first call).
   1251         for (CallsManagerListener listener : mListeners) {
   1252             if (Log.SYSTRACE_DEBUG) {
   1253                 Trace.beginSection(listener.getClass().toString() + " addCall");
   1254             }
   1255             listener.onCallAdded(call);
   1256             if (Log.SYSTRACE_DEBUG) {
   1257                 Trace.endSection();
   1258             }
   1259         }
   1260         updateCallsManagerState();
   1261         Trace.endSection();
   1262     }
   1263 
   1264     private void removeCall(Call call) {
   1265         Trace.beginSection("removeCall");
   1266         Log.v(this, "removeCall(%s)", call);
   1267 
   1268         call.setParentCall(null);  // need to clean up parent relationship before destroying.
   1269         call.removeListener(this);
   1270         call.clearConnectionService();
   1271 
   1272         boolean shouldNotify = false;
   1273         if (mCalls.contains(call)) {
   1274             mCalls.remove(call);
   1275             shouldNotify = true;
   1276         }
   1277 
   1278         call.destroy();
   1279 
   1280         // Only broadcast changes for calls that are being tracked.
   1281         if (shouldNotify) {
   1282             for (CallsManagerListener listener : mListeners) {
   1283                 if (Log.SYSTRACE_DEBUG) {
   1284                     Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
   1285                 }
   1286                 listener.onCallRemoved(call);
   1287                 if (Log.SYSTRACE_DEBUG) {
   1288                     Trace.endSection();
   1289                 }
   1290             }
   1291             updateCallsManagerState();
   1292         }
   1293         Trace.endSection();
   1294     }
   1295 
   1296     /**
   1297      * Sets the specified state on the specified call.
   1298      *
   1299      * @param call The call.
   1300      * @param newState The new state of the call.
   1301      */
   1302     private void setCallState(Call call, int newState, String tag) {
   1303         if (call == null) {
   1304             return;
   1305         }
   1306         int oldState = call.getState();
   1307         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
   1308                 CallState.toString(newState), call);
   1309         if (newState != oldState) {
   1310             // Unfortunately, in the telephony world the radio is king. So if the call notifies
   1311             // us that the call is in a particular state, we allow it even if it doesn't make
   1312             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
   1313             // TODO: Consider putting a stop to the above and turning CallState
   1314             // into a well-defined state machine.
   1315             // TODO: Define expected state transitions here, and log when an
   1316             // unexpected transition occurs.
   1317             call.setState(newState, tag);
   1318 
   1319             Trace.beginSection("onCallStateChanged");
   1320             // Only broadcast state change for calls that are being tracked.
   1321             if (mCalls.contains(call)) {
   1322                 for (CallsManagerListener listener : mListeners) {
   1323                     if (Log.SYSTRACE_DEBUG) {
   1324                         Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
   1325                     }
   1326                     listener.onCallStateChanged(call, oldState, newState);
   1327                     if (Log.SYSTRACE_DEBUG) {
   1328                         Trace.endSection();
   1329                     }
   1330                 }
   1331                 updateCallsManagerState();
   1332             }
   1333             Trace.endSection();
   1334         }
   1335     }
   1336 
   1337     /**
   1338      * Checks which call should be visible to the user and have audio focus.
   1339      */
   1340     private void updateForegroundCall() {
   1341         Trace.beginSection("updateForegroundCall");
   1342         Call newForegroundCall = null;
   1343         for (Call call : mCalls) {
   1344             // TODO: Foreground-ness needs to be explicitly set. No call, regardless
   1345             // of its state will be foreground by default and instead the connection service should
   1346             // be notified when its calls enter and exit foreground state. Foreground will mean that
   1347             // the call should play audio and listen to microphone if it wants.
   1348 
   1349             // Only top-level calls can be in foreground
   1350             if (call.getParentCall() != null) {
   1351                 continue;
   1352             }
   1353 
   1354             // Active calls have priority.
   1355             if (call.isActive()) {
   1356                 newForegroundCall = call;
   1357                 break;
   1358             }
   1359 
   1360             if (call.isAlive() || call.getState() == CallState.RINGING) {
   1361                 newForegroundCall = call;
   1362                 // Don't break in case there's an active call that has priority.
   1363             }
   1364         }
   1365 
   1366         if (newForegroundCall != mForegroundCall) {
   1367             Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
   1368             Call oldForegroundCall = mForegroundCall;
   1369             mForegroundCall = newForegroundCall;
   1370 
   1371             for (CallsManagerListener listener : mListeners) {
   1372                 if (Log.SYSTRACE_DEBUG) {
   1373                     Trace.beginSection(listener.getClass().toString() + " updateForegroundCall");
   1374                 }
   1375                 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
   1376                 if (Log.SYSTRACE_DEBUG) {
   1377                     Trace.endSection();
   1378                 }
   1379             }
   1380         }
   1381         Trace.endSection();
   1382     }
   1383 
   1384     private void updateCanAddCall() {
   1385         boolean newCanAddCall = canAddCall();
   1386         if (newCanAddCall != mCanAddCall) {
   1387             mCanAddCall = newCanAddCall;
   1388             for (CallsManagerListener listener : mListeners) {
   1389                 if (Log.SYSTRACE_DEBUG) {
   1390                     Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
   1391                 }
   1392                 listener.onCanAddCallChanged(mCanAddCall);
   1393                 if (Log.SYSTRACE_DEBUG) {
   1394                     Trace.endSection();
   1395                 }
   1396             }
   1397         }
   1398     }
   1399 
   1400     private void updateCallsManagerState() {
   1401         updateForegroundCall();
   1402         updateCanAddCall();
   1403     }
   1404 
   1405     private boolean isPotentialMMICode(Uri handle) {
   1406         return (handle != null && handle.getSchemeSpecificPart() != null
   1407                 && handle.getSchemeSpecificPart().contains("#"));
   1408     }
   1409 
   1410     /**
   1411      * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
   1412      * MMI codes which can be dialed when one or more calls are in progress.
   1413      * <P>
   1414      * Checks for numbers formatted similar to the MMI codes defined in:
   1415      * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
   1416      * and
   1417      * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
   1418      *
   1419      * @param handle The URI to call.
   1420      * @return {@code True} if the URI represents a number which could be an in-call MMI code.
   1421      */
   1422     private boolean isPotentialInCallMMICode(Uri handle) {
   1423         if (handle != null && handle.getSchemeSpecificPart() != null &&
   1424                 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
   1425 
   1426             String dialedNumber = handle.getSchemeSpecificPart();
   1427             return (dialedNumber.equals("0") ||
   1428                     (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
   1429                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
   1430                     dialedNumber.equals("3") ||
   1431                     dialedNumber.equals("4") ||
   1432                     dialedNumber.equals("5"));
   1433         }
   1434         return false;
   1435     }
   1436 
   1437     private int getNumCallsWithState(int... states) {
   1438         int count = 0;
   1439         for (int state : states) {
   1440             for (Call call : mCalls) {
   1441                 if (call.getParentCall() == null && call.getState() == state) {
   1442                     count++;
   1443                 }
   1444             }
   1445         }
   1446         return count;
   1447     }
   1448 
   1449     private boolean hasMaximumLiveCalls() {
   1450         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
   1451     }
   1452 
   1453     private boolean hasMaximumHoldingCalls() {
   1454         return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
   1455     }
   1456 
   1457     private boolean hasMaximumRingingCalls() {
   1458         return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
   1459     }
   1460 
   1461     private boolean hasMaximumOutgoingCalls() {
   1462         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
   1463     }
   1464 
   1465     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
   1466         if (hasMaximumLiveCalls()) {
   1467             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
   1468             // have to change.
   1469             Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
   1470             Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
   1471                    liveCall);
   1472 
   1473             if (call == liveCall) {
   1474                 // If the call is already the foreground call, then we are golden.
   1475                 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT
   1476                 // state since the call was already populated into the list.
   1477                 return true;
   1478             }
   1479 
   1480             if (hasMaximumOutgoingCalls()) {
   1481                 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
   1482                 if (isEmergency && !outgoingCall.isEmergencyCall()) {
   1483                     // Disconnect the current outgoing call if it's not an emergency call. If the
   1484                     // user tries to make two outgoing calls to different emergency call numbers,
   1485                     // we will try to connect the first outgoing call.
   1486                     outgoingCall.disconnect();
   1487                     return true;
   1488                 }
   1489                 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
   1490                     // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
   1491                     // state, just disconnect it since the user has explicitly started a new call.
   1492                     outgoingCall.disconnect();
   1493                     return true;
   1494                 }
   1495                 return false;
   1496             }
   1497 
   1498             if (hasMaximumHoldingCalls()) {
   1499                 // There is no more room for any more calls, unless it's an emergency.
   1500                 if (isEmergency) {
   1501                     // Kill the current active call, this is easier then trying to disconnect a
   1502                     // holding call and hold an active call.
   1503                     liveCall.disconnect();
   1504                     return true;
   1505                 }
   1506                 return false;  // No more room!
   1507             }
   1508 
   1509             // We have room for at least one more holding call at this point.
   1510 
   1511             // TODO: Remove once b/23035408 has been corrected.
   1512             // If the live call is a conference, it will not have a target phone account set.  This
   1513             // means the check to see if the live call has the same target phone account as the new
   1514             // call will not cause us to bail early.  As a result, we'll end up holding the
   1515             // ongoing conference call.  However, the ConnectionService is already doing that.  This
   1516             // has caused problems with some carriers.  As a workaround until b/23035408 is
   1517             // corrected, we will try and get the target phone account for one of the conference's
   1518             // children and use that instead.
   1519             PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount();
   1520             if (liveCallPhoneAccount == null && liveCall.isConference() &&
   1521                     !liveCall.getChildCalls().isEmpty()) {
   1522                 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall);
   1523                 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " +
   1524                         liveCallPhoneAccount);
   1525             }
   1526 
   1527             // First thing, if we are trying to make a call with the same phone account as the live
   1528             // call, then allow it so that the connection service can make its own decision about
   1529             // how to handle the new call relative to the current one.
   1530             if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
   1531                 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
   1532                 return true;
   1533             } else if (call.getTargetPhoneAccount() == null) {
   1534                 // Without a phone account, we can't say reliably that the call will fail.
   1535                 // If the user chooses the same phone account as the live call, then it's
   1536                 // still possible that the call can be made (like with CDMA calls not supporting
   1537                 // hold but they still support adding a call by going immediately into conference
   1538                 // mode). Return true here and we'll run this code again after user chooses an
   1539                 // account.
   1540                 return true;
   1541             }
   1542 
   1543             // Try to hold the live call before attempting the new outgoing call.
   1544             if (liveCall.can(Connection.CAPABILITY_HOLD)) {
   1545                 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
   1546                 liveCall.hold();
   1547                 return true;
   1548             }
   1549 
   1550             // The live call cannot be held so we're out of luck here.  There's no room.
   1551             return false;
   1552         }
   1553         return true;
   1554     }
   1555 
   1556     /**
   1557      * Given a call, find the first non-null phone account handle of its children.
   1558      *
   1559      * @param parentCall The parent call.
   1560      * @return The first non-null phone account handle of the children, or {@code null} if none.
   1561      */
   1562     private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) {
   1563         for (Call childCall : parentCall.getChildCalls()) {
   1564             PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount();
   1565             if (childPhoneAccount != null) {
   1566                 return childPhoneAccount;
   1567             }
   1568         }
   1569         return null;
   1570     }
   1571 
   1572     /**
   1573      * Checks to see if the call should be on speakerphone and if so, set it.
   1574      */
   1575     private void maybeMoveToSpeakerPhone(Call call) {
   1576         if (call.getStartWithSpeakerphoneOn()) {
   1577             setAudioRoute(CallAudioState.ROUTE_SPEAKER);
   1578             call.setStartWithSpeakerphoneOn(false);
   1579         }
   1580     }
   1581 
   1582     /**
   1583      * Creates a new call for an existing connection.
   1584      *
   1585      * @param callId The id of the new call.
   1586      * @param connection The connection information.
   1587      * @return The new call.
   1588      */
   1589     Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
   1590         Call call = new Call(
   1591                 mContext,
   1592                 this,
   1593                 mLock,
   1594                 mConnectionServiceRepository,
   1595                 mContactsAsyncHelper,
   1596                 mCallerInfoAsyncQueryFactory,
   1597                 connection.getHandle() /* handle */,
   1598                 null /* gatewayInfo */,
   1599                 null /* connectionManagerPhoneAccount */,
   1600                 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
   1601                 false /* isIncoming */,
   1602                 false /* isConference */,
   1603                 connection.getConnectTimeMillis() /* connectTimeMillis */);
   1604 
   1605         setCallState(call, Call.getStateFromConnectionState(connection.getState()),
   1606                 "existing connection");
   1607         call.setConnectionCapabilities(connection.getConnectionCapabilities());
   1608         call.setCallerDisplayName(connection.getCallerDisplayName(),
   1609                 connection.getCallerDisplayNamePresentation());
   1610 
   1611         call.addListener(this);
   1612         addCall(call);
   1613 
   1614         return call;
   1615     }
   1616 
   1617     /**
   1618      * Dumps the state of the {@link CallsManager}.
   1619      *
   1620      * @param pw The {@code IndentingPrintWriter} to write the state to.
   1621      */
   1622     public void dump(IndentingPrintWriter pw) {
   1623         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   1624         if (mCalls != null) {
   1625             pw.println("mCalls: ");
   1626             pw.increaseIndent();
   1627             for (Call call : mCalls) {
   1628                 pw.println(call);
   1629             }
   1630             pw.decreaseIndent();
   1631         }
   1632         pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall));
   1633 
   1634         if (mCallAudioManager != null) {
   1635             pw.println("mCallAudioManager:");
   1636             pw.increaseIndent();
   1637             mCallAudioManager.dump(pw);
   1638             pw.decreaseIndent();
   1639         }
   1640 
   1641         if (mTtyManager != null) {
   1642             pw.println("mTtyManager:");
   1643             pw.increaseIndent();
   1644             mTtyManager.dump(pw);
   1645             pw.decreaseIndent();
   1646         }
   1647 
   1648         if (mInCallController != null) {
   1649             pw.println("mInCallController:");
   1650             pw.increaseIndent();
   1651             mInCallController.dump(pw);
   1652             pw.decreaseIndent();
   1653         }
   1654 
   1655         if (mConnectionServiceRepository != null) {
   1656             pw.println("mConnectionServiceRepository:");
   1657             pw.increaseIndent();
   1658             mConnectionServiceRepository.dump(pw);
   1659             pw.decreaseIndent();
   1660         }
   1661     }
   1662 }
   1663