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