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