Home | History | Annotate | Download | only in telecom
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.telecom;
     18 
     19 import android.content.Context;
     20 import android.content.Intent;
     21 import android.content.pm.PackageManager;
     22 import android.content.pm.ResolveInfo;
     23 import android.net.Uri;
     24 import android.os.Bundle;
     25 import android.provider.CallLog.Calls;
     26 import android.telecom.AudioState;
     27 import android.telecom.CallState;
     28 import android.telecom.DisconnectCause;
     29 import android.telecom.GatewayInfo;
     30 import android.telecom.ParcelableConference;
     31 import android.telecom.PhoneAccount;
     32 import android.telecom.PhoneAccountHandle;
     33 import android.telecom.PhoneCapabilities;
     34 import android.telecom.TelecomManager;
     35 import android.telephony.TelephonyManager;
     36 
     37 import com.android.internal.util.IndentingPrintWriter;
     38 
     39 import com.google.common.collect.ImmutableCollection;
     40 import com.google.common.collect.ImmutableList;
     41 
     42 import java.util.Collections;
     43 import java.util.HashSet;
     44 import java.util.List;
     45 import java.util.Objects;
     46 import java.util.Set;
     47 import java.util.concurrent.ConcurrentHashMap;
     48 
     49 /**
     50  * Singleton.
     51  *
     52  * NOTE: by design most APIs are package private, use the relevant adapter/s to allow
     53  * access from other packages specifically refraining from passing the CallsManager instance
     54  * beyond the com.android.server.telecom package boundary.
     55  */
     56 public final class CallsManager extends Call.ListenerBase {
     57 
     58     // TODO: Consider renaming this CallsManagerPlugin.
     59     interface CallsManagerListener {
     60         void onCallAdded(Call call);
     61         void onCallRemoved(Call call);
     62         void onCallStateChanged(Call call, int oldState, int newState);
     63         void onConnectionServiceChanged(
     64                 Call call,
     65                 ConnectionServiceWrapper oldService,
     66                 ConnectionServiceWrapper newService);
     67         void onIncomingCallAnswered(Call call);
     68         void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage);
     69         void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall);
     70         void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState);
     71         void onRingbackRequested(Call call, boolean ringback);
     72         void onIsConferencedChanged(Call call);
     73         void onIsVoipAudioModeChanged(Call call);
     74         void onVideoStateChanged(Call call);
     75     }
     76 
     77     /**
     78      * Singleton instance of the {@link CallsManager}, initialized from {@link TelecomService}.
     79      */
     80     private static CallsManager INSTANCE = null;
     81 
     82     private static final String TAG = "CallsManager";
     83 
     84     private static final int MAXIMUM_LIVE_CALLS = 1;
     85     private static final int MAXIMUM_HOLD_CALLS = 1;
     86     private static final int MAXIMUM_RINGING_CALLS = 1;
     87     private static final int MAXIMUM_OUTGOING_CALLS = 1;
     88 
     89     private static final int[] LIVE_CALL_STATES =
     90             {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE};
     91 
     92     private static final int[] OUTGOING_CALL_STATES =
     93             {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING};
     94 
     95     /**
     96      * The main call repository. Keeps an instance of all live calls. New incoming and outgoing
     97      * calls are added to the map and removed when the calls move to the disconnected state.
     98     *
     99      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
    100      * load factor before resizing, 1 means we only expect a single thread to
    101      * access the map so make only a single shard
    102      */
    103     private final Set<Call> mCalls = Collections.newSetFromMap(
    104             new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1));
    105 
    106     private final ConnectionServiceRepository mConnectionServiceRepository;
    107     private final DtmfLocalTonePlayer mDtmfLocalTonePlayer;
    108     private final InCallController mInCallController;
    109     private final CallAudioManager mCallAudioManager;
    110     private final Ringer mRinger;
    111     // For this set initial table size to 16 because we add 13 listeners in
    112     // the CallsManager constructor.
    113     private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(
    114             new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));
    115     private final HeadsetMediaButton mHeadsetMediaButton;
    116     private final WiredHeadsetManager mWiredHeadsetManager;
    117     private final TtyManager mTtyManager;
    118     private final ProximitySensorManager mProximitySensorManager;
    119     private final PhoneStateBroadcaster mPhoneStateBroadcaster;
    120     private final CallLogManager mCallLogManager;
    121     private final Context mContext;
    122     private final PhoneAccountRegistrar mPhoneAccountRegistrar;
    123     private final MissedCallNotifier mMissedCallNotifier;
    124     private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>();
    125 
    126     /**
    127      * The call the user is currently interacting with. This is the call that should have audio
    128      * focus and be visible in the in-call UI.
    129      */
    130     private Call mForegroundCall;
    131 
    132     /** Singleton accessor. */
    133     static CallsManager getInstance() {
    134         return INSTANCE;
    135     }
    136 
    137     /**
    138      * Sets the static singleton instance.
    139      *
    140      * @param instance The instance to set.
    141      */
    142     static void initialize(CallsManager instance) {
    143         INSTANCE = instance;
    144     }
    145 
    146     /**
    147      * Initializes the required Telecom components.
    148      */
    149      CallsManager(Context context, MissedCallNotifier missedCallNotifier,
    150              PhoneAccountRegistrar phoneAccountRegistrar) {
    151         mContext = context;
    152         mPhoneAccountRegistrar = phoneAccountRegistrar;
    153         mMissedCallNotifier = missedCallNotifier;
    154         StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this);
    155         mWiredHeadsetManager = new WiredHeadsetManager(context);
    156         mCallAudioManager = new CallAudioManager(context, statusBarNotifier, mWiredHeadsetManager);
    157         InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager);
    158         mRinger = new Ringer(mCallAudioManager, this, playerFactory, context);
    159         mHeadsetMediaButton = new HeadsetMediaButton(context, this);
    160         mTtyManager = new TtyManager(context, mWiredHeadsetManager);
    161         mProximitySensorManager = new ProximitySensorManager(context);
    162         mPhoneStateBroadcaster = new PhoneStateBroadcaster();
    163         mCallLogManager = new CallLogManager(context);
    164         mInCallController = new InCallController(context);
    165         mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context);
    166         mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar,
    167                 context);
    168 
    169         mListeners.add(statusBarNotifier);
    170         mListeners.add(mCallLogManager);
    171         mListeners.add(mPhoneStateBroadcaster);
    172         mListeners.add(mInCallController);
    173         mListeners.add(mRinger);
    174         mListeners.add(new RingbackPlayer(this, playerFactory));
    175         mListeners.add(new InCallToneMonitor(playerFactory, this));
    176         mListeners.add(mCallAudioManager);
    177         mListeners.add(missedCallNotifier);
    178         mListeners.add(mDtmfLocalTonePlayer);
    179         mListeners.add(mHeadsetMediaButton);
    180         mListeners.add(RespondViaSmsManager.getInstance());
    181         mListeners.add(mProximitySensorManager);
    182     }
    183 
    184     @Override
    185     public void onSuccessfulOutgoingCall(Call call, int callState) {
    186         Log.v(this, "onSuccessfulOutgoingCall, %s", call);
    187 
    188         setCallState(call, callState);
    189         if (!mCalls.contains(call)) {
    190             // Call was not added previously in startOutgoingCall due to it being a potential MMI
    191             // code, so add it now.
    192             addCall(call);
    193         }
    194 
    195         // The call's ConnectionService has been updated.
    196         for (CallsManagerListener listener : mListeners) {
    197             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
    198         }
    199 
    200         markCallAsDialing(call);
    201     }
    202 
    203     @Override
    204     public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {
    205         Log.v(this, "onFailedOutgoingCall, call: %s", call);
    206 
    207         markCallAsRemoved(call);
    208     }
    209 
    210     @Override
    211     public void onSuccessfulIncomingCall(Call incomingCall) {
    212         Log.d(this, "onSuccessfulIncomingCall");
    213         setCallState(incomingCall, CallState.RINGING);
    214 
    215         if (hasMaximumRingingCalls()) {
    216             incomingCall.reject(false, null);
    217             // since the call was not added to the list of calls, we have to call the missed
    218             // call notifier and the call logger manually.
    219             mMissedCallNotifier.showMissedCallNotification(incomingCall);
    220             mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE);
    221         } else {
    222             addCall(incomingCall);
    223         }
    224     }
    225 
    226     @Override
    227     public void onFailedIncomingCall(Call call) {
    228         setCallState(call, CallState.DISCONNECTED);
    229         call.removeListener(this);
    230     }
    231 
    232     @Override
    233     public void onSuccessfulUnknownCall(Call call, int callState) {
    234         setCallState(call, callState);
    235         Log.i(this, "onSuccessfulUnknownCall for call %s", call);
    236         addCall(call);
    237     }
    238 
    239     @Override
    240     public void onFailedUnknownCall(Call call) {
    241         Log.i(this, "onFailedUnknownCall for call %s", call);
    242         setCallState(call, CallState.DISCONNECTED);
    243         call.removeListener(this);
    244     }
    245 
    246     @Override
    247     public void onRingbackRequested(Call call, boolean ringback) {
    248         for (CallsManagerListener listener : mListeners) {
    249             listener.onRingbackRequested(call, ringback);
    250         }
    251     }
    252 
    253     @Override
    254     public void onPostDialWait(Call call, String remaining) {
    255         mInCallController.onPostDialWait(call, remaining);
    256     }
    257 
    258     @Override
    259     public void onParentChanged(Call call) {
    260         // parent-child relationship affects which call should be foreground, so do an update.
    261         updateForegroundCall();
    262         for (CallsManagerListener listener : mListeners) {
    263             listener.onIsConferencedChanged(call);
    264         }
    265     }
    266 
    267     @Override
    268     public void onChildrenChanged(Call call) {
    269         // parent-child relationship affects which call should be foreground, so do an update.
    270         updateForegroundCall();
    271         for (CallsManagerListener listener : mListeners) {
    272             listener.onIsConferencedChanged(call);
    273         }
    274     }
    275 
    276     @Override
    277     public void onIsVoipAudioModeChanged(Call call) {
    278         for (CallsManagerListener listener : mListeners) {
    279             listener.onIsVoipAudioModeChanged(call);
    280         }
    281     }
    282 
    283     @Override
    284     public void onVideoStateChanged(Call call) {
    285         for (CallsManagerListener listener : mListeners) {
    286             listener.onVideoStateChanged(call);
    287         }
    288     }
    289 
    290     ImmutableCollection<Call> getCalls() {
    291         return ImmutableList.copyOf(mCalls);
    292     }
    293 
    294     Call getForegroundCall() {
    295         return mForegroundCall;
    296     }
    297 
    298     Ringer getRinger() {
    299         return mRinger;
    300     }
    301 
    302     InCallController getInCallController() {
    303         return mInCallController;
    304     }
    305 
    306     boolean hasEmergencyCall() {
    307         for (Call call : mCalls) {
    308             if (call.isEmergencyCall()) {
    309                 return true;
    310             }
    311         }
    312         return false;
    313     }
    314 
    315     AudioState getAudioState() {
    316         return mCallAudioManager.getAudioState();
    317     }
    318 
    319     boolean isTtySupported() {
    320         return mTtyManager.isTtySupported();
    321     }
    322 
    323     int getCurrentTtyMode() {
    324         return mTtyManager.getCurrentTtyMode();
    325     }
    326 
    327     void addListener(CallsManagerListener listener) {
    328         mListeners.add(listener);
    329     }
    330 
    331     void removeListener(CallsManagerListener listener) {
    332         mListeners.remove(listener);
    333     }
    334 
    335     /**
    336      * Starts the process to attach the call to a connection service.
    337      *
    338      * @param phoneAccountHandle The phone account which contains the component name of the
    339      *        connection service to use for this call.
    340      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    341      */
    342     void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    343         Log.d(this, "processIncomingCallIntent");
    344         Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
    345         Call call = new Call(
    346                 mContext,
    347                 mConnectionServiceRepository,
    348                 handle,
    349                 null /* gatewayInfo */,
    350                 null /* connectionManagerPhoneAccount */,
    351                 phoneAccountHandle,
    352                 true /* isIncoming */,
    353                 false /* isConference */);
    354 
    355         call.setExtras(extras);
    356         // TODO: Move this to be a part of addCall()
    357         call.addListener(this);
    358         call.startCreateConnection(mPhoneAccountRegistrar);
    359     }
    360 
    361     void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    362         Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE);
    363         Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle));
    364         Call call = new Call(
    365                 mContext,
    366                 mConnectionServiceRepository,
    367                 handle,
    368                 null /* gatewayInfo */,
    369                 null /* connectionManagerPhoneAccount */,
    370                 phoneAccountHandle,
    371                 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach
    372                 // to the existing connection instead of trying to create a new one.
    373                 true /* isIncoming */,
    374                 false /* isConference */);
    375         call.setConnectTimeMillis(System.currentTimeMillis());
    376         call.setIsUnknown(true);
    377         call.setExtras(extras);
    378         call.addListener(this);
    379         call.startCreateConnection(mPhoneAccountRegistrar);
    380     }
    381 
    382     /**
    383      * Kicks off the first steps to creating an outgoing call so that InCallUI can launch.
    384      *
    385      * @param handle Handle to connect the call with.
    386      * @param phoneAccountHandle The phone account which contains the component name of the
    387      *        connection service to use for this call.
    388      * @param extras The optional extras Bundle passed with the intent used for the incoming call.
    389      */
    390     Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    391         // Create a call with original handle. The handle may be changed when the call is attached
    392         // to a connection service, but in most cases will remain the same.
    393         Call call = new Call(
    394                 mContext,
    395                 mConnectionServiceRepository,
    396                 handle,
    397                 null /* gatewayInfo */,
    398                 null /* connectionManagerPhoneAccount */,
    399                 null /* phoneAccountHandle */,
    400                 false /* isIncoming */,
    401                 false /* isConference */);
    402 
    403         List<PhoneAccountHandle> accounts =
    404                 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme());
    405 
    406         // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call
    407         // as if a phoneAccount was not specified (does the default behavior instead).
    408         // Note: We will not attempt to dial with a requested phoneAccount if it is disabled.
    409         if (phoneAccountHandle != null) {
    410             if (!accounts.contains(phoneAccountHandle)) {
    411                 phoneAccountHandle = null;
    412             }
    413         }
    414 
    415         if (phoneAccountHandle == null) {
    416             // No preset account, check if default exists that supports the URI scheme for the
    417             // handle.
    418             PhoneAccountHandle defaultAccountHandle =
    419                     mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount(
    420                             handle.getScheme());
    421             if (defaultAccountHandle != null) {
    422                 phoneAccountHandle = defaultAccountHandle;
    423             }
    424         }
    425 
    426         call.setTargetPhoneAccount(phoneAccountHandle);
    427 
    428         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
    429                 call.getHandle());
    430         boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle);
    431 
    432         // Do not support any more live calls.  Our options are to move a call to hold, disconnect
    433         // a call, or cancel this call altogether.
    434         if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) {
    435             // just cancel at this point.
    436             return null;
    437         }
    438 
    439         if (phoneAccountHandle == null && accounts.size() > 1 && !isEmergencyCall) {
    440             // This is the state where the user is expected to select an account
    441             call.setState(CallState.PRE_DIAL_WAIT);
    442             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
    443         } else {
    444             call.setState(CallState.CONNECTING);
    445         }
    446 
    447         call.setExtras(extras);
    448 
    449         // Do not add the call if it is a potential MMI code.
    450         if (isPotentialMMICode(handle) || isPotentialInCallMMICode) {
    451             call.addListener(this);
    452         } else {
    453             addCall(call);
    454         }
    455 
    456         return call;
    457     }
    458 
    459     /**
    460      * Attempts to issue/connect the specified call.
    461      *
    462      * @param handle Handle to connect the call with.
    463      * @param gatewayInfo Optional gateway information that can be used to route the call to the
    464      *        actual dialed handle via a gateway provider. May be null.
    465      * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects.
    466      * @param videoState The desired video state for the outgoing call.
    467      */
    468     void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn,
    469             int videoState) {
    470         if (call == null) {
    471             // don't do anything if the call no longer exists
    472             Log.i(this, "Canceling unknown call.");
    473             return;
    474         }
    475 
    476         final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress();
    477 
    478         if (gatewayInfo == null) {
    479             Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle));
    480         } else {
    481             Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s",
    482                     Log.pii(uriHandle), Log.pii(handle));
    483         }
    484 
    485         call.setHandle(uriHandle);
    486         call.setGatewayInfo(gatewayInfo);
    487         call.setStartWithSpeakerphoneOn(speakerphoneOn);
    488         call.setVideoState(videoState);
    489 
    490         boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
    491                 call.getHandle());
    492         if (isEmergencyCall) {
    493             // Emergency -- CreateConnectionProcessor will choose accounts automatically
    494             call.setTargetPhoneAccount(null);
    495         }
    496 
    497         if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
    498             // If the account has been set, proceed to place the outgoing call.
    499             // Otherwise the connection will be initiated when the account is set by the user.
    500             call.startCreateConnection(mPhoneAccountRegistrar);
    501         }
    502     }
    503 
    504     /**
    505      * Attempts to start a conference call for the specified call.
    506      *
    507      * @param call The call to conference.
    508      * @param otherCall The other call to conference with.
    509      */
    510     void conference(Call call, Call otherCall) {
    511         call.conferenceWith(otherCall);
    512     }
    513 
    514     /**
    515      * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
    516      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
    517      * the user opting to answer said call.
    518      *
    519      * @param call The call to answer.
    520      * @param videoState The video state in which to answer the call.
    521      */
    522     void answerCall(Call call, int videoState) {
    523         if (!mCalls.contains(call)) {
    524             Log.i(this, "Request to answer a non-existent call %s", call);
    525         } else {
    526             // If the foreground call is not the ringing call and it is currently isActive() or
    527             // STATE_DIALING, put it on hold before answering the call.
    528             if (mForegroundCall != null && mForegroundCall != call &&
    529                     (mForegroundCall.isActive() ||
    530                      mForegroundCall.getState() == CallState.DIALING)) {
    531                 if (0 == (mForegroundCall.getCallCapabilities() & PhoneCapabilities.HOLD)) {
    532                     // This call does not support hold.  If it is from a different connection
    533                     // service, then disconnect it, otherwise allow the connection service to
    534                     // figure out the right states.
    535                     if (mForegroundCall.getConnectionService() != call.getConnectionService()) {
    536                         mForegroundCall.disconnect();
    537                     }
    538                 } else {
    539                     Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
    540                             mForegroundCall, call);
    541                     mForegroundCall.hold();
    542                 }
    543                 // TODO: Wait until we get confirmation of the active call being
    544                 // on-hold before answering the new call.
    545                 // TODO: Import logic from CallManager.acceptCall()
    546             }
    547 
    548             for (CallsManagerListener listener : mListeners) {
    549                 listener.onIncomingCallAnswered(call);
    550             }
    551 
    552             // We do not update the UI until we get confirmation of the answer() through
    553             // {@link #markCallAsActive}.
    554             call.answer(videoState);
    555         }
    556     }
    557 
    558     /**
    559      * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call
    560      * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
    561      * the user opting to reject said call.
    562      */
    563     void rejectCall(Call call, boolean rejectWithMessage, String textMessage) {
    564         if (!mCalls.contains(call)) {
    565             Log.i(this, "Request to reject a non-existent call %s", call);
    566         } else {
    567             for (CallsManagerListener listener : mListeners) {
    568                 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage);
    569             }
    570             call.reject(rejectWithMessage, textMessage);
    571         }
    572     }
    573 
    574     /**
    575      * Instructs Telecom to play the specified DTMF tone within the specified call.
    576      *
    577      * @param digit The DTMF digit to play.
    578      */
    579     void playDtmfTone(Call call, char digit) {
    580         if (!mCalls.contains(call)) {
    581             Log.i(this, "Request to play DTMF in a non-existent call %s", call);
    582         } else {
    583             call.playDtmfTone(digit);
    584             mDtmfLocalTonePlayer.playTone(call, digit);
    585         }
    586     }
    587 
    588     /**
    589      * Instructs Telecom to stop the currently playing DTMF tone, if any.
    590      */
    591     void stopDtmfTone(Call call) {
    592         if (!mCalls.contains(call)) {
    593             Log.i(this, "Request to stop DTMF in a non-existent call %s", call);
    594         } else {
    595             call.stopDtmfTone();
    596             mDtmfLocalTonePlayer.stopTone(call);
    597         }
    598     }
    599 
    600     /**
    601      * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any.
    602      */
    603     void postDialContinue(Call call, boolean proceed) {
    604         if (!mCalls.contains(call)) {
    605             Log.i(this, "Request to continue post-dial string in a non-existent call %s", call);
    606         } else {
    607             call.postDialContinue(proceed);
    608         }
    609     }
    610 
    611     /**
    612      * Instructs Telecom to disconnect the specified call. Intended to be invoked by the
    613      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
    614      * the user hitting the end-call button.
    615      */
    616     void disconnectCall(Call call) {
    617         Log.v(this, "disconnectCall %s", call);
    618 
    619         if (!mCalls.contains(call)) {
    620             Log.w(this, "Unknown call (%s) asked to disconnect", call);
    621         } else {
    622             mLocallyDisconnectingCalls.add(call);
    623             call.disconnect();
    624         }
    625     }
    626 
    627     /**
    628      * Instructs Telecom to disconnect all calls.
    629      */
    630     void disconnectAllCalls() {
    631         Log.v(this, "disconnectAllCalls");
    632 
    633         for (Call call : mCalls) {
    634             disconnectCall(call);
    635         }
    636     }
    637 
    638 
    639     /**
    640      * Instructs Telecom to put the specified call on hold. Intended to be invoked by the
    641      * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by
    642      * the user hitting the hold button during an active call.
    643      */
    644     void holdCall(Call call) {
    645         if (!mCalls.contains(call)) {
    646             Log.w(this, "Unknown call (%s) asked to be put on hold", call);
    647         } else {
    648             Log.d(this, "Putting call on hold: (%s)", call);
    649             call.hold();
    650         }
    651     }
    652 
    653     /**
    654      * Instructs Telecom to release the specified call from hold. Intended to be invoked by
    655      * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered
    656      * by the user hitting the hold button during a held call.
    657      */
    658     void unholdCall(Call call) {
    659         if (!mCalls.contains(call)) {
    660             Log.w(this, "Unknown call (%s) asked to be removed from hold", call);
    661         } else {
    662             Log.d(this, "unholding call: (%s)", call);
    663             for (Call c : mCalls) {
    664                 if (c != null && c.isAlive() && c != call) {
    665                     c.hold();
    666                 }
    667             }
    668             call.unhold();
    669         }
    670     }
    671 
    672     /** Called by the in-call UI to change the mute state. */
    673     void mute(boolean shouldMute) {
    674         mCallAudioManager.mute(shouldMute);
    675     }
    676 
    677     /**
    678       * Called by the in-call UI to change the audio route, for example to change from earpiece to
    679       * speaker phone.
    680       */
    681     void setAudioRoute(int route) {
    682         mCallAudioManager.setAudioRoute(route);
    683     }
    684 
    685     /** Called by the in-call UI to turn the proximity sensor on. */
    686     void turnOnProximitySensor() {
    687         mProximitySensorManager.turnOn();
    688     }
    689 
    690     /**
    691      * Called by the in-call UI to turn the proximity sensor off.
    692      * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise,
    693      *        the screen will be kept off until the proximity sensor goes negative.
    694      */
    695     void turnOffProximitySensor(boolean screenOnImmediately) {
    696         mProximitySensorManager.turnOff(screenOnImmediately);
    697     }
    698 
    699     void phoneAccountSelected(Call call, PhoneAccountHandle account) {
    700         if (!mCalls.contains(call)) {
    701             Log.i(this, "Attempted to add account to unknown call %s", call);
    702         } else {
    703             // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and
    704             // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the
    705             // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without
    706             // respecting a rewritten number or a canceled number. This is unlikely since
    707             // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting
    708             // a phone account from the in-call UI.
    709             call.setTargetPhoneAccount(account);
    710 
    711             // Note: emergency calls never go through account selection dialog so they never
    712             // arrive here.
    713             if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) {
    714                 call.startCreateConnection(mPhoneAccountRegistrar);
    715             } else {
    716                 call.disconnect();
    717             }
    718         }
    719     }
    720 
    721     /** Called when the audio state changes. */
    722     void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) {
    723         Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState);
    724         for (CallsManagerListener listener : mListeners) {
    725             listener.onAudioStateChanged(oldAudioState, newAudioState);
    726         }
    727     }
    728 
    729     void markCallAsRinging(Call call) {
    730         setCallState(call, CallState.RINGING);
    731     }
    732 
    733     void markCallAsDialing(Call call) {
    734         setCallState(call, CallState.DIALING);
    735     }
    736 
    737     void markCallAsActive(Call call) {
    738         if (call.getConnectTimeMillis() == 0) {
    739             call.setConnectTimeMillis(System.currentTimeMillis());
    740         }
    741         setCallState(call, CallState.ACTIVE);
    742 
    743         if (call.getStartWithSpeakerphoneOn()) {
    744             setAudioRoute(AudioState.ROUTE_SPEAKER);
    745         }
    746     }
    747 
    748     void markCallAsOnHold(Call call) {
    749         setCallState(call, CallState.ON_HOLD);
    750     }
    751 
    752     /**
    753      * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the
    754      * last live call, then also disconnect from the in-call controller.
    755      *
    756      * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}.
    757      */
    758     void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) {
    759         call.setDisconnectCause(disconnectCause);
    760         setCallState(call, CallState.DISCONNECTED);
    761     }
    762 
    763     /**
    764      * Removes an existing disconnected call, and notifies the in-call app.
    765      */
    766     void markCallAsRemoved(Call call) {
    767         removeCall(call);
    768         if (mLocallyDisconnectingCalls.contains(call)) {
    769             mLocallyDisconnectingCalls.remove(call);
    770             if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) {
    771                 mForegroundCall.unhold();
    772             }
    773         }
    774     }
    775 
    776     /**
    777      * Cleans up any calls currently associated with the specified connection service when the
    778      * service binder disconnects unexpectedly.
    779      *
    780      * @param service The connection service that disconnected.
    781      */
    782     void handleConnectionServiceDeath(ConnectionServiceWrapper service) {
    783         if (service != null) {
    784             for (Call call : mCalls) {
    785                 if (call.getConnectionService() == service) {
    786                     markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR));
    787                 }
    788             }
    789         }
    790     }
    791 
    792     boolean hasAnyCalls() {
    793         return !mCalls.isEmpty();
    794     }
    795 
    796     boolean hasActiveOrHoldingCall() {
    797         return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null;
    798     }
    799 
    800     boolean hasRingingCall() {
    801         return getFirstCallWithState(CallState.RINGING) != null;
    802     }
    803 
    804     boolean onMediaButton(int type) {
    805         if (hasAnyCalls()) {
    806             if (HeadsetMediaButton.SHORT_PRESS == type) {
    807                 Call ringingCall = getFirstCallWithState(CallState.RINGING);
    808                 if (ringingCall == null) {
    809                     mCallAudioManager.toggleMute();
    810                     return true;
    811                 } else {
    812                     ringingCall.answer(ringingCall.getVideoState());
    813                     return true;
    814                 }
    815             } else if (HeadsetMediaButton.LONG_PRESS == type) {
    816                 Log.d(this, "handleHeadsetHook: longpress -> hangup");
    817                 Call callToHangup = getFirstCallWithState(
    818                         CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD);
    819                 if (callToHangup != null) {
    820                     callToHangup.disconnect();
    821                     return true;
    822                 }
    823             }
    824         }
    825         return false;
    826     }
    827 
    828     /**
    829      * Checks to see if the specified call is the only high-level call and if so, enable the
    830      * "Add-call" button. We allow you to add a second call but not a third or beyond.
    831      *
    832      * @param call The call to test for add-call.
    833      * @return Whether the add-call feature should be enabled for the call.
    834      */
    835     protected boolean isAddCallCapable(Call call) {
    836         if (call.getParentCall() != null) {
    837             // Never true for child calls.
    838             return false;
    839         }
    840 
    841         // Use canManageConference as a mechanism to check if the call is CDMA.
    842         // Disable "Add Call" for CDMA calls which are conference calls.
    843         boolean canManageConference = PhoneCapabilities.MANAGE_CONFERENCE
    844                 == (call.getCallCapabilities() & PhoneCapabilities.MANAGE_CONFERENCE);
    845         if (call.isConference() && !canManageConference) {
    846             return false;
    847         }
    848 
    849         // Loop through all the other calls and there exists a top level (has no parent) call
    850         // that is not the specified call, return false.
    851         for (Call otherCall : mCalls) {
    852             if (call != otherCall && otherCall.getParentCall() == null) {
    853                 return false;
    854             }
    855         }
    856         return true;
    857     }
    858 
    859     Call getRingingCall() {
    860         return getFirstCallWithState(CallState.RINGING);
    861     }
    862 
    863     Call getActiveCall() {
    864         return getFirstCallWithState(CallState.ACTIVE);
    865     }
    866 
    867     Call getDialingCall() {
    868         return getFirstCallWithState(CallState.DIALING);
    869     }
    870 
    871     Call getHeldCall() {
    872         return getFirstCallWithState(CallState.ON_HOLD);
    873     }
    874 
    875     int getNumHeldCalls() {
    876         int count = 0;
    877         for (Call call : mCalls) {
    878             if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
    879                 count++;
    880             }
    881         }
    882         return count;
    883     }
    884 
    885     Call getFirstCallWithState(int... states) {
    886         return getFirstCallWithState(null, states);
    887     }
    888 
    889     /**
    890      * Returns the first call that it finds with the given states. The states are treated as having
    891      * priority order so that any call with the first state will be returned before any call with
    892      * states listed later in the parameter list.
    893      *
    894      * @param callToSkip Call that this method should skip while searching
    895      */
    896     Call getFirstCallWithState(Call callToSkip, int... states) {
    897         for (int currentState : states) {
    898             // check the foreground first
    899             if (mForegroundCall != null && mForegroundCall.getState() == currentState) {
    900                 return mForegroundCall;
    901             }
    902 
    903             for (Call call : mCalls) {
    904                 if (Objects.equals(callToSkip, call)) {
    905                     continue;
    906                 }
    907 
    908                 // Only operate on top-level calls
    909                 if (call.getParentCall() != null) {
    910                     continue;
    911                 }
    912 
    913                 if (currentState == call.getState()) {
    914                     return call;
    915                 }
    916             }
    917         }
    918         return null;
    919     }
    920 
    921     Call createConferenceCall(
    922             PhoneAccountHandle phoneAccount,
    923             ParcelableConference parcelableConference) {
    924         Call call = new Call(
    925                 mContext,
    926                 mConnectionServiceRepository,
    927                 null /* handle */,
    928                 null /* gatewayInfo */,
    929                 null /* connectionManagerPhoneAccount */,
    930                 phoneAccount,
    931                 false /* isIncoming */,
    932                 true /* isConference */);
    933 
    934         setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()));
    935         if (call.getState() == CallState.ACTIVE) {
    936             call.setConnectTimeMillis(System.currentTimeMillis());
    937         }
    938         call.setCallCapabilities(parcelableConference.getCapabilities());
    939 
    940         // TODO: Move this to be a part of addCall()
    941         call.addListener(this);
    942         addCall(call);
    943         return call;
    944     }
    945 
    946     /**
    947      * @return the call state currently tracked by {@link PhoneStateBroadcaster}
    948      */
    949     int getCallState() {
    950         return mPhoneStateBroadcaster.getCallState();
    951     }
    952 
    953     /**
    954      * Retrieves the {@link PhoneAccountRegistrar}.
    955      *
    956      * @return The {@link PhoneAccountRegistrar}.
    957      */
    958     PhoneAccountRegistrar getPhoneAccountRegistrar() {
    959         return mPhoneAccountRegistrar;
    960     }
    961 
    962     /**
    963      * Retrieves the {@link MissedCallNotifier}
    964      * @return The {@link MissedCallNotifier}.
    965      */
    966     MissedCallNotifier getMissedCallNotifier() {
    967         return mMissedCallNotifier;
    968     }
    969 
    970     /**
    971      * Adds the specified call to the main list of live calls.
    972      *
    973      * @param call The call to add.
    974      */
    975     private void addCall(Call call) {
    976         Log.v(this, "addCall(%s)", call);
    977 
    978         call.addListener(this);
    979         mCalls.add(call);
    980 
    981         // TODO: Update mForegroundCall prior to invoking
    982         // onCallAdded for calls which immediately take the foreground (like the first call).
    983         for (CallsManagerListener listener : mListeners) {
    984             listener.onCallAdded(call);
    985         }
    986         updateForegroundCall();
    987     }
    988 
    989     private void removeCall(Call call) {
    990         Log.v(this, "removeCall(%s)", call);
    991 
    992         call.setParentCall(null);  // need to clean up parent relationship before destroying.
    993         call.removeListener(this);
    994         call.clearConnectionService();
    995 
    996         boolean shouldNotify = false;
    997         if (mCalls.contains(call)) {
    998             mCalls.remove(call);
    999             shouldNotify = true;
   1000         }
   1001 
   1002         // Only broadcast changes for calls that are being tracked.
   1003         if (shouldNotify) {
   1004             for (CallsManagerListener listener : mListeners) {
   1005                 listener.onCallRemoved(call);
   1006             }
   1007             updateForegroundCall();
   1008         }
   1009 
   1010         // Now that a call has been removed, other calls may gain new call capabilities (for
   1011         // example, if only one call is left, it is now add-call capable again). Trigger the
   1012         // recalculation of the call's current capabilities by forcing an update. (See
   1013         // InCallController.toParcelableCall()).
   1014         for (Call otherCall : mCalls) {
   1015             otherCall.setCallCapabilities(otherCall.getCallCapabilities(), true /* forceUpdate */);
   1016         }
   1017     }
   1018 
   1019     /**
   1020      * Sets the specified state on the specified call.
   1021      *
   1022      * @param call The call.
   1023      * @param newState The new state of the call.
   1024      */
   1025     private void setCallState(Call call, int newState) {
   1026         if (call == null) {
   1027             return;
   1028         }
   1029         int oldState = call.getState();
   1030         Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState),
   1031                 CallState.toString(newState), call);
   1032         if (newState != oldState) {
   1033             // Unfortunately, in the telephony world the radio is king. So if the call notifies
   1034             // us that the call is in a particular state, we allow it even if it doesn't make
   1035             // sense (e.g., STATE_ACTIVE -> STATE_RINGING).
   1036             // TODO: Consider putting a stop to the above and turning CallState
   1037             // into a well-defined state machine.
   1038             // TODO: Define expected state transitions here, and log when an
   1039             // unexpected transition occurs.
   1040             call.setState(newState);
   1041 
   1042             // Only broadcast state change for calls that are being tracked.
   1043             if (mCalls.contains(call)) {
   1044                 for (CallsManagerListener listener : mListeners) {
   1045                     listener.onCallStateChanged(call, oldState, newState);
   1046                 }
   1047                 updateForegroundCall();
   1048             }
   1049         }
   1050     }
   1051 
   1052     /**
   1053      * Checks which call should be visible to the user and have audio focus.
   1054      */
   1055     private void updateForegroundCall() {
   1056         Call newForegroundCall = null;
   1057         for (Call call : mCalls) {
   1058             // TODO: Foreground-ness needs to be explicitly set. No call, regardless
   1059             // of its state will be foreground by default and instead the connection service should
   1060             // be notified when its calls enter and exit foreground state. Foreground will mean that
   1061             // the call should play audio and listen to microphone if it wants.
   1062 
   1063             // Only top-level calls can be in foreground
   1064             if (call.getParentCall() != null) {
   1065                 continue;
   1066             }
   1067 
   1068             // Active calls have priority.
   1069             if (call.isActive()) {
   1070                 newForegroundCall = call;
   1071                 break;
   1072             }
   1073 
   1074             if (call.isAlive() || call.getState() == CallState.RINGING) {
   1075                 newForegroundCall = call;
   1076                 // Don't break in case there's an active call that has priority.
   1077             }
   1078         }
   1079 
   1080         if (newForegroundCall != mForegroundCall) {
   1081             Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall);
   1082             Call oldForegroundCall = mForegroundCall;
   1083             mForegroundCall = newForegroundCall;
   1084 
   1085             for (CallsManagerListener listener : mListeners) {
   1086                 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall);
   1087             }
   1088         }
   1089     }
   1090 
   1091     private boolean isPotentialMMICode(Uri handle) {
   1092         return (handle != null && handle.getSchemeSpecificPart() != null
   1093                 && handle.getSchemeSpecificPart().contains("#"));
   1094     }
   1095 
   1096     /**
   1097      * Determines if a dialed number is potentially an In-Call MMI code.  In-Call MMI codes are
   1098      * MMI codes which can be dialed when one or more calls are in progress.
   1099      * <P>
   1100      * Checks for numbers formatted similar to the MMI codes defined in:
   1101      * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)}
   1102      * and
   1103      * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)}
   1104      *
   1105      * @param handle The URI to call.
   1106      * @return {@code True} if the URI represents a number which could be an in-call MMI code.
   1107      */
   1108     private boolean isPotentialInCallMMICode(Uri handle) {
   1109         if (handle != null && handle.getSchemeSpecificPart() != null &&
   1110                 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) {
   1111 
   1112             String dialedNumber = handle.getSchemeSpecificPart();
   1113             return (dialedNumber.equals("0") ||
   1114                     (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) ||
   1115                     (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) ||
   1116                     dialedNumber.equals("3") ||
   1117                     dialedNumber.equals("4") ||
   1118                     dialedNumber.equals("5"));
   1119         }
   1120         return false;
   1121     }
   1122 
   1123     private int getNumCallsWithState(int... states) {
   1124         int count = 0;
   1125         for (int state : states) {
   1126             for (Call call : mCalls) {
   1127                 if (call.getState() == state) {
   1128                     count++;
   1129                 }
   1130             }
   1131         }
   1132         return count;
   1133     }
   1134 
   1135     private boolean hasMaximumLiveCalls() {
   1136         return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES);
   1137     }
   1138 
   1139     private boolean hasMaximumHoldingCalls() {
   1140         return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD);
   1141     }
   1142 
   1143     private boolean hasMaximumRingingCalls() {
   1144         return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING);
   1145     }
   1146 
   1147     private boolean hasMaximumOutgoingCalls() {
   1148         return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES);
   1149     }
   1150 
   1151     private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) {
   1152         if (hasMaximumLiveCalls()) {
   1153             // NOTE: If the amount of live calls changes beyond 1, this logic will probably
   1154             // have to change.
   1155             Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES);
   1156 
   1157             if (call == liveCall) {
   1158                 // If the call is already the foreground call, then we are golden.
   1159                 // This can happen after the user selects an account in the PRE_DIAL_WAIT
   1160                 // state since the call was already populated into the list.
   1161                 return true;
   1162             }
   1163 
   1164             if (hasMaximumOutgoingCalls()) {
   1165                 // Disconnect the current outgoing call if it's not an emergency call. If the user
   1166                 // tries to make two outgoing calls to different emergency call numbers, we will try
   1167                 // to connect the first outgoing call.
   1168                 if (isEmergency) {
   1169                     Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES);
   1170                     if (!outgoingCall.isEmergencyCall()) {
   1171                         outgoingCall.disconnect();
   1172                         return true;
   1173                     }
   1174                 }
   1175                 return false;
   1176             }
   1177 
   1178             if (hasMaximumHoldingCalls()) {
   1179                 // There is no more room for any more calls, unless it's an emergency.
   1180                 if (isEmergency) {
   1181                     // Kill the current active call, this is easier then trying to disconnect a
   1182                     // holding call and hold an active call.
   1183                     liveCall.disconnect();
   1184                     return true;
   1185                 }
   1186                 return false;  // No more room!
   1187             }
   1188 
   1189             // We have room for at least one more holding call at this point.
   1190 
   1191             // First thing, if we are trying to make a call with the same phone account as the live
   1192             // call, then allow it so that the connection service can make its own decision about
   1193             // how to handle the new call relative to the current one.
   1194             if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) {
   1195                 return true;
   1196             } else if (call.getTargetPhoneAccount() == null) {
   1197                 // Without a phone account, we can't say reliably that the call will fail.
   1198                 // If the user chooses the same phone account as the live call, then it's
   1199                 // still possible that the call can be made (like with CDMA calls not supporting
   1200                 // hold but they still support adding a call by going immediately into conference
   1201                 // mode). Return true here and we'll run this code again after user chooses an
   1202                 // account.
   1203                 return true;
   1204             }
   1205 
   1206             // Try to hold the live call before attempting the new outgoing call.
   1207             if (liveCall.can(PhoneCapabilities.HOLD)) {
   1208                 liveCall.hold();
   1209                 return true;
   1210             }
   1211 
   1212             // The live call cannot be held so we're out of luck here.  There's no room.
   1213             return false;
   1214         }
   1215         return true;
   1216     }
   1217 
   1218     /**
   1219      * Dumps the state of the {@link CallsManager}.
   1220      *
   1221      * @param pw The {@code IndentingPrintWriter} to write the state to.
   1222      */
   1223     public void dump(IndentingPrintWriter pw) {
   1224         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   1225         pw.increaseIndent();
   1226         if (mCalls != null) {
   1227             pw.println("mCalls: ");
   1228             pw.increaseIndent();
   1229             for (Call call : mCalls) {
   1230                 pw.println(call);
   1231             }
   1232             pw.decreaseIndent();
   1233         }
   1234         pw.decreaseIndent();
   1235     }
   1236 }
   1237