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.app.ActivityManager;
     20 import android.content.Context;
     21 import android.content.pm.UserInfo;
     22 import android.content.Intent;
     23 import android.media.AudioManager;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.Handler;
     27 import android.os.Looper;
     28 import android.os.Process;
     29 import android.os.SystemClock;
     30 import android.os.SystemProperties;
     31 import android.os.SystemVibrator;
     32 import android.os.Trace;
     33 import android.os.UserHandle;
     34 import android.os.UserManager;
     35 import android.provider.CallLog.Calls;
     36 import android.provider.Settings;
     37 import android.telecom.CallAudioState;
     38 import android.telecom.Conference;
     39 import android.telecom.Connection;
     40 import android.telecom.DisconnectCause;
     41 import android.telecom.GatewayInfo;
     42 import android.telecom.ParcelableConference;
     43 import android.telecom.ParcelableConnection;
     44 import android.telecom.PhoneAccount;
     45 import android.telecom.PhoneAccountHandle;
     46 import android.telecom.TelecomManager;
     47 import android.telecom.VideoProfile;
     48 import android.telephony.PhoneNumberUtils;
     49 import android.telephony.TelephonyManager;
     50 import android.text.TextUtils;
     51 
     52 import com.android.internal.annotations.VisibleForTesting;
     53 import com.android.internal.telephony.AsyncEmergencyContactNotifier;
     54 import com.android.internal.telephony.PhoneConstants;
     55 import com.android.internal.telephony.TelephonyProperties;
     56 import com.android.internal.util.IndentingPrintWriter;
     57 import com.android.server.telecom.TelecomServiceImpl.DefaultDialerManagerAdapter;
     58 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
     59 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
     60 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
     61 import com.android.server.telecom.callfiltering.CallFilteringResult;
     62 import com.android.server.telecom.callfiltering.CallScreeningServiceFilter;
     63 import com.android.server.telecom.callfiltering.DirectToVoicemailCallFilter;
     64 import com.android.server.telecom.callfiltering.IncomingCallFilter;
     65 import com.android.server.telecom.components.ErrorDialogActivity;
     66 
     67 import java.util.ArrayList;
     68 import java.util.Collection;
     69 import java.util.Collections;
     70 import java.util.HashMap;
     71 import java.util.HashSet;
     72 import java.util.List;
     73 import java.util.Map;
     74 import java.util.Objects;
     75 import java.util.Set;
     76 import java.util.concurrent.ConcurrentHashMap;
     77 
     78 /**
     79  * Singleton.
     80  *
     81  * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
     82  * access from other packages specifically refraining from passing the CallsManager instance
     83  * beyond the com.android.server.telecom package boundary.
     84  */
     85 @VisibleForTesting
     86 public class CallsManager extends Call.ListenerBase
     87         implements VideoProviderProxy.Listener, CallFilterResultCallback {
     88 
     89     // TODO: Consider renaming this CallsManagerPlugin.
     90     @VisibleForTesting
     91     public interface CallsManagerListener {
     92         void onCallAdded(Call call);
     93         void onCallRemoved(Call call);
     94         void onCallStateChanged(Call call, int oldState, int newState);
     95         void onConnectionServiceChanged(
     96                 Call call,
     97                 ConnectionServiceWrapper oldService,
     98                 ConnectionServiceWrapper newService);
     99         void onIncomingCallAnswered(Call call);
    100         void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
    101         void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState);
    102         void onRingbackRequested(Call call, boolean ringback);
    103         void onIsConferencedChanged(Call call);
    104         void onIsVoipAudioModeChanged(Call call);
    105         void onVideoStateChanged(Call call);
    106         void onCanAddCallChanged(boolean canAddCall);
    107         void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile);
    108         void onHoldToneRequested(Call call);
    109         void onExternalCallChanged(Call call, boolean isExternalCall);
    110     }
    111 
    112     private static final String TAG = "CallsManager";
    113 
    114     private static final int MAXIMUM_LIVE_CALLS = 1;
    115     private static final int MAXIMUM_HOLD_CALLS = 1;
    116     private static final int MAXIMUM_RINGING_CALLS = 1;
    117     private static final int MAXIMUM_DIALING_CALLS = 1;
    118     private static final int MAXIMUM_OUTGOING_CALLS = 1;
    119     private static final int MAXIMUM_TOP_LEVEL_CALLS = 2;
    120 
    121     private static final int[] OUTGOING_CALL_STATES =
    122             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING};
    123 
    124     private static final int[] LIVE_CALL_STATES =
    125             {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING,
    126                 CallState.ACTIVE};
    127     public static final String TELECOM_CALL_ID_PREFIX = "TC@";
    128 
    129     // Maps call technologies in PhoneConstants to those in Analytics.
    130     private static final Map<Integer, Integer> sAnalyticsTechnologyMap;
    131     static {
    132         sAnalyticsTechnologyMap = new HashMap<>(5);
    133         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE);
    134         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE);
    135         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE);
    136         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE);
    137         sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY,
    138                 Analytics.THIRD_PARTY_PHONE);
    139     }
    140 
    141     /**
    142      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
    143      * calls are added to the map and removed when the calls move to the disconnected state.
    144      *
    145      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    146      * load factor before resizing, 1 means we only expect a single thread to
    147      * access the map so make only a single shard
    148      */
    149     private final Set<Call> mCalls = Collections.newSetFromMap(
    150             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
    151 
    152     /**
    153      * The current telecom call ID.  Used when creating new instances of {@link Call}.  Should
    154      * only be accessed using the {@link #getNextCallId()} method which synchronizes on the
    155      * {@link #mLock} sync root.
    156      */
    157     private int mCallId = 0;
    158 
    159     /**
    160      * Stores the current foreground user.
    161      */
    162     private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
    163 
    164     private final ConnectionServiceRepository mConnectionServiceRepository;
    165     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
    166     private final InCallController mInCallController;
    167     private final CallAudioManager mCallAudioManager;
    168     private RespondViaSmsManager mRespondViaSmsManager;
    169     private final Ringer mRinger;
    170     private final InCallWakeLockController mInCallWakeLockController;
    171     // For this set initial table size to 16 because we add 13 listeners in
    172     // the CallsManager constructor.
    173     private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
    174             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
    175     private final HeadsetMediaButton mHeadsetMediaButton;
    176     private final WiredHeadsetManager mWiredHeadsetManager;
    177     private final BluetoothManager mBluetoothManager;
    178     private final DockManager mDockManager;
    179     private final TtyManager mTtyManager;
    180     private final ProximitySensorManager mProximitySensorManager;
    181     private final PhoneStateBroadcaster mPhoneStateBroadcaster;
    182     private final CallLogManager mCallLogManager;
    183     private final Context mContext;
    184     private final TelecomSystem.SyncRoot mLock;
    185     private final ContactsAsyncHelper mContactsAsyncHelper;
    186     private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
    187     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    188     private final MissedCallNotifier mMissedCallNotifier;
    189     private final CallerInfoLookupHelper mCallerInfoLookupHelper;
    190     private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
    191     private final Timeouts.Adapter mTimeoutsAdapter;
    192     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
    193     private final Set<Call> mPendingCallsToDisconnect = new HashSet<>();
    194     /* Handler tied to thread in which CallManager was initialized. */
    195     private final Handler mHandler = new Handler(Looper.getMainLooper());
    196 
    197     private boolean mCanAddCall = true;
    198 
    199     private TelephonyManager.MultiSimVariants mRadioSimVariants = null;
    200 
    201     private Runnable mStopTone;
    202 
    203     /**
    204      * Initializes the required Telecom components.
    205      */
    206     CallsManager(
    207             Context context,
    208             TelecomSystem.SyncRoot lock,
    209             ContactsAsyncHelper contactsAsyncHelper,
    210             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
    211             MissedCallNotifier missedCallNotifier,
    212             PhoneAccountRegistrar phoneAccountRegistrar,
    213             HeadsetMediaButtonFactory headsetMediaButtonFactory,
    214             ProximitySensorManagerFactory proximitySensorManagerFactory,
    215             InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
    216             CallAudioManager.AudioServiceFactory audioServiceFactory,
    217             BluetoothManager bluetoothManager,
    218             WiredHeadsetManager wiredHeadsetManager,
    219             SystemStateProvider systemStateProvider,
    220             DefaultDialerManagerAdapter defaultDialerAdapter,
    221             Timeouts.Adapter timeoutsAdapter,
    222             AsyncRingtonePlayer asyncRingtonePlayer) {
    223         mContext = context;
    224         mLock = lock;
    225         mContactsAsyncHelper = contactsAsyncHelper;
    226         mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
    227         mPhoneAccountRegistrar = phoneAccountRegistrar;
    228         mMissedCallNotifier = missedCallNotifier;
    229         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
    230         mWiredHeadsetManager = wiredHeadsetManager;
    231         mBluetoothManager = bluetoothManager;
    232         mDefaultDialerManagerAdapter = defaultDialerAdapter;
    233         mDockManager = new DockManager(context);
    234         mTimeoutsAdapter = timeoutsAdapter;
    235         mCallerInfoLookupHelper = new CallerInfoLookupHelper(context, mCallerInfoAsyncQueryFactory,
    236                 mContactsAsyncHelper, mLock);
    237 
    238         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer();
    239         CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine(
    240                 context,
    241                 this,
    242                 bluetoothManager,
    243                 wiredHeadsetManager,
    244                 statusBarNotifier,
    245                 audioServiceFactory,
    246                 CallAudioRouteStateMachine.doesDeviceSupportEarpieceRoute()
    247         );
    248         callAudioRouteStateMachine.initialize();
    249 
    250         CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter =
    251                 new CallAudioRoutePeripheralAdapter(
    252                         callAudioRouteStateMachine,
    253                         bluetoothManager,
    254                         wiredHeadsetManager,
    255                         mDockManager);
    256 
    257         InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(
    258                 callAudioRoutePeripheralAdapter, lock);
    259 
    260         SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil();
    261         RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context);
    262         SystemVibrator systemVibrator = new SystemVibrator(context);
    263         mInCallController = new InCallController(
    264                 context, mLock, this, systemStateProvider, defaultDialerAdapter);
    265         mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer,
    266                 ringtoneFactory, systemVibrator, mInCallController);
    267 
    268         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
    269                 this,new CallAudioModeStateMachine((AudioManager)
    270                         mContext.getSystemService(Context.AUDIO_SERVICE)),
    271                 playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer);
    272 
    273         mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock);
    274         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
    275         mProximitySensorManager = proximitySensorManagerFactory.create(context, this);
    276         mPhoneStateBroadcaster = new PhoneStateBroadcaster(this);
    277         mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier);
    278         mConnectionServiceRepository =
    279                 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this);
    280         mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this);
    281 
    282         mListeners.add(mInCallWakeLockController);
    283         mListeners.add(statusBarNotifier);
    284         mListeners.add(mCallLogManager);
    285         mListeners.add(mPhoneStateBroadcaster);
    286         mListeners.add(mInCallController);
    287         mListeners.add(mCallAudioManager);
    288         mListeners.add(missedCallNotifier);
    289         mListeners.add(mHeadsetMediaButton);
    290         mListeners.add(mProximitySensorManager);
    291 
    292         // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
    293         final UserManager userManager = UserManager.get(mContext);
    294         // Don't load missed call if it is run in split user model.
    295         if (userManager.isPrimaryUser()) {
    296             onUserSwitch(Process.myUserHandle());
    297         }
    298     }
    299 
    300     public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) {
    301         if (mRespondViaSmsManager != null) {
    302             mListeners.remove(mRespondViaSmsManager);
    303         }
    304         mRespondViaSmsManager = respondViaSmsManager;
    305         mListeners.add(respondViaSmsManager);
    306     }
    307 
    308     public RespondViaSmsManager getRespondViaSmsManager() {
    309         return mRespondViaSmsManager;
    310     }
    311 
    312     public CallerInfoLookupHelper getCallerInfoLookupHelper() {
    313         return mCallerInfoLookupHelper;
    314     }
    315 
    316     @Override
    317     public void onSuccessfulOutgoingCall(Call call, int callState) {
    318         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
    319 
    320         setCallState(call, callState, "successful outgoing call");
    321         if (!mCalls.contains(call)) {
    322             // Call was not added previously in startOutgoingCall due to it being a potential MMI
    323             // code, so add it now.
    324             addCall(call);
    325         }
    326 
    327         // The call's ConnectionService has been updated.
    328         for (CallsManagerListener listener : mListeners) {
    329             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
    330         }
    331 
    332         markCallAsDialing(call);
    333     }
    334 
    335     @Override
    336     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
    337         Log.v(this, "onFailedOutgoingCall, call: %s", call);
    338 
    339         markCallAsRemoved(call);
    340     }
    341 
    342     @Override
    343     public void onSuccessfulIncomingCall(Call incomingCall) {
    344         Log.d(this, "onSuccessfulIncomingCall");
    345         List<IncomingCallFilter.CallFilter> filters = new ArrayList<>();
    346         filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper));
    347         filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter()));
    348         filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar,
    349                 mDefaultDialerManagerAdapter,
    350                 new ParcelableCallUtils.Converter(), mLock));
    351         new IncomingCallFilter(mContext, this, incomingCall, mLock,
    352                 mTimeoutsAdapter, filters).performFiltering();
    353     }
    354 
    355     @Override
    356     public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) {
    357         // Only set the incoming call as ringing if it isn't already disconnected. It is possible
    358         // that the connection service disconnected the call before it was even added to Telecom, in
    359         // which case it makes no sense to set it back to a ringing state.
    360         if (incomingCall.getState() != CallState.DISCONNECTED &&
    361                 incomingCall.getState() != CallState.DISCONNECTING) {
    362             setCallState(incomingCall, CallState.RINGING,
    363                     result.shouldAllowCall ? "successful incoming call" : "blocking call");
    364         } else {
    365             Log.i(this, "onCallFilteringCompleted: call already disconnected.");
    366         }
    367 
    368         if (result.shouldAllowCall) {
    369             if (hasMaximumRingingCalls()) {
    370                 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
    371                         "ringing calls.");
    372                 rejectCallAndLog(incomingCall);
    373             } else if (hasMaximumDialingCalls()) {
    374                 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " +
    375                         "dialing calls.");
    376                 rejectCallAndLog(incomingCall);
    377             } else {
    378                 addCall(incomingCall);
    379             }
    380         } else {
    381             if (result.shouldReject) {
    382                 Log.i(this, "onCallFilteringCompleted: blocked call, rejecting.");
    383                 incomingCall.reject(false, null);
    384             }
    385             if (result.shouldAddToCallLog) {
    386                 Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log.");
    387                 if (result.shouldShowNotification) {
    388                     Log.w(this, "onCallScreeningCompleted: blocked call, showing notification.");
    389                 }
    390                 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
    391                         result.shouldShowNotification);
    392             } else if (result.shouldShowNotification) {
    393                 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification.");
    394                 mMissedCallNotifier.showMissedCallNotification(incomingCall);
    395             }
    396         }
    397     }
    398 
    399     @Override
    400     public void onFailedIncomingCall(Call call) {
    401         setCallState(call, CallState.DISCONNECTED, "failed incoming call");
    402         call.removeListener(this);
    403     }
    404 
    405     @Override
    406     public void onSuccessfulUnknownCall(Call call, int callState) {
    407         setCallState(call, callState, "successful unknown call");
    408         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
    409         addCall(call);
    410     }
    411 
    412     @Override
    413     public void onFailedUnknownCall(Call call) {
    414         Log.i(this, "onFailedUnknownCall for call %s", call);
    415         setCallState(call, CallState.DISCONNECTED, "failed unknown call");
    416         call.removeListener(this);
    417     }
    418 
    419     @Override
    420     public void onRingbackRequested(Call call, boolean ringback) {
    421         for (CallsManagerListener listener : mListeners) {
    422             listener.onRingbackRequested(call, ringback);
    423         }
    424     }
    425 
    426     @Override
    427     public void onPostDialWait(Call call, String remaining) {
    428         mInCallController.onPostDialWait(call, remaining);
    429     }
    430 
    431     @Override
    432     public void onPostDialChar(final Call call, char nextChar) {
    433         if (PhoneNumberUtils.is12Key(nextChar)) {
    434             // Play tone if it is one of the dialpad digits, canceling out the previously queued
    435             // up stopTone runnable since playing a new tone automatically stops the previous tone.
    436             if (mStopTone != null) {
    437                 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
    438                 mStopTone.cancel();
    439             }
    440 
    441             mDtmfLocalTonePlayer.playTone(call, nextChar);
    442 
    443             // TODO: Create a LockedRunnable class that does the synchronization automatically.
    444             mStopTone = new Runnable("CM.oPDC") {
    445                 @Override
    446                 public void loggedRun() {
    447                     synchronized (mLock) {
    448                         // Set a timeout to stop the tone in case there isn't another tone to
    449                         // follow.
    450                         mDtmfLocalTonePlayer.stopTone(call);
    451                     }
    452                 }
    453             };
    454             mHandler.postDelayed(mStopTone.prepare(),
    455                     Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver()));
    456         } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT ||
    457                 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) {
    458             // Stop the tone if a tone is playing, removing any other stopTone callbacks since
    459             // the previous tone is being stopped anyway.
    460             if (mStopTone != null) {
    461                 mHandler.removeCallbacks(mStopTone.getRunnableToCancel());
    462                 mStopTone.cancel();
    463             }
    464             mDtmfLocalTonePlayer.stopTone(call);
    465         } else {
    466             Log.w(this, "onPostDialChar: invalid value %d", nextChar);
    467         }
    468     }
    469 
    470     @Override
    471     public void onParentChanged(Call call) {
    472         // parent-child relationship affects which call should be foreground, so do an update.
    473         updateCallsManagerState();
    474         for (CallsManagerListener listener : mListeners) {
    475             listener.onIsConferencedChanged(call);
    476         }
    477     }
    478 
    479     @Override
    480     public void onChildrenChanged(Call call) {
    481         // parent-child relationship affects which call should be foreground, so do an update.
    482         updateCallsManagerState();
    483         for (CallsManagerListener listener : mListeners) {
    484             listener.onIsConferencedChanged(call);
    485         }
    486     }
    487 
    488     @Override
    489     public void onIsVoipAudioModeChanged(Call call) {
    490         for (CallsManagerListener listener : mListeners) {
    491             listener.onIsVoipAudioModeChanged(call);
    492         }
    493     }
    494 
    495     @Override
    496     public void onVideoStateChanged(Call call) {
    497         for (CallsManagerListener listener : mListeners) {
    498             listener.onVideoStateChanged(call);
    499         }
    500     }
    501 
    502     @Override
    503     public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) {
    504         mPendingCallsToDisconnect.add(call);
    505         mHandler.postDelayed(new Runnable("CM.oCVNOCB") {
    506             @Override
    507             public void loggedRun() {
    508                 synchronized (mLock) {
    509                     if (mPendingCallsToDisconnect.remove(call)) {
    510                         Log.i(this, "Delayed disconnection of call: %s", call);
    511                         call.disconnect();
    512                     }
    513                 }
    514             }
    515         }.prepare(), Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver()));
    516 
    517         return true;
    518     }
    519 
    520     /**
    521      * Handles changes to the {@link Connection.VideoProvider} for a call.  Adds the
    522      * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created
    523      * in {@link Call#setVideoProvider(IVideoProvider)}.  This allows the {@link CallsManager} to
    524      * respond to callbacks from the {@link VideoProviderProxy}.
    525      *
    526      * @param call The call.
    527      */
    528     @Override
    529     public void onVideoCallProviderChanged(Call call) {
    530         VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy();
    531 
    532         if (videoProviderProxy == null) {
    533             return;
    534         }
    535 
    536         videoProviderProxy.addListener(this);
    537     }
    538 
    539     /**
    540      * Handles session modification requests received via the {@link TelecomVideoCallCallback} for
    541      * a call.  Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session
    542      * modification request.
    543      *
    544      * @param call The call.
    545      * @param videoProfile The {@link VideoProfile}.
    546      */
    547     @Override
    548     public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) {
    549         int videoState = videoProfile != null ? videoProfile.getVideoState() :
    550                 VideoProfile.STATE_AUDIO_ONLY;
    551         Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile
    552                 .videoStateToString(videoState));
    553 
    554         for (CallsManagerListener listener : mListeners) {
    555             listener.onSessionModifyRequestReceived(call, videoProfile);
    556         }
    557     }
    558 
    559     public Collection<Call> getCalls() {
    560         return Collections.unmodifiableCollection(mCalls);
    561     }
    562 
    563     /**
    564      * Play or stop a call hold tone for a call.  Triggered via
    565      * {@link Connection#sendConnectionEvent(String)} when the
    566      * {@link Connection#EVENT_ON_HOLD_TONE_START} event or
    567      * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the
    568      *
    569      * @param call The call which requested the hold tone.
    570      */
    571     @Override
    572     public void onHoldToneRequested(Call call) {
    573         for (CallsManagerListener listener : mListeners) {
    574             listener.onHoldToneRequested(call);
    575         }
    576     }
    577 
    578     @VisibleForTesting
    579     public Call getForegroundCall() {
    580         if (mCallAudioManager == null) {
    581             // Happens when getForegroundCall is called before full initialization.
    582             return null;
    583         }
    584         return mCallAudioManager.getForegroundCall();
    585     }
    586 
    587     public UserHandle getCurrentUserHandle() {
    588         return mCurrentUserHandle;
    589     }
    590 
    591     public CallAudioManager getCallAudioManager() {
    592         return mCallAudioManager;
    593     }
    594 
    595     InCallController getInCallController() {
    596         return mInCallController;
    597     }
    598 
    599     @VisibleForTesting
    600     public boolean hasEmergencyCall() {
    601         for (Call call : mCalls) {
    602             if (call.isEmergencyCall()) {
    603                 return true;
    604             }
    605         }
    606         return false;
    607     }
    608 
    609     boolean hasOnlyDisconnectedCalls() {
    610         for (Call call : mCalls) {
    611             if (!call.isDisconnected()) {
    612                 return false;
    613             }
    614         }
    615         return true;
    616     }
    617 
    618     boolean hasVideoCall() {
    619         for (Call call : mCalls) {
    620             if (VideoProfile.isVideo(call.getVideoState())) {
    621                 return true;
    622             }
    623         }
    624         return false;
    625     }
    626 
    627     CallAudioState getAudioState() {
    628         return mCallAudioManager.getCallAudioState();
    629     }
    630 
    631     boolean isTtySupported() {
    632         return mTtyManager.isTtySupported();
    633     }
    634 
    635     int getCurrentTtyMode() {
    636         return mTtyManager.getCurrentTtyMode();
    637     }
    638 
    639     @VisibleForTesting
    640     public void addListener(CallsManagerListener listener) {
    641         mListeners.add(listener);
    642     }
    643 
    644     void removeListener(CallsManagerListener listener) {
    645         mListeners.remove(listener);
    646     }
    647 
    648     /**
    649      * Starts the process to attach the call to a connection service.
    650      *
    651      * @param phoneAccountHandle The phone account which contains the component name of the
    652      *        connection service to use for this call.
    653      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    654      */
    655     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    656         Log.d(this, "processIncomingCallIntent");
    657         Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
    658         if (handle == null) {
    659             // Required for backwards compatibility
    660             handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
    661         }
    662         Call call = new Call(
    663                 getNextCallId(),
    664                 mContext,
    665                 this,
    666                 mLock,
    667                 mConnectionServiceRepository,
    668                 mContactsAsyncHelper,
    669                 mCallerInfoAsyncQueryFactory,
    670                 handle,
    671                 null /* gatewayInfo */,
    672                 null /* connectionManagerPhoneAccount */,
    673                 phoneAccountHandle,
    674                 Call.CALL_DIRECTION_INCOMING /* callDirection */,
    675                 false /* forceAttachToExistingConnection */,
    676                 false /* isConference */
    677         );
    678 
    679         call.initAnalytics();
    680         if (getForegroundCall() != null) {
    681             getForegroundCall().getAnalytics().setCallIsInterrupted(true);
    682             call.getAnalytics().setCallIsAdditional(true);
    683         }
    684 
    685         setIntentExtrasAndStartTime(call, extras);
    686         // TODO: Move this to be a part of addCall()
    687         call.addListener(this);
    688         call.startCreateConnection(mPhoneAccountRegistrar);
    689     }
    690 
    691     void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    692         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
    693         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
    694         Call call = new Call(
    695                 getNextCallId(),
    696                 mContext,
    697                 this,
    698                 mLock,
    699                 mConnectionServiceRepository,
    700                 mContactsAsyncHelper,
    701                 mCallerInfoAsyncQueryFactory,
    702                 handle,
    703                 null /* gatewayInfo */,
    704                 null /* connectionManagerPhoneAccount */,
    705                 phoneAccountHandle,
    706                 Call.CALL_DIRECTION_UNKNOWN /* callDirection */,
    707                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
    708                 // to the existing connection instead of trying to create a new one.
    709                 true /* forceAttachToExistingConnection */,
    710                 false /* isConference */
    711         );
    712         call.initAnalytics();
    713 
    714         setIntentExtrasAndStartTime(call, extras);
    715         call.addListener(this);
    716         call.startCreateConnection(mPhoneAccountRegistrar);
    717     }
    718 
    719     private boolean areHandlesEqual(Uri handle1, Uri handle2) {
    720         if (handle1 == null || handle2 == null) {
    721             return handle1 == handle2;
    722         }
    723 
    724         if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) {
    725             return false;
    726         }
    727 
    728         final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart());
    729         final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart());
    730         return TextUtils.equals(number1, number2);
    731     }
    732 
    733     private Call reuseOutgoingCall(Uri handle) {
    734         // Check to see if we can reuse any of the calls that are waiting to disconnect.
    735         // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information.
    736         Call reusedCall = null;
    737         for (Call pendingCall : mPendingCallsToDisconnect) {
    738             if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) {
    739                 mPendingCallsToDisconnect.remove(pendingCall);
    740                 Log.i(this, "Reusing disconnected call %s", pendingCall);
    741                 reusedCall = pendingCall;
    742             } else {
    743                 Log.i(this, "Not reusing disconnected call %s", pendingCall);
    744                 pendingCall.disconnect();
    745             }
    746         }
    747 
    748         return reusedCall;
    749     }
    750 
    751     /**
    752      * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
    753      *
    754      * @param handle Handle to connect the call with.
    755      * @param phoneAccountHandle The phone account which contains the component name of the
    756      *        connection service to use for this call.
    757      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    758      * @param initiatingUser {@link UserHandle} of user that place the outgoing call.
    759      */
    760     Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras,
    761             UserHandle initiatingUser) {
    762         boolean isReusedCall = true;
    763         Call call = reuseOutgoingCall(handle);
    764 
    765         // Create a call with original handle. The handle may be changed when the call is attached
    766         // to a connection service, but in most cases will remain the same.
    767         if (call == null) {
    768             call = new Call(getNextCallId(), mContext,
    769                     this,
    770                     mLock,
    771                     mConnectionServiceRepository,
    772                     mContactsAsyncHelper,
    773                     mCallerInfoAsyncQueryFactory,
    774                     handle,
    775                     null /* gatewayInfo */,
    776                     null /* connectionManagerPhoneAccount */,
    777                     null /* phoneAccountHandle */,
    778                     Call.CALL_DIRECTION_OUTGOING /* callDirection */,
    779                     false /* forceAttachToExistingConnection */,
    780                     false /* isConference */
    781             );
    782             call.setInitiatingUser(initiatingUser);
    783 
    784             call.initAnalytics();
    785 
    786             isReusedCall = false;
    787         }
    788 
    789         // Set the video state on the call early so that when it is added to the InCall UI the UI
    790         // knows to configure itself as a video call immediately.
    791         if (extras != null) {
    792             int videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
    793                     VideoProfile.STATE_AUDIO_ONLY);
    794 
    795             // If this is an emergency video call, we need to check if the phone account supports
    796             // emergency video calling.
    797             if (call.isEmergencyCall() && VideoProfile.isVideo(videoState)) {
    798                 PhoneAccount account =
    799                         mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser);
    800 
    801                 if (account != null &&
    802                         !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) {
    803                     // Phone account doesn't support emergency video calling, so fallback to
    804                     // audio-only now to prevent the InCall UI from setting up video surfaces
    805                     // needlessly.
    806                     Log.i(this, "startOutgoingCall - emergency video calls not supported; " +
    807                             "falling back to audio-only");
    808                     videoState = VideoProfile.STATE_AUDIO_ONLY;
    809                 }
    810             }
    811 
    812             call.setVideoState(videoState);
    813         }
    814 
    815         List<PhoneAccountHandle> accounts = constructPossiblePhoneAccounts(handle, initiatingUser);
    816         Log.v(this, "startOutgoingCall found accounts = " + accounts);
    817 
    818         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
    819         // as if a phoneAccount was not specified (does the default behavior instead).
    820         // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
    821         if (phoneAccountHandle != null) {
    822             if (!accounts.contains(phoneAccountHandle)) {
    823                 phoneAccountHandle = null;
    824             }
    825         }
    826 
    827         if (phoneAccountHandle == null && accounts.size() > 0 && !call.isEmergencyCall()) {
    828             // No preset account, check if default exists that supports the URI scheme for the
    829             // handle and verify it can be used.
    830             if(accounts.size() > 1) {
    831                 PhoneAccountHandle defaultPhoneAccountHandle =
    832                         mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme(handle.getScheme(),
    833                                 initiatingUser);
    834                 if (defaultPhoneAccountHandle != null &&
    835                         accounts.contains(defaultPhoneAccountHandle)) {
    836                     phoneAccountHandle = defaultPhoneAccountHandle;
    837                 }
    838             } else {
    839                 // Use the only PhoneAccount that is available
    840                 phoneAccountHandle = accounts.get(0);
    841             }
    842 
    843         }
    844 
    845         call.setTargetPhoneAccount(phoneAccountHandle);
    846 
    847         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
    848 
    849         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
    850         // a call, or cancel this call altogether. If a call is being reused, then it has already
    851         // passed the makeRoomForOutgoingCall check once and will fail the second time due to the
    852         // call transitioning into the CONNECTING state.
    853         if (!isPotentialInCallMMICode && (!isReusedCall &&
    854                 !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) {
    855             // just cancel at this point.
    856             Log.i(this, "No remaining room for outgoing call: %s", call);
    857             if (mCalls.contains(call)) {
    858                 // This call can already exist if it is a reused call,
    859                 // See {@link #reuseOutgoingCall}.
    860                 call.disconnect();
    861             }
    862             return null;
    863         }
    864 
    865         boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
    866                 !call.isEmergencyCall();
    867 
    868         if (needsAccountSelection) {
    869             // This is the state where the user is expected to select an account
    870             call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection");
    871             // Create our own instance to modify (since extras may be Bundle.EMPTY)
    872             extras = new Bundle(extras);
    873             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
    874         } else {
    875             call.setState(
    876                     CallState.CONNECTING,
    877                     phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString());
    878         }
    879 
    880         setIntentExtrasAndStartTime(call, extras);
    881 
    882         // Do not add the call if it is a potential MMI code.
    883         if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
    884             call.addListener(this);
    885         } else if (!mCalls.contains(call)) {
    886             // We check if mCalls already contains the call because we could potentially be reusing
    887             // a call which was previously added (See {@link #reuseOutgoingCall}).
    888             addCall(call);
    889         }
    890 
    891         return call;
    892     }
    893 
    894     /**
    895      * Attempts to issue/connect the specified call.
    896      *
    897      * @param handle Handle to connect the call with.
    898      * @param gatewayInfo Optional gateway information that can be used to route the call to the
    899      *        actual dialed handle via a gateway provider. May be null.
    900      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
    901      * @param videoState The desired video state for the outgoing call.
    902      */
    903     @VisibleForTesting
    904     public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo,
    905             boolean speakerphoneOn, int videoState) {
    906         if (call == null) {
    907             // don't do anything if the call no longer exists
    908             Log.i(this, "Canceling unknown call.");
    909             return;
    910         }
    911 
    912         final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
    913 
    914         if (gatewayInfo == null) {
    915             Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
    916         } else {
    917             Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
    918                     Log.pii(uriHandle), Log.pii(handle));
    919         }
    920 
    921         call.setHandle(uriHandle);
    922         call.setGatewayInfo(gatewayInfo);
    923 
    924         final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean(
    925                 R.bool.use_speaker_when_docked);
    926         final boolean isDocked = mDockManager.isDocked();
    927         final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabled(videoState);
    928 
    929         // Auto-enable speakerphone if the originating intent specified to do so, if the call
    930         // is a video call, of if using speaker when docked
    931         call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall
    932                 || (useSpeakerWhenDocked && isDocked));
    933         call.setVideoState(videoState);
    934 
    935         if (speakerphoneOn) {
    936             Log.i(this, "%s Starting with speakerphone as requested", call);
    937         } else if (useSpeakerWhenDocked && useSpeakerWhenDocked) {
    938             Log.i(this, "%s Starting with speakerphone because car is docked.", call);
    939         } else if (useSpeakerForVideoCall) {
    940             Log.i(this, "%s Starting with speakerphone because its a video call.", call);
    941         }
    942 
    943         if (call.isEmergencyCall()) {
    944             // Emergency -- CreateConnectionProcessor will choose accounts automatically
    945             call.setTargetPhoneAccount(null);
    946             new AsyncEmergencyContactNotifier(mContext).execute();
    947         }
    948 
    949         final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean(
    950                 com.android.internal.R.bool.config_requireCallCapableAccountForHandle);
    951 
    952         if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) {
    953             // If the account has been set, proceed to place the outgoing call.
    954             // Otherwise the connection will be initiated when the account is set by the user.
    955             call.startCreateConnection(mPhoneAccountRegistrar);
    956         } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts(
    957                 requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false,
    958                 call.getInitiatingUser()).isEmpty()) {
    959             // If there are no call capable accounts, disconnect the call.
    960             markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED,
    961                     "No registered PhoneAccounts"));
    962             markCallAsRemoved(call);
    963         }
    964     }
    965 
    966     /**
    967      * Attempts to start a conference call for the specified call.
    968      *
    969      * @param call The call to conference.
    970      * @param otherCall The other call to conference with.
    971      */
    972     @VisibleForTesting
    973     public void conference(Call call, Call otherCall) {
    974         call.conferenceWith(otherCall);
    975     }
    976 
    977     /**
    978      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
    979      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
    980      * the user opting to answer said call.
    981      *
    982      * @param call The call to answer.
    983      * @param videoState The video state in which to answer the call.
    984      */
    985     @VisibleForTesting
    986     public void answerCall(Call call, int videoState) {
    987         if (!mCalls.contains(call)) {
    988             Log.i(this, "Request to answer a non-existent call %s", call);
    989         } else {
    990             Call foregroundCall = getForegroundCall();
    991             // If the foreground call is not the ringing call and it is currently isActive() or
    992             // STATE_DIALING, put it on hold before answering the call.
    993             if (foregroundCall != null && foregroundCall != call &&
    994                     (foregroundCall.isActive() ||
    995                      foregroundCall.getState() == CallState.DIALING)) {
    996                 if (0 == (foregroundCall.getConnectionCapabilities()
    997                         & Connection.CAPABILITY_HOLD)) {
    998                     // This call does not support hold.  If it is from a different connection
    999                     // service, then disconnect it, otherwise allow the connection service to
   1000                     // figure out the right states.
   1001                     if (foregroundCall.getConnectionService() != call.getConnectionService()) {
   1002                         foregroundCall.disconnect();
   1003                     }
   1004                 } else {
   1005                     Call heldCall = getHeldCall();
   1006                     if (heldCall != null) {
   1007                         Log.v(this, "Disconnecting held call %s before holding active call.",
   1008                                 heldCall);
   1009                         heldCall.disconnect();
   1010                     }
   1011 
   1012                     Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
   1013                             foregroundCall, call);
   1014                     foregroundCall.hold();
   1015                 }
   1016                 // TODO: Wait until we get confirmation of the active call being
   1017                 // on-hold before answering the new call.
   1018                 // TODO: Import logic from CallManager.acceptCall()
   1019             }
   1020 
   1021             for (CallsManagerListener listener : mListeners) {
   1022                 listener.onIncomingCallAnswered(call);
   1023             }
   1024 
   1025             // We do not update the UI until we get confirmation of the answer() through
   1026             // {@link #markCallAsActive}.
   1027             call.answer(videoState);
   1028             if (isSpeakerphoneAutoEnabled(videoState)) {
   1029                 call.setStartWithSpeakerphoneOn(true);
   1030             }
   1031         }
   1032     }
   1033 
   1034     /**
   1035      * Determines if the speakerphone should be automatically enabled for the call.  Speakerphone
   1036      * should be enabled if the call is a video call and bluetooth or the wired headset are not in
   1037      * use.
   1038      *
   1039      * @param videoState The video state of the call.
   1040      * @return {@code true} if the speakerphone should be enabled.
   1041      */
   1042     private boolean isSpeakerphoneAutoEnabled(int videoState) {
   1043         return VideoProfile.isVideo(videoState) &&
   1044             !mWiredHeadsetManager.isPluggedIn() &&
   1045             !mBluetoothManager.isBluetoothAvailable() &&
   1046             isSpeakerEnabledForVideoCalls();
   1047     }
   1048 
   1049     /**
   1050      * Determines if the speakerphone should be automatically enabled for video calls.
   1051      *
   1052      * @return {@code true} if the speakerphone should automatically be enabled.
   1053      */
   1054     private static boolean isSpeakerEnabledForVideoCalls() {
   1055         return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT,
   1056                 PhoneConstants.AUDIO_OUTPUT_DEFAULT) ==
   1057                 PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER);
   1058     }
   1059 
   1060     /**
   1061      * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
   1062      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
   1063      * the user opting to reject said call.
   1064      */
   1065     @VisibleForTesting
   1066     public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
   1067         if (!mCalls.contains(call)) {
   1068             Log.i(this, "Request to reject a non-existent call %s", call);
   1069         } else {
   1070             for (CallsManagerListener listener : mListeners) {
   1071                 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
   1072             }
   1073             call.reject(rejectWithMessage, textMessage);
   1074         }
   1075     }
   1076 
   1077     /**
   1078      * Instructs Telecom to play the specified DTMF tone within the specified call.
   1079      *
   1080      * @param digit The DTMF digit to play.
   1081      */
   1082     @VisibleForTesting
   1083     public void playDtmfTone(Call call, char digit) {
   1084         if (!mCalls.contains(call)) {
   1085             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
   1086         } else {
   1087             call.playDtmfTone(digit);
   1088             mDtmfLocalTonePlayer.playTone(call, digit);
   1089         }
   1090     }
   1091 
   1092     /**
   1093      * Instructs Telecom to stop the currently playing DTMF tone, if any.
   1094      */
   1095     @VisibleForTesting
   1096     public void stopDtmfTone(Call call) {
   1097         if (!mCalls.contains(call)) {
   1098             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
   1099         } else {
   1100             call.stopDtmfTone();
   1101             mDtmfLocalTonePlayer.stopTone(call);
   1102         }
   1103     }
   1104 
   1105     /**
   1106      * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
   1107      */
   1108     void postDialContinue(Call call, boolean proceed) {
   1109         if (!mCalls.contains(call)) {
   1110             Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
   1111         } else {
   1112             call.postDialContinue(proceed);
   1113         }
   1114     }
   1115 
   1116     /**
   1117      * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
   1118      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
   1119      * the user hitting the end-call button.
   1120      */
   1121     @VisibleForTesting
   1122     public void disconnectCall(Call call) {
   1123         Log.v(this, "disconnectCall %s", call);
   1124 
   1125         if (!mCalls.contains(call)) {
   1126             Log.w(this, "Unknown call (%s) asked to disconnect", call);
   1127         } else {
   1128             mLocallyDisconnectingCalls.add(call);
   1129             call.disconnect();
   1130         }
   1131     }
   1132 
   1133     /**
   1134      * Instructs Telecom to disconnect all calls.
   1135      */
   1136     void disconnectAllCalls() {
   1137         Log.v(this, "disconnectAllCalls");
   1138 
   1139         for (Call call : mCalls) {
   1140             disconnectCall(call);
   1141         }
   1142     }
   1143 
   1144 
   1145     /**
   1146      * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
   1147      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
   1148      * the user hitting the hold button during an active call.
   1149      */
   1150     @VisibleForTesting
   1151     public void holdCall(Call call) {
   1152         if (!mCalls.contains(call)) {
   1153             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
   1154         } else {
   1155             Log.d(this, "Putting call on hold: (%s)", call);
   1156             call.hold();
   1157         }
   1158     }
   1159 
   1160     /**
   1161      * Instructs Telecom to release the specified call from hold. Intended to be invoked by
   1162      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
   1163      * by the user hitting the hold button during a held call.
   1164      */
   1165     @VisibleForTesting
   1166     public void unholdCall(Call call) {
   1167         if (!mCalls.contains(call)) {
   1168             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
   1169         } else {
   1170             Log.d(this, "unholding call: (%s)", call);
   1171             for (Call c : mCalls) {
   1172                 // Only attempt to hold parent calls and not the individual children.
   1173                 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) {
   1174                     c.hold();
   1175                 }
   1176             }
   1177             call.unhold();
   1178         }
   1179     }
   1180 
   1181     @Override
   1182     public void onExtrasChanged(Call c, int source, Bundle extras) {
   1183         if (source != Call.SOURCE_CONNECTION_SERVICE) {
   1184             return;
   1185         }
   1186         handleCallTechnologyChange(c);
   1187         handleChildAddressChange(c);
   1188     }
   1189 
   1190     // Construct the list of possible PhoneAccounts that the outgoing call can use based on the
   1191     // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount,
   1192     // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP.
   1193     private List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user) {
   1194         if (handle == null) {
   1195             return Collections.emptyList();
   1196         }
   1197         List<PhoneAccountHandle> allAccounts =
   1198                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user);
   1199         // First check the Radio SIM Technology
   1200         if(mRadioSimVariants == null) {
   1201             TelephonyManager tm = (TelephonyManager) mContext.getSystemService(
   1202                     Context.TELEPHONY_SERVICE);
   1203             // Cache Sim Variants
   1204             mRadioSimVariants = tm.getMultiSimConfiguration();
   1205         }
   1206         // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount
   1207         // Should be available if a call is already active on the SIM account.
   1208         if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) {
   1209             List<PhoneAccountHandle> simAccounts =
   1210                     mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser();
   1211             PhoneAccountHandle ongoingCallAccount = null;
   1212             for (Call c : mCalls) {
   1213                 if (!c.isDisconnected() && !c.isNew() && simAccounts.contains(
   1214                         c.getTargetPhoneAccount())) {
   1215                     ongoingCallAccount = c.getTargetPhoneAccount();
   1216                     break;
   1217                 }
   1218             }
   1219             if (ongoingCallAccount != null) {
   1220                 // Remove all SIM accounts that are not the active SIM from the list.
   1221                 simAccounts.remove(ongoingCallAccount);
   1222                 allAccounts.removeAll(simAccounts);
   1223             }
   1224         }
   1225         return allAccounts;
   1226     }
   1227 
   1228     /**
   1229      * Informs listeners (notably {@link CallAudioManager} of a change to the call's external
   1230      * property.
   1231      * .
   1232      * @param call The call whose external property changed.
   1233      * @param isExternalCall {@code True} if the call is now external, {@code false} otherwise.
   1234      */
   1235     @Override
   1236     public void onExternalCallChanged(Call call, boolean isExternalCall) {
   1237         Log.v(this, "onConnectionPropertiesChanged: %b", isExternalCall);
   1238         for (CallsManagerListener listener : mListeners) {
   1239             listener.onExternalCallChanged(call, isExternalCall);
   1240         }
   1241     }
   1242 
   1243     private void handleCallTechnologyChange(Call call) {
   1244         if (call.getExtras() != null
   1245                 && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) {
   1246 
   1247             Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get(
   1248                     call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE));
   1249             if (analyticsCallTechnology == null) {
   1250                 analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE;
   1251             }
   1252             call.getAnalytics().addCallTechnology(analyticsCallTechnology);
   1253         }
   1254     }
   1255 
   1256     public void handleChildAddressChange(Call call) {
   1257         if (call.getExtras() != null
   1258                 && call.getExtras().containsKey(Connection.EXTRA_CHILD_ADDRESS)) {
   1259 
   1260             String viaNumber = call.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS);
   1261             call.setViaNumber(viaNumber);
   1262         }
   1263     }
   1264 
   1265     /** Called by the in-call UI to change the mute state. */
   1266     void mute(boolean shouldMute) {
   1267         mCallAudioManager.mute(shouldMute);
   1268     }
   1269 
   1270     /**
   1271       * Called by the in-call UI to change the audio route, for example to change from earpiece to
   1272       * speaker phone.
   1273       */
   1274     void setAudioRoute(int route) {
   1275         mCallAudioManager.setAudioRoute(route);
   1276     }
   1277 
   1278     /** Called by the in-call UI to turn the proximity sensor on. */
   1279     void turnOnProximitySensor() {
   1280         mProximitySensorManager.turnOn();
   1281     }
   1282 
   1283     /**
   1284      * Called by the in-call UI to turn the proximity sensor off.
   1285      * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
   1286      *        the screen will be kept off until the proximity sensor goes negative.
   1287      */
   1288     void turnOffProximitySensor(boolean screenOnImmediately) {
   1289         mProximitySensorManager.turnOff(screenOnImmediately);
   1290     }
   1291 
   1292     void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) {
   1293         if (!mCalls.contains(call)) {
   1294             Log.i(this, "Attempted to add account to unknown call %s", call);
   1295         } else {
   1296             call.setTargetPhoneAccount(account);
   1297 
   1298             if (!call.isNewOutgoingCallIntentBroadcastDone()) {
   1299                 return;
   1300             }
   1301 
   1302             // Note: emergency calls never go through account selection dialog so they never
   1303             // arrive here.
   1304             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
   1305                 call.startCreateConnection(mPhoneAccountRegistrar);
   1306             } else {
   1307                 call.disconnect();
   1308             }
   1309 
   1310             if (setDefault) {
   1311                 mPhoneAccountRegistrar
   1312                         .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser());
   1313             }
   1314         }
   1315     }
   1316 
   1317     /** Called when the audio state changes. */
   1318     @VisibleForTesting
   1319     public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState
   1320             newAudioState) {
   1321         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
   1322         for (CallsManagerListener listener : mListeners) {
   1323             listener.onCallAudioStateChanged(oldAudioState, newAudioState);
   1324         }
   1325     }
   1326 
   1327     void markCallAsRinging(Call call) {
   1328         setCallState(call, CallState.RINGING, "ringing set explicitly");
   1329     }
   1330 
   1331     void markCallAsDialing(Call call) {
   1332         setCallState(call, CallState.DIALING, "dialing set explicitly");
   1333         maybeMoveToSpeakerPhone(call);
   1334     }
   1335 
   1336     void markCallAsActive(Call call) {
   1337         setCallState(call, CallState.ACTIVE, "active set explicitly");
   1338         maybeMoveToSpeakerPhone(call);
   1339     }
   1340 
   1341     void markCallAsOnHold(Call call) {
   1342         setCallState(call, CallState.ON_HOLD, "on-hold set explicitly");
   1343     }
   1344 
   1345     /**
   1346      * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
   1347      * last live call, then also disconnect from the in-call controller.
   1348      *
   1349      * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}.
   1350      */
   1351     void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
   1352         call.setDisconnectCause(disconnectCause);
   1353         setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly");
   1354     }
   1355 
   1356     /**
   1357      * Removes an existing disconnected call, and notifies the in-call app.
   1358      */
   1359     void markCallAsRemoved(Call call) {
   1360         removeCall(call);
   1361         if (mLocallyDisconnectingCalls.contains(call)) {
   1362             mLocallyDisconnectingCalls.remove(call);
   1363             Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall();
   1364             if (foregroundCall != null && foregroundCall.getState() == CallState.ON_HOLD) {
   1365                 foregroundCall.unhold();
   1366             }
   1367         }
   1368     }
   1369 
   1370     /**
   1371      * Cleans up any calls currently associated with the specified connection service when the
   1372      * service binder disconnects unexpectedly.
   1373      *
   1374      * @param service The connection service that disconnected.
   1375      */
   1376     void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
   1377         if (service != null) {
   1378             for (Call call : mCalls) {
   1379                 if (call.getConnectionService() == service) {
   1380                     if (call.getState() != CallState.DISCONNECTED) {
   1381                         markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
   1382                     }
   1383                     markCallAsRemoved(call);
   1384                 }
   1385             }
   1386         }
   1387     }
   1388 
   1389     /**
   1390      * Determines if the {@link CallsManager} has any non-external calls.
   1391      *
   1392      * @return {@code True} if there are any non-external calls, {@code false} otherwise.
   1393      */
   1394     boolean hasAnyCalls() {
   1395         if (mCalls.isEmpty()) {
   1396             return false;
   1397         }
   1398 
   1399         for (Call call : mCalls) {
   1400             if (!call.isExternalCall()) {
   1401                 return true;
   1402             }
   1403         }
   1404         return false;
   1405     }
   1406 
   1407     boolean hasActiveOrHoldingCall() {
   1408         return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
   1409     }
   1410 
   1411     boolean hasRingingCall() {
   1412         return getFirstCallWithState(CallState.RINGING) != null;
   1413     }
   1414 
   1415     boolean onMediaButton(int type) {
   1416         if (hasAnyCalls()) {
   1417             if (HeadsetMediaButton.SHORT_PRESS == type) {
   1418                 Call ringingCall = getFirstCallWithState(CallState.RINGING);
   1419                 if (ringingCall == null) {
   1420                     mCallAudioManager.toggleMute();
   1421                     return true;
   1422                 } else {
   1423                     ringingCall.answer(ringingCall.getVideoState());
   1424                     return true;
   1425                 }
   1426             } else if (HeadsetMediaButton.LONG_PRESS == type) {
   1427                 Log.d(this, "handleHeadsetHook: longpress -> hangup");
   1428                 Call callToHangup = getFirstCallWithState(
   1429                         CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
   1430                 if (callToHangup != null) {
   1431                     callToHangup.disconnect();
   1432                     return true;
   1433                 }
   1434             }
   1435         }
   1436         return false;
   1437     }
   1438 
   1439     /**
   1440      * Returns true if telecom supports adding another top-level call.
   1441      */
   1442     boolean canAddCall() {
   1443         boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
   1444                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
   1445         if (!isDeviceProvisioned) {
   1446             Log.d(TAG, "Device not provisioned, canAddCall is false.");
   1447             return false;
   1448         }
   1449 
   1450         if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) {
   1451             return false;
   1452         }
   1453 
   1454         int count = 0;
   1455         for (Call call : mCalls) {
   1456             if (call.isEmergencyCall()) {
   1457                 // We never support add call if one of the calls is an emergency call.
   1458                 return false;
   1459             } else if (call.getParentCall() == null) {
   1460                 count++;
   1461             }
   1462 
   1463             // We do not check states for canAddCall. We treat disconnected calls the same
   1464             // and wait until they are removed instead. If we didn't count disconnected calls,
   1465             // we could put InCallServices into a state where they are showing two calls but
   1466             // also support add-call. Technically it's right, but overall looks better (UI-wise)
   1467             // and acts better if we wait until the call is removed.
   1468             if (count >= MAXIMUM_TOP_LEVEL_CALLS) {
   1469                 return false;
   1470             }
   1471         }
   1472         return true;
   1473     }
   1474 
   1475     @VisibleForTesting
   1476     public Call getRingingCall() {
   1477         return getFirstCallWithState(CallState.RINGING);
   1478     }
   1479 
   1480     @VisibleForTesting
   1481     public Call getActiveCall() {
   1482         return getFirstCallWithState(CallState.ACTIVE);
   1483     }
   1484 
   1485     Call getDialingCall() {
   1486         return getFirstCallWithState(CallState.DIALING);
   1487     }
   1488 
   1489     @VisibleForTesting
   1490     public Call getHeldCall() {
   1491         return getFirstCallWithState(CallState.ON_HOLD);
   1492     }
   1493 
   1494     @VisibleForTesting
   1495     public int getNumHeldCalls() {
   1496         int count = 0;
   1497         for (Call call : mCalls) {
   1498             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
   1499                 count++;
   1500             }
   1501         }
   1502         return count;
   1503     }
   1504 
   1505     @VisibleForTesting
   1506     public Call getOutgoingCall() {
   1507         return getFirstCallWithState(OUTGOING_CALL_STATES);
   1508     }
   1509 
   1510     @VisibleForTesting
   1511     public Call getFirstCallWithState(int... states) {
   1512         return getFirstCallWithState(null, states);
   1513     }
   1514 
   1515     /**
   1516      * Returns the first call that it finds with the given states. The states are treated as having
   1517      * priority order so that any call with the first state will be returned before any call with
   1518      * states listed later in the parameter list.
   1519      *
   1520      * @param callToSkip Call that this method should skip while searching
   1521      */
   1522     Call getFirstCallWithState(Call callToSkip, int... states) {
   1523         for (int currentState : states) {
   1524             // check the foreground first
   1525             Call foregroundCall = getForegroundCall();
   1526             if (foregroundCall != null && foregroundCall.getState() == currentState) {
   1527                 return foregroundCall;
   1528             }
   1529 
   1530             for (Call call : mCalls) {
   1531                 if (Objects.equals(callToSkip, call)) {
   1532                     continue;
   1533                 }
   1534 
   1535                 // Only operate on top-level calls
   1536                 if (call.getParentCall() != null) {
   1537                     continue;
   1538                 }
   1539 
   1540                 if (currentState == call.getState()) {
   1541                     return call;
   1542                 }
   1543             }
   1544         }
   1545         return null;
   1546     }
   1547 
   1548     Call createConferenceCall(
   1549             String callId,
   1550             PhoneAccountHandle phoneAccount,
   1551             ParcelableConference parcelableConference) {
   1552 
   1553         // If the parceled conference specifies a connect time, use it; otherwise default to 0,
   1554         // which is the default value for new Calls.
   1555         long connectTime =
   1556                 parcelableConference.getConnectTimeMillis() ==
   1557                         Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 :
   1558                         parcelableConference.getConnectTimeMillis();
   1559 
   1560         Call call = new Call(
   1561                 callId,
   1562                 mContext,
   1563                 this,
   1564                 mLock,
   1565                 mConnectionServiceRepository,
   1566                 mContactsAsyncHelper,
   1567                 mCallerInfoAsyncQueryFactory,
   1568                 null /* handle */,
   1569                 null /* gatewayInfo */,
   1570                 null /* connectionManagerPhoneAccount */,
   1571                 phoneAccount,
   1572                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
   1573                 false /* forceAttachToExistingConnection */,
   1574                 true /* isConference */,
   1575                 connectTime);
   1576 
   1577         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()),
   1578                 "new conference call");
   1579         call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities());
   1580         call.setConnectionProperties(parcelableConference.getConnectionProperties());
   1581         call.setVideoState(parcelableConference.getVideoState());
   1582         call.setVideoProvider(parcelableConference.getVideoProvider());
   1583         call.setStatusHints(parcelableConference.getStatusHints());
   1584         call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras());
   1585 
   1586         // TODO: Move this to be a part of addCall()
   1587         call.addListener(this);
   1588         addCall(call);
   1589         return call;
   1590     }
   1591 
   1592     /**
   1593      * @return the call state currently tracked by {@link PhoneStateBroadcaster}
   1594      */
   1595     int getCallState() {
   1596         return mPhoneStateBroadcaster.getCallState();
   1597     }
   1598 
   1599     /**
   1600      * Retrieves the {@link PhoneAccountRegistrar}.
   1601      *
   1602      * @return The {@link PhoneAccountRegistrar}.
   1603      */
   1604     PhoneAccountRegistrar getPhoneAccountRegistrar() {
   1605         return mPhoneAccountRegistrar;
   1606     }
   1607 
   1608     /**
   1609      * Retrieves the {@link MissedCallNotifier}
   1610      * @return The {@link MissedCallNotifier}.
   1611      */
   1612     MissedCallNotifier getMissedCallNotifier() {
   1613         return mMissedCallNotifier;
   1614     }
   1615 
   1616     /**
   1617      * Reject an incoming call and manually add it to the Call Log.
   1618      * @param incomingCall Incoming call that has been rejected
   1619      */
   1620     private void rejectCallAndLog(Call incomingCall) {
   1621         incomingCall.reject(false, null);
   1622         // Since the call was not added to the list of calls, we have to call the missed
   1623         // call notifier and the call logger manually.
   1624         // Do we need missed call notification for direct to Voicemail calls?
   1625         mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE,
   1626                 true /*showNotificationForMissedCall*/);
   1627     }
   1628 
   1629     /**
   1630      * Adds the specified call to the main list of live calls.
   1631      *
   1632      * @param call The call to add.
   1633      */
   1634     private void addCall(Call call) {
   1635         Trace.beginSection("addCall");
   1636         Log.v(this, "addCall(%s)", call);
   1637         call.addListener(this);
   1638         mCalls.add(call);
   1639 
   1640         // Specifies the time telecom finished routing the call. This is used by the dialer for
   1641         // analytics.
   1642         Bundle extras = call.getIntentExtras();
   1643         extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS,
   1644                 SystemClock.elapsedRealtime());
   1645 
   1646         updateCallsManagerState();
   1647         // onCallAdded for calls which immediately take the foreground (like the first call).
   1648         for (CallsManagerListener listener : mListeners) {
   1649             if (Log.SYSTRACE_DEBUG) {
   1650                 Trace.beginSection(listener.getClass().toString() + " addCall");
   1651             }
   1652             listener.onCallAdded(call);
   1653             if (Log.SYSTRACE_DEBUG) {
   1654                 Trace.endSection();
   1655             }
   1656         }
   1657         Trace.endSection();
   1658     }
   1659 
   1660     private void removeCall(Call call) {
   1661         Trace.beginSection("removeCall");
   1662         Log.v(this, "removeCall(%s)", call);
   1663 
   1664         call.setParentCall(null);  // need to clean up parent relationship before destroying.
   1665         call.removeListener(this);
   1666         call.clearConnectionService();
   1667 
   1668         boolean shouldNotify = false;
   1669         if (mCalls.contains(call)) {
   1670             mCalls.remove(call);
   1671             shouldNotify = true;
   1672         }
   1673 
   1674         call.destroy();
   1675 
   1676         // Only broadcast changes for calls that are being tracked.
   1677         if (shouldNotify) {
   1678             updateCallsManagerState();
   1679             for (CallsManagerListener listener : mListeners) {
   1680                 if (Log.SYSTRACE_DEBUG) {
   1681                     Trace.beginSection(listener.getClass().toString() + " onCallRemoved");
   1682                 }
   1683                 listener.onCallRemoved(call);
   1684                 if (Log.SYSTRACE_DEBUG) {
   1685                     Trace.endSection();
   1686                 }
   1687             }
   1688         }
   1689         Trace.endSection();
   1690     }
   1691 
   1692     /**
   1693      * Sets the specified state on the specified call.
   1694      *
   1695      * @param call The call.
   1696      * @param newState The new state of the call.
   1697      */
   1698     private void setCallState(Call call, int newState, String tag) {
   1699         if (call == null) {
   1700             return;
   1701         }
   1702         int oldState = call.getState();
   1703         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
   1704                 CallState.toString(newState), call);
   1705         if (newState != oldState) {
   1706             // Unfortunately, in the telephony world the radio is king. So if the call notifies
   1707             // us that the call is in a particular state, we allow it even if it doesn't make
   1708             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
   1709             // TODO: Consider putting a stop to the above and turning CallState
   1710             // into a well-defined state machine.
   1711             // TODO: Define expected state transitions here, and log when an
   1712             // unexpected transition occurs.
   1713             call.setState(newState, tag);
   1714             maybeShowErrorDialogOnDisconnect(call);
   1715 
   1716             Trace.beginSection("onCallStateChanged");
   1717             // Only broadcast state change for calls that are being tracked.
   1718             if (mCalls.contains(call)) {
   1719                 updateCallsManagerState();
   1720                 for (CallsManagerListener listener : mListeners) {
   1721                     if (Log.SYSTRACE_DEBUG) {
   1722                         Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
   1723                     }
   1724                     listener.onCallStateChanged(call, oldState, newState);
   1725                     if (Log.SYSTRACE_DEBUG) {
   1726                         Trace.endSection();
   1727                     }
   1728                 }
   1729             }
   1730             Trace.endSection();
   1731         }
   1732     }
   1733 
   1734     private void updateCanAddCall() {
   1735         boolean newCanAddCall = canAddCall();
   1736         if (newCanAddCall != mCanAddCall) {
   1737             mCanAddCall = newCanAddCall;
   1738             for (CallsManagerListener listener : mListeners) {
   1739                 if (Log.SYSTRACE_DEBUG) {
   1740                     Trace.beginSection(listener.getClass().toString() + " updateCanAddCall");
   1741                 }
   1742                 listener.onCanAddCallChanged(mCanAddCall);
   1743                 if (Log.SYSTRACE_DEBUG) {
   1744                     Trace.endSection();
   1745                 }
   1746             }
   1747         }
   1748     }
   1749 
   1750     private void updateCallsManagerState() {
   1751         updateCanAddCall();
   1752     }
   1753 
   1754     private boolean isPotentialMMICode(Uri handle) {
   1755         return (handle != null && handle.getSchemeSpecificPart() != null
   1756                 && handle.getSchemeSpecificPart().contains("#"));
   1757     }
   1758 
   1759     /**
   1760      * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
   1761      * MMI codes which can be dialed when one or more calls are in progress.
   1762      * <P>
   1763      * Checks for numbers formatted similar to the MMI codes defined in:
   1764      * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)}
   1765      *
   1766      * @param handle The URI to call.
   1767      * @return {@code True} if the URI represents a number which could be an in-call MMI code.
   1768      */
   1769     private boolean isPotentialInCallMMICode(Uri handle) {
   1770         if (handle != null && handle.getSchemeSpecificPart() != null &&
   1771                 handle.getScheme() != null &&
   1772                 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
   1773 
   1774             String dialedNumber = handle.getSchemeSpecificPart();
   1775             return (dialedNumber.equals("0") ||
   1776                     (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
   1777                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
   1778                     dialedNumber.equals("3") ||
   1779                     dialedNumber.equals("4") ||
   1780                     dialedNumber.equals("5"));
   1781         }
   1782         return false;
   1783     }
   1784 
   1785     private int getNumCallsWithState(int... states) {
   1786         int count = 0;
   1787         for (int state : states) {
   1788             for (Call call : mCalls) {
   1789                 if (call.getParentCall() == null && call.getState() == state) {
   1790                     count++;
   1791                 }
   1792             }
   1793         }
   1794         return count;
   1795     }
   1796 
   1797     private boolean hasMaximumLiveCalls() {
   1798         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
   1799     }
   1800 
   1801     private boolean hasMaximumHoldingCalls() {
   1802         return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
   1803     }
   1804 
   1805     private boolean hasMaximumRingingCalls() {
   1806         return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
   1807     }
   1808 
   1809     private boolean hasMaximumOutgoingCalls() {
   1810         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
   1811     }
   1812 
   1813     private boolean hasMaximumDialingCalls() {
   1814         return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(CallState.DIALING);
   1815     }
   1816 
   1817     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
   1818         if (hasMaximumLiveCalls()) {
   1819             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
   1820             // have to change.
   1821             Call liveCall = getFirstCallWithState(LIVE_CALL_STATES);
   1822             Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " +
   1823                    liveCall);
   1824 
   1825             if (call == liveCall) {
   1826                 // If the call is already the foreground call, then we are golden.
   1827                 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT
   1828                 // state since the call was already populated into the list.
   1829                 return true;
   1830             }
   1831 
   1832             if (hasMaximumOutgoingCalls()) {
   1833                 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
   1834                 if (isEmergency && !outgoingCall.isEmergencyCall()) {
   1835                     // Disconnect the current outgoing call if it's not an emergency call. If the
   1836                     // user tries to make two outgoing calls to different emergency call numbers,
   1837                     // we will try to connect the first outgoing call.
   1838                     call.getAnalytics().setCallIsAdditional(true);
   1839                     outgoingCall.getAnalytics().setCallIsInterrupted(true);
   1840                     outgoingCall.disconnect();
   1841                     return true;
   1842                 }
   1843                 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) {
   1844                     // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT}
   1845                     // state, just disconnect it since the user has explicitly started a new call.
   1846                     call.getAnalytics().setCallIsAdditional(true);
   1847                     outgoingCall.getAnalytics().setCallIsInterrupted(true);
   1848                     outgoingCall.disconnect();
   1849                     return true;
   1850                 }
   1851                 return false;
   1852             }
   1853 
   1854             if (hasMaximumHoldingCalls()) {
   1855                 // There is no more room for any more calls, unless it's an emergency.
   1856                 if (isEmergency) {
   1857                     // Kill the current active call, this is easier then trying to disconnect a
   1858                     // holding call and hold an active call.
   1859                     call.getAnalytics().setCallIsAdditional(true);
   1860                     liveCall.getAnalytics().setCallIsInterrupted(true);
   1861                     liveCall.disconnect();
   1862                     return true;
   1863                 }
   1864                 return false;  // No more room!
   1865             }
   1866 
   1867             // We have room for at least one more holding call at this point.
   1868 
   1869             // TODO: Remove once b/23035408 has been corrected.
   1870             // If the live call is a conference, it will not have a target phone account set.  This
   1871             // means the check to see if the live call has the same target phone account as the new
   1872             // call will not cause us to bail early.  As a result, we'll end up holding the
   1873             // ongoing conference call.  However, the ConnectionService is already doing that.  This
   1874             // has caused problems with some carriers.  As a workaround until b/23035408 is
   1875             // corrected, we will try and get the target phone account for one of the conference's
   1876             // children and use that instead.
   1877             PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount();
   1878             if (liveCallPhoneAccount == null && liveCall.isConference() &&
   1879                     !liveCall.getChildCalls().isEmpty()) {
   1880                 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall);
   1881                 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " +
   1882                         liveCallPhoneAccount);
   1883             }
   1884 
   1885             // First thing, if we are trying to make a call with the same phone account as the live
   1886             // call, then allow it so that the connection service can make its own decision about
   1887             // how to handle the new call relative to the current one.
   1888             if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) {
   1889                 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches.");
   1890                 call.getAnalytics().setCallIsAdditional(true);
   1891                 liveCall.getAnalytics().setCallIsInterrupted(true);
   1892                 return true;
   1893             } else if (call.getTargetPhoneAccount() == null) {
   1894                 // Without a phone account, we can't say reliably that the call will fail.
   1895                 // If the user chooses the same phone account as the live call, then it's
   1896                 // still possible that the call can be made (like with CDMA calls not supporting
   1897                 // hold but they still support adding a call by going immediately into conference
   1898                 // mode). Return true here and we'll run this code again after user chooses an
   1899                 // account.
   1900                 return true;
   1901             }
   1902 
   1903             // Try to hold the live call before attempting the new outgoing call.
   1904             if (liveCall.can(Connection.CAPABILITY_HOLD)) {
   1905                 Log.i(this, "makeRoomForOutgoingCall: holding live call.");
   1906                 call.getAnalytics().setCallIsAdditional(true);
   1907                 liveCall.getAnalytics().setCallIsInterrupted(true);
   1908                 liveCall.hold();
   1909                 return true;
   1910             }
   1911 
   1912             // The live call cannot be held so we're out of luck here.  There's no room.
   1913             return false;
   1914         }
   1915         return true;
   1916     }
   1917 
   1918     /**
   1919      * Given a call, find the first non-null phone account handle of its children.
   1920      *
   1921      * @param parentCall The parent call.
   1922      * @return The first non-null phone account handle of the children, or {@code null} if none.
   1923      */
   1924     private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) {
   1925         for (Call childCall : parentCall.getChildCalls()) {
   1926             PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount();
   1927             if (childPhoneAccount != null) {
   1928                 return childPhoneAccount;
   1929             }
   1930         }
   1931         return null;
   1932     }
   1933 
   1934     /**
   1935      * Checks to see if the call should be on speakerphone and if so, set it.
   1936      */
   1937     private void maybeMoveToSpeakerPhone(Call call) {
   1938         if (call.getStartWithSpeakerphoneOn()) {
   1939             setAudioRoute(CallAudioState.ROUTE_SPEAKER);
   1940             call.setStartWithSpeakerphoneOn(false);
   1941         }
   1942     }
   1943 
   1944     /**
   1945      * Creates a new call for an existing connection.
   1946      *
   1947      * @param callId The id of the new call.
   1948      * @param connection The connection information.
   1949      * @return The new call.
   1950      */
   1951     Call createCallForExistingConnection(String callId, ParcelableConnection connection) {
   1952         Call call = new Call(
   1953                 callId,
   1954                 mContext,
   1955                 this,
   1956                 mLock,
   1957                 mConnectionServiceRepository,
   1958                 mContactsAsyncHelper,
   1959                 mCallerInfoAsyncQueryFactory,
   1960                 connection.getHandle() /* handle */,
   1961                 null /* gatewayInfo */,
   1962                 null /* connectionManagerPhoneAccount */,
   1963                 connection.getPhoneAccount(), /* targetPhoneAccountHandle */
   1964                 Call.CALL_DIRECTION_UNDEFINED /* callDirection */,
   1965                 false /* forceAttachToExistingConnection */,
   1966                 false /* isConference */,
   1967                 connection.getConnectTimeMillis() /* connectTimeMillis */);
   1968 
   1969         call.initAnalytics();
   1970         call.getAnalytics().setCreatedFromExistingConnection(true);
   1971 
   1972         setCallState(call, Call.getStateFromConnectionState(connection.getState()),
   1973                 "existing connection");
   1974         call.setConnectionCapabilities(connection.getConnectionCapabilities());
   1975         call.setConnectionProperties(connection.getConnectionProperties());
   1976         call.setCallerDisplayName(connection.getCallerDisplayName(),
   1977                 connection.getCallerDisplayNamePresentation());
   1978 
   1979         call.addListener(this);
   1980         addCall(call);
   1981 
   1982         return call;
   1983     }
   1984 
   1985     /**
   1986      * @return A new unique telecom call Id.
   1987      */
   1988     private String getNextCallId() {
   1989         synchronized(mLock) {
   1990             return TELECOM_CALL_ID_PREFIX + (++mCallId);
   1991         }
   1992     }
   1993 
   1994     /**
   1995      * Callback when foreground user is switched. We will reload missed call in all profiles
   1996      * including the user itself. There may be chances that profiles are not started yet.
   1997      */
   1998     void onUserSwitch(UserHandle userHandle) {
   1999         mCurrentUserHandle = userHandle;
   2000         mMissedCallNotifier.setCurrentUserHandle(userHandle);
   2001         final UserManager userManager = UserManager.get(mContext);
   2002         List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier());
   2003         for (UserInfo profile : profiles) {
   2004             reloadMissedCallsOfUser(profile.getUserHandle());
   2005         }
   2006     }
   2007 
   2008     /**
   2009      * Because there may be chances that profiles are not started yet though its parent user is
   2010      * switched, we reload missed calls of profile that are just started here.
   2011      */
   2012     void onUserStarting(UserHandle userHandle) {
   2013         if (UserUtil.isProfile(mContext, userHandle)) {
   2014             reloadMissedCallsOfUser(userHandle);
   2015         }
   2016     }
   2017 
   2018     private void reloadMissedCallsOfUser(UserHandle userHandle) {
   2019         mMissedCallNotifier.reloadFromDatabase(
   2020                 mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, userHandle);
   2021     }
   2022 
   2023     /**
   2024      * Dumps the state of the {@link CallsManager}.
   2025      *
   2026      * @param pw The {@code IndentingPrintWriter} to write the state to.
   2027      */
   2028     public void dump(IndentingPrintWriter pw) {
   2029         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   2030         if (mCalls != null) {
   2031             pw.println("mCalls: ");
   2032             pw.increaseIndent();
   2033             for (Call call : mCalls) {
   2034                 pw.println(call);
   2035             }
   2036             pw.decreaseIndent();
   2037         }
   2038 
   2039         if (mCallAudioManager != null) {
   2040             pw.println("mCallAudioManager:");
   2041             pw.increaseIndent();
   2042             mCallAudioManager.dump(pw);
   2043             pw.decreaseIndent();
   2044         }
   2045 
   2046         if (mTtyManager != null) {
   2047             pw.println("mTtyManager:");
   2048             pw.increaseIndent();
   2049             mTtyManager.dump(pw);
   2050             pw.decreaseIndent();
   2051         }
   2052 
   2053         if (mInCallController != null) {
   2054             pw.println("mInCallController:");
   2055             pw.increaseIndent();
   2056             mInCallController.dump(pw);
   2057             pw.decreaseIndent();
   2058         }
   2059 
   2060         if (mConnectionServiceRepository != null) {
   2061             pw.println("mConnectionServiceRepository:");
   2062             pw.increaseIndent();
   2063             mConnectionServiceRepository.dump(pw);
   2064             pw.decreaseIndent();
   2065         }
   2066     }
   2067 
   2068     /**
   2069     * For some disconnected causes, we show a dialog when it's a mmi code or potential mmi code.
   2070     *
   2071     * @param call The call.
   2072     */
   2073     private void maybeShowErrorDialogOnDisconnect(Call call) {
   2074         if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle())
   2075                 || isPotentialInCallMMICode(call.getHandle()))) {
   2076             DisconnectCause disconnectCause = call.getDisconnectCause();
   2077             if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode()
   2078                     == DisconnectCause.ERROR)) {
   2079                 Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class);
   2080                 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_STRING_EXTRA,
   2081                         disconnectCause.getDescription());
   2082                 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   2083                 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT);
   2084             }
   2085         }
   2086     }
   2087 
   2088     private void setIntentExtrasAndStartTime(Call call, Bundle extras) {
   2089       // Create our own instance to modify (since extras may be Bundle.EMPTY)
   2090       extras = new Bundle(extras);
   2091 
   2092       // Specifies the time telecom began routing the call. This is used by the dialer for
   2093       // analytics.
   2094       extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS,
   2095               SystemClock.elapsedRealtime());
   2096 
   2097       call.setIntentExtras(extras);
   2098     }
   2099 }
   2100