• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.graphics.Bitmap;
21 import android.graphics.drawable.Drawable;
22 import android.net.Uri;
23 import android.os.Bundle;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.RemoteException;
27 import android.os.Trace;
28 import android.provider.ContactsContract.Contacts;
29 import android.telecom.CallAudioState;
30 import android.telecom.Conference;
31 import android.telecom.DisconnectCause;
32 import android.telecom.Connection;
33 import android.telecom.GatewayInfo;
34 import android.telecom.ParcelableConnection;
35 import android.telecom.PhoneAccount;
36 import android.telecom.PhoneAccountHandle;
37 import android.telecom.Response;
38 import android.telecom.StatusHints;
39 import android.telecom.TelecomManager;
40 import android.telecom.VideoProfile;
41 import android.telephony.PhoneNumberUtils;
42 import android.text.TextUtils;
43 import android.os.UserHandle;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.telecom.IVideoProvider;
47 import com.android.internal.telephony.CallerInfo;
48 import com.android.internal.telephony.SmsApplication;
49 import com.android.internal.util.Preconditions;
50 
51 import java.lang.String;
52 import java.util.ArrayList;
53 import java.util.Collections;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Locale;
57 import java.util.Objects;
58 import java.util.Set;
59 import java.util.concurrent.ConcurrentHashMap;
60 
61 /**
62  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
63  *  from the time the call intent was received by Telecom (vs. the time the call was
64  *  connected etc).
65  */
66 @VisibleForTesting
67 public class Call implements CreateConnectionResponse {
68     public final static String CALL_ID_UNKNOWN = "-1";
69     public final static long DATA_USAGE_NOT_SET = -1;
70 
71     public static final int CALL_DIRECTION_UNDEFINED = 0;
72     public static final int CALL_DIRECTION_OUTGOING = 1;
73     public static final int CALL_DIRECTION_INCOMING = 2;
74     public static final int CALL_DIRECTION_UNKNOWN = 3;
75 
76     /** Identifies extras changes which originated from a connection service. */
77     public static final int SOURCE_CONNECTION_SERVICE = 1;
78     /** Identifies extras changes which originated from an incall service. */
79     public static final int SOURCE_INCALL_SERVICE = 2;
80 
81     /**
82      * Listener for events on the call.
83      */
84     @VisibleForTesting
85     public interface Listener {
onSuccessfulOutgoingCall(Call call, int callState)86         void onSuccessfulOutgoingCall(Call call, int callState);
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)87         void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
onSuccessfulIncomingCall(Call call)88         void onSuccessfulIncomingCall(Call call);
onFailedIncomingCall(Call call)89         void onFailedIncomingCall(Call call);
onSuccessfulUnknownCall(Call call, int callState)90         void onSuccessfulUnknownCall(Call call, int callState);
onFailedUnknownCall(Call call)91         void onFailedUnknownCall(Call call);
onRingbackRequested(Call call, boolean ringbackRequested)92         void onRingbackRequested(Call call, boolean ringbackRequested);
onPostDialWait(Call call, String remaining)93         void onPostDialWait(Call call, String remaining);
onPostDialChar(Call call, char nextChar)94         void onPostDialChar(Call call, char nextChar);
onConnectionCapabilitiesChanged(Call call)95         void onConnectionCapabilitiesChanged(Call call);
onConnectionPropertiesChanged(Call call)96         void onConnectionPropertiesChanged(Call call);
onParentChanged(Call call)97         void onParentChanged(Call call);
onChildrenChanged(Call call)98         void onChildrenChanged(Call call);
onCannedSmsResponsesLoaded(Call call)99         void onCannedSmsResponsesLoaded(Call call);
onVideoCallProviderChanged(Call call)100         void onVideoCallProviderChanged(Call call);
onCallerInfoChanged(Call call)101         void onCallerInfoChanged(Call call);
onIsVoipAudioModeChanged(Call call)102         void onIsVoipAudioModeChanged(Call call);
onStatusHintsChanged(Call call)103         void onStatusHintsChanged(Call call);
onExtrasChanged(Call c, int source, Bundle extras)104         void onExtrasChanged(Call c, int source, Bundle extras);
onExtrasRemoved(Call c, int source, List<String> keys)105         void onExtrasRemoved(Call c, int source, List<String> keys);
onHandleChanged(Call call)106         void onHandleChanged(Call call);
onCallerDisplayNameChanged(Call call)107         void onCallerDisplayNameChanged(Call call);
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)108         void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
onTargetPhoneAccountChanged(Call call)109         void onTargetPhoneAccountChanged(Call call);
onConnectionManagerPhoneAccountChanged(Call call)110         void onConnectionManagerPhoneAccountChanged(Call call);
onPhoneAccountChanged(Call call)111         void onPhoneAccountChanged(Call call);
onConferenceableCallsChanged(Call call)112         void onConferenceableCallsChanged(Call call);
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)113         boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
onHoldToneRequested(Call call)114         void onHoldToneRequested(Call call);
onConnectionEvent(Call call, String event, Bundle extras)115         void onConnectionEvent(Call call, String event, Bundle extras);
onExternalCallChanged(Call call, boolean isExternalCall)116         void onExternalCallChanged(Call call, boolean isExternalCall);
117     }
118 
119     public abstract static class ListenerBase implements Listener {
120         @Override
onSuccessfulOutgoingCall(Call call, int callState)121         public void onSuccessfulOutgoingCall(Call call, int callState) {}
122         @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)123         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
124         @Override
onSuccessfulIncomingCall(Call call)125         public void onSuccessfulIncomingCall(Call call) {}
126         @Override
onFailedIncomingCall(Call call)127         public void onFailedIncomingCall(Call call) {}
128         @Override
onSuccessfulUnknownCall(Call call, int callState)129         public void onSuccessfulUnknownCall(Call call, int callState) {}
130         @Override
onFailedUnknownCall(Call call)131         public void onFailedUnknownCall(Call call) {}
132         @Override
onRingbackRequested(Call call, boolean ringbackRequested)133         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
134         @Override
onPostDialWait(Call call, String remaining)135         public void onPostDialWait(Call call, String remaining) {}
136         @Override
onPostDialChar(Call call, char nextChar)137         public void onPostDialChar(Call call, char nextChar) {}
138         @Override
onConnectionCapabilitiesChanged(Call call)139         public void onConnectionCapabilitiesChanged(Call call) {}
140         @Override
onConnectionPropertiesChanged(Call call)141         public void onConnectionPropertiesChanged(Call call) {}
142         @Override
onParentChanged(Call call)143         public void onParentChanged(Call call) {}
144         @Override
onChildrenChanged(Call call)145         public void onChildrenChanged(Call call) {}
146         @Override
onCannedSmsResponsesLoaded(Call call)147         public void onCannedSmsResponsesLoaded(Call call) {}
148         @Override
onVideoCallProviderChanged(Call call)149         public void onVideoCallProviderChanged(Call call) {}
150         @Override
onCallerInfoChanged(Call call)151         public void onCallerInfoChanged(Call call) {}
152         @Override
onIsVoipAudioModeChanged(Call call)153         public void onIsVoipAudioModeChanged(Call call) {}
154         @Override
onStatusHintsChanged(Call call)155         public void onStatusHintsChanged(Call call) {}
156         @Override
onExtrasChanged(Call c, int source, Bundle extras)157         public void onExtrasChanged(Call c, int source, Bundle extras) {}
158         @Override
onExtrasRemoved(Call c, int source, List<String> keys)159         public void onExtrasRemoved(Call c, int source, List<String> keys) {}
160         @Override
onHandleChanged(Call call)161         public void onHandleChanged(Call call) {}
162         @Override
onCallerDisplayNameChanged(Call call)163         public void onCallerDisplayNameChanged(Call call) {}
164         @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)165         public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}
166         @Override
onTargetPhoneAccountChanged(Call call)167         public void onTargetPhoneAccountChanged(Call call) {}
168         @Override
onConnectionManagerPhoneAccountChanged(Call call)169         public void onConnectionManagerPhoneAccountChanged(Call call) {}
170         @Override
onPhoneAccountChanged(Call call)171         public void onPhoneAccountChanged(Call call) {}
172         @Override
onConferenceableCallsChanged(Call call)173         public void onConferenceableCallsChanged(Call call) {}
174         @Override
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)175         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
176             return false;
177         }
178 
179         @Override
onHoldToneRequested(Call call)180         public void onHoldToneRequested(Call call) {}
181         @Override
onConnectionEvent(Call call, String event, Bundle extras)182         public void onConnectionEvent(Call call, String event, Bundle extras) {}
183         @Override
onExternalCallChanged(Call call, boolean isExternalCall)184         public void onExternalCallChanged(Call call, boolean isExternalCall) {}
185     }
186 
187     private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
188             new CallerInfoLookupHelper.OnQueryCompleteListener() {
189                 /** ${inheritDoc} */
190                 @Override
191                 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) {
192                     synchronized (mLock) {
193                         Call.this.setCallerInfo(handle, callerInfo);
194                     }
195                 }
196 
197                 @Override
198                 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) {
199                     synchronized (mLock) {
200                         Call.this.setCallerInfo(handle, callerInfo);
201                     }
202                 }
203             };
204 
205     /**
206      * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
207      */
208     private final int mCallDirection;
209 
210     /**
211      * The post-dial digits that were dialed after the network portion of the number
212      */
213     private final String mPostDialDigits;
214 
215     /**
216      * The secondary line number that an incoming call has been received on if the SIM subscription
217      * has multiple associated numbers.
218      */
219     private String mViaNumber = "";
220 
221     /**
222      * The time this call was created. Beyond logging and such, may also be used for bookkeeping
223      * and specifically for marking certain call attempts as failed attempts.
224      */
225     private long mCreationTimeMillis = System.currentTimeMillis();
226 
227     /** The time this call was made active. */
228     private long mConnectTimeMillis = 0;
229 
230     /** The time this call was disconnected. */
231     private long mDisconnectTimeMillis = 0;
232 
233     /** The gateway information associated with this call. This stores the original call handle
234      * that the user is attempting to connect to via the gateway, the actual handle to dial in
235      * order to connect the call via the gateway, as well as the package name of the gateway
236      * service. */
237     private GatewayInfo mGatewayInfo;
238 
239     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
240 
241     private PhoneAccountHandle mTargetPhoneAccountHandle;
242 
243     private UserHandle mInitiatingUser;
244 
245     private final Handler mHandler = new Handler(Looper.getMainLooper());
246 
247     private final List<Call> mConferenceableCalls = new ArrayList<>();
248 
249     /** The state of the call. */
250     private int mState;
251 
252     /** The handle with which to establish this call. */
253     private Uri mHandle;
254 
255     /**
256      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
257      */
258     private int mHandlePresentation;
259 
260     /** The caller display name (CNAP) set by the connection service. */
261     private String mCallerDisplayName;
262 
263     /**
264      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
265      */
266     private int mCallerDisplayNamePresentation;
267 
268     /**
269      * The connection service which is attempted or already connecting this call.
270      */
271     private ConnectionServiceWrapper mConnectionService;
272 
273     private boolean mIsEmergencyCall;
274 
275     private boolean mSpeakerphoneOn;
276 
277     /**
278      * Tracks the video states which were applicable over the duration of a call.
279      * See {@link VideoProfile} for a list of valid video states.
280      * <p>
281      * Video state history is tracked when the call is active, and when a call is rejected or
282      * missed.
283      */
284     private int mVideoStateHistory;
285 
286     private int mVideoState;
287 
288     /**
289      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
290      * See {@link android.telecom.DisconnectCause}.
291      */
292     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
293 
294     private Bundle mIntentExtras = new Bundle();
295 
296     /** Set of listeners on this call.
297      *
298      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
299      * load factor before resizing, 1 means we only expect a single thread to
300      * access the map so make only a single shard
301      */
302     private final Set<Listener> mListeners = Collections.newSetFromMap(
303             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
304 
305     private CreateConnectionProcessor mCreateConnectionProcessor;
306 
307     /** Caller information retrieved from the latest contact query. */
308     private CallerInfo mCallerInfo;
309 
310     /** The latest token used with a contact info query. */
311     private int mQueryToken = 0;
312 
313     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
314     private boolean mRingbackRequested = false;
315 
316     /** Whether direct-to-voicemail query is pending. */
317     private boolean mDirectToVoicemailQueryPending;
318 
319     private int mConnectionCapabilities;
320 
321     private int mConnectionProperties;
322 
323     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
324 
325     private boolean mIsConference = false;
326 
327     private final boolean mShouldAttachToExistingConnection;
328 
329     private Call mParentCall = null;
330 
331     private List<Call> mChildCalls = new LinkedList<>();
332 
333     /** Set of text message responses allowed for this call, if applicable. */
334     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
335 
336     /** Whether an attempt has been made to load the text message responses. */
337     private boolean mCannedSmsResponsesLoadingStarted = false;
338 
339     private IVideoProvider mVideoProvider;
340     private VideoProviderProxy mVideoProviderProxy;
341 
342     private boolean mIsVoipAudioMode;
343     private StatusHints mStatusHints;
344     private Bundle mExtras;
345     private final ConnectionServiceRepository mRepository;
346     private final Context mContext;
347     private final CallsManager mCallsManager;
348     private final TelecomSystem.SyncRoot mLock;
349     private final String mId;
350     private String mConnectionId;
351     private Analytics.CallInfo mAnalytics;
352 
353     private boolean mWasConferencePreviouslyMerged = false;
354 
355     // For conferences which support merge/swap at their level, we retain a notion of an active
356     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
357     // the notion of the current "active" call within the conference call. This maintains the
358     // "active" call and switches every time the user hits "swap".
359     private Call mConferenceLevelActiveCall = null;
360 
361     private boolean mIsLocallyDisconnecting = false;
362 
363     /**
364      * Tracks the current call data usage as reported by the video provider.
365      */
366     private long mCallDataUsage = DATA_USAGE_NOT_SET;
367 
368     private boolean mIsWorkCall;
369 
370     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
371     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
372 
373 
374     /**
375      * Indicates whether the call is remotely held.  A call is considered remotely held when
376      * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
377      * event.
378      */
379     private boolean mIsRemotelyHeld = false;
380 
381     /**
382      * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
383      * {@code True} if the phone account supports video calling, {@code false} otherwise.
384      */
385     private boolean mIsVideoCallingSupported = false;
386 
387     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
388 
389     /**
390      * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
391      * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
392      * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
393      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
394      * originally created it.
395      *
396      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
397      */
398     private String mOriginalConnectionId;
399 
400     /**
401      * Persists the specified parameters and initializes the new instance.
402      *
403      * @param context The context.
404      * @param repository The connection service repository.
405      * @param handle The handle to dial.
406      * @param gatewayInfo Gateway information to use for the call.
407      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
408      *         This account must be one that was registered with the
409      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
410      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
411      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
412      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
413      *         or CALL_DIRECTION_UNKNOWN.
414      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
415      *         connection, regardless of whether it's incoming or outgoing.
416      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, ContactsAsyncHelper contactsAsyncHelper, CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference)417     public Call(
418             String callId,
419             Context context,
420             CallsManager callsManager,
421             TelecomSystem.SyncRoot lock,
422             ConnectionServiceRepository repository,
423             ContactsAsyncHelper contactsAsyncHelper,
424             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
425             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
426             Uri handle,
427             GatewayInfo gatewayInfo,
428             PhoneAccountHandle connectionManagerPhoneAccountHandle,
429             PhoneAccountHandle targetPhoneAccountHandle,
430             int callDirection,
431             boolean shouldAttachToExistingConnection,
432             boolean isConference) {
433         mId = callId;
434         mConnectionId = callId;
435         mState = isConference ? CallState.ACTIVE : CallState.NEW;
436         mContext = context;
437         mCallsManager = callsManager;
438         mLock = lock;
439         mRepository = repository;
440         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
441         setHandle(handle);
442         mPostDialDigits = handle != null
443                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
444         mGatewayInfo = gatewayInfo;
445         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
446         setTargetPhoneAccount(targetPhoneAccountHandle);
447         mCallDirection = callDirection;
448         mIsConference = isConference;
449         mShouldAttachToExistingConnection = shouldAttachToExistingConnection
450                 || callDirection == CALL_DIRECTION_INCOMING;
451         maybeLoadCannedSmsResponses();
452         mAnalytics = new Analytics.CallInfo();
453 
454     }
455 
456     /**
457      * Persists the specified parameters and initializes the new instance.
458      *
459      * @param context The context.
460      * @param repository The connection service repository.
461      * @param handle The handle to dial.
462      * @param gatewayInfo Gateway information to use for the call.
463      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
464      *         This account must be one that was registered with the
465      *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
466      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
467      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
468      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
469      *         or CALL_DIRECTION_UNKNOWN
470      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
471      *         connection, regardless of whether it's incoming or outgoing.
472      * @param connectTimeMillis The connection time of the call.
473      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, ContactsAsyncHelper contactsAsyncHelper, CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, long connectTimeMillis)474     Call(
475             String callId,
476             Context context,
477             CallsManager callsManager,
478             TelecomSystem.SyncRoot lock,
479             ConnectionServiceRepository repository,
480             ContactsAsyncHelper contactsAsyncHelper,
481             CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
482             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
483             Uri handle,
484             GatewayInfo gatewayInfo,
485             PhoneAccountHandle connectionManagerPhoneAccountHandle,
486             PhoneAccountHandle targetPhoneAccountHandle,
487             int callDirection,
488             boolean shouldAttachToExistingConnection,
489             boolean isConference,
490             long connectTimeMillis) {
491         this(callId, context, callsManager, lock, repository, contactsAsyncHelper,
492                 callerInfoAsyncQueryFactory, phoneNumberUtilsAdapter, handle, gatewayInfo,
493                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
494                 shouldAttachToExistingConnection, isConference);
495 
496         mConnectTimeMillis = connectTimeMillis;
497         mAnalytics.setCallStartTime(connectTimeMillis);
498     }
499 
addListener(Listener listener)500     public void addListener(Listener listener) {
501         mListeners.add(listener);
502     }
503 
removeListener(Listener listener)504     public void removeListener(Listener listener) {
505         if (listener != null) {
506             mListeners.remove(listener);
507         }
508     }
509 
initAnalytics()510     public void initAnalytics() {
511         int analyticsDirection;
512         switch (mCallDirection) {
513             case CALL_DIRECTION_OUTGOING:
514                 analyticsDirection = Analytics.OUTGOING_DIRECTION;
515                 break;
516             case CALL_DIRECTION_INCOMING:
517                 analyticsDirection = Analytics.INCOMING_DIRECTION;
518                 break;
519             case CALL_DIRECTION_UNKNOWN:
520             case CALL_DIRECTION_UNDEFINED:
521             default:
522                 analyticsDirection = Analytics.UNKNOWN_DIRECTION;
523         }
524         mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
525         Log.event(this, Log.Events.CREATED);
526     }
527 
getAnalytics()528     public Analytics.CallInfo getAnalytics() {
529         return mAnalytics;
530     }
531 
destroy()532     public void destroy() {
533         Log.event(this, Log.Events.DESTROYED);
534     }
535 
536     /** {@inheritDoc} */
537     @Override
toString()538     public String toString() {
539         String component = null;
540         if (mConnectionService != null && mConnectionService.getComponentName() != null) {
541             component = mConnectionService.getComponentName().flattenToShortString();
542         }
543 
544         return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]",
545                 mId,
546                 CallState.toString(mState),
547                 component,
548                 Log.piiHandle(mHandle),
549                 getVideoStateDescription(getVideoState()),
550                 getChildCalls().size(),
551                 getParentCall() != null,
552                 Connection.capabilitiesToString(getConnectionCapabilities()),
553                 Connection.propertiesToString(getConnectionProperties()));
554     }
555 
556     /**
557      * Builds a debug-friendly description string for a video state.
558      * <p>
559      * A = audio active, T = video transmission active, R = video reception active, P = video
560      * paused.
561      *
562      * @param videoState The video state.
563      * @return A string indicating which bits are set in the video state.
564      */
getVideoStateDescription(int videoState)565     private String getVideoStateDescription(int videoState) {
566         StringBuilder sb = new StringBuilder();
567         sb.append("A");
568 
569         if (VideoProfile.isTransmissionEnabled(videoState)) {
570             sb.append("T");
571         }
572 
573         if (VideoProfile.isReceptionEnabled(videoState)) {
574             sb.append("R");
575         }
576 
577         if (VideoProfile.isPaused(videoState)) {
578             sb.append("P");
579         }
580 
581         return sb.toString();
582     }
583 
584     @VisibleForTesting
getState()585     public int getState() {
586         return mState;
587     }
588 
shouldContinueProcessingAfterDisconnect()589     private boolean shouldContinueProcessingAfterDisconnect() {
590         // Stop processing once the call is active.
591         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
592             return false;
593         }
594 
595         // Only Redial a Call in the case of it being an Emergency Call.
596         if(!isEmergencyCall()) {
597             return false;
598         }
599 
600         // Make sure that there are additional connection services to process.
601         if (mCreateConnectionProcessor == null
602             || !mCreateConnectionProcessor.isProcessingComplete()
603             || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
604             return false;
605         }
606 
607         if (mDisconnectCause == null) {
608             return false;
609         }
610 
611         // Continue processing if the current attempt failed or timed out.
612         return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
613             mCreateConnectionProcessor.isCallTimedOut();
614     }
615 
616     /**
617      * Returns the unique ID for this call as it exists in Telecom.
618      * @return The call ID.
619      */
getId()620     public String getId() {
621         return mId;
622     }
623 
624     /**
625      * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
626      * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
627      * @return The call ID with an appended attempt id.
628      */
getConnectionId()629     public String getConnectionId() {
630         if(mCreateConnectionProcessor != null) {
631             mConnectionId = mId + "_" +
632                     String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
633             return mConnectionId;
634         } else {
635             return mConnectionId;
636         }
637     }
638 
639     /**
640      * Sets the call state. Although there exists the notion of appropriate state transitions
641      * (see {@link CallState}), in practice those expectations break down when cellular systems
642      * misbehave and they do this very often. The result is that we do not enforce state transitions
643      * and instead keep the code resilient to unexpected state changes.
644      */
setState(int newState, String tag)645     public void setState(int newState, String tag) {
646         if (mState != newState) {
647             Log.v(this, "setState %s -> %s", mState, newState);
648 
649             if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
650                 Log.w(this, "continuing processing disconnected call with another service");
651                 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
652                 return;
653             }
654 
655             mState = newState;
656             maybeLoadCannedSmsResponses();
657 
658             if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
659                 if (mConnectTimeMillis == 0) {
660                     // We check to see if mConnectTime is already set to prevent the
661                     // call from resetting active time when it goes in and out of
662                     // ACTIVE/ON_HOLD
663                     mConnectTimeMillis = System.currentTimeMillis();
664                     mAnalytics.setCallStartTime(mConnectTimeMillis);
665                 }
666 
667                 // Video state changes are normally tracked against history when a call is active.
668                 // When the call goes active we need to be sure we track the history in case the
669                 // state never changes during the duration of the call -- we want to ensure we
670                 // always know the state at the start of the call.
671                 mVideoStateHistory = mVideoStateHistory | mVideoState;
672 
673                 // We're clearly not disconnected, so reset the disconnected time.
674                 mDisconnectTimeMillis = 0;
675             } else if (mState == CallState.DISCONNECTED) {
676                 mDisconnectTimeMillis = System.currentTimeMillis();
677                 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
678                 setLocallyDisconnecting(false);
679                 fixParentAfterDisconnect();
680             }
681             if (mState == CallState.DISCONNECTED &&
682                     mDisconnectCause.getCode() == DisconnectCause.MISSED) {
683                 // Ensure when an incoming call is missed that the video state history is updated.
684                 mVideoStateHistory |= mVideoState;
685             }
686 
687             // Log the state transition event
688             String event = null;
689             Object data = null;
690             switch (newState) {
691                 case CallState.ACTIVE:
692                     event = Log.Events.SET_ACTIVE;
693                     break;
694                 case CallState.CONNECTING:
695                     event = Log.Events.SET_CONNECTING;
696                     break;
697                 case CallState.DIALING:
698                     event = Log.Events.SET_DIALING;
699                     break;
700                 case CallState.PULLING:
701                     event = Log.Events.SET_PULLING;
702                     break;
703                 case CallState.DISCONNECTED:
704                     event = Log.Events.SET_DISCONNECTED;
705                     data = getDisconnectCause();
706                     break;
707                 case CallState.DISCONNECTING:
708                     event = Log.Events.SET_DISCONNECTING;
709                     break;
710                 case CallState.ON_HOLD:
711                     event = Log.Events.SET_HOLD;
712                     break;
713                 case CallState.SELECT_PHONE_ACCOUNT:
714                     event = Log.Events.SET_SELECT_PHONE_ACCOUNT;
715                     break;
716                 case CallState.RINGING:
717                     event = Log.Events.SET_RINGING;
718                     break;
719             }
720             if (event != null) {
721                 // The string data should be just the tag.
722                 String stringData = tag;
723                 if (data != null) {
724                     // If data exists, add it to tag.  If no tag, just use data.toString().
725                     stringData = stringData == null ? data.toString() : stringData + "> " + data;
726                 }
727                 Log.event(this, event, stringData);
728             }
729         }
730     }
731 
setRingbackRequested(boolean ringbackRequested)732     void setRingbackRequested(boolean ringbackRequested) {
733         mRingbackRequested = ringbackRequested;
734         for (Listener l : mListeners) {
735             l.onRingbackRequested(this, mRingbackRequested);
736         }
737     }
738 
isRingbackRequested()739     boolean isRingbackRequested() {
740         return mRingbackRequested;
741     }
742 
743     @VisibleForTesting
isConference()744     public boolean isConference() {
745         return mIsConference;
746     }
747 
getHandle()748     public Uri getHandle() {
749         return mHandle;
750     }
751 
getPostDialDigits()752     public String getPostDialDigits() {
753         return mPostDialDigits;
754     }
755 
getViaNumber()756     public String getViaNumber() {
757         return mViaNumber;
758     }
759 
setViaNumber(String viaNumber)760     public void setViaNumber(String viaNumber) {
761         // If at any point the via number is not empty throughout the call, save that via number.
762         if (!TextUtils.isEmpty(viaNumber)) {
763             mViaNumber = viaNumber;
764         }
765     }
766 
getHandlePresentation()767     int getHandlePresentation() {
768         return mHandlePresentation;
769     }
770 
771 
setHandle(Uri handle)772     void setHandle(Uri handle) {
773         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
774     }
775 
setHandle(Uri handle, int presentation)776     public void setHandle(Uri handle, int presentation) {
777         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
778             mHandlePresentation = presentation;
779             if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
780                     mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
781                 mHandle = null;
782             } else {
783                 mHandle = handle;
784                 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
785                         && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
786                     // If the number is actually empty, set it to null, unless this is a
787                     // SCHEME_VOICEMAIL uri which always has an empty number.
788                     mHandle = null;
789                 }
790             }
791 
792             // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
793             // call, it will remain so for the rest of it's lifetime.
794             if (!mIsEmergencyCall) {
795                 mIsEmergencyCall = mHandle != null &&
796                         mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
797                                 mHandle.getSchemeSpecificPart());
798             }
799             startCallerInfoLookup();
800             for (Listener l : mListeners) {
801                 l.onHandleChanged(this);
802             }
803         }
804     }
805 
getCallerDisplayName()806     String getCallerDisplayName() {
807         return mCallerDisplayName;
808     }
809 
getCallerDisplayNamePresentation()810     int getCallerDisplayNamePresentation() {
811         return mCallerDisplayNamePresentation;
812     }
813 
setCallerDisplayName(String callerDisplayName, int presentation)814     void setCallerDisplayName(String callerDisplayName, int presentation) {
815         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
816                 presentation != mCallerDisplayNamePresentation) {
817             mCallerDisplayName = callerDisplayName;
818             mCallerDisplayNamePresentation = presentation;
819             for (Listener l : mListeners) {
820                 l.onCallerDisplayNameChanged(this);
821             }
822         }
823     }
824 
getName()825     public String getName() {
826         return mCallerInfo == null ? null : mCallerInfo.name;
827     }
828 
getPhoneNumber()829     public String getPhoneNumber() {
830         return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
831     }
832 
getPhotoIcon()833     public Bitmap getPhotoIcon() {
834         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
835     }
836 
getPhoto()837     public Drawable getPhoto() {
838         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
839     }
840 
841     /**
842      * @param disconnectCause The reason for the disconnection, represented by
843      *         {@link android.telecom.DisconnectCause}.
844      */
setDisconnectCause(DisconnectCause disconnectCause)845     public void setDisconnectCause(DisconnectCause disconnectCause) {
846         // TODO: Consider combining this method with a setDisconnected() method that is totally
847         // separate from setState.
848         mAnalytics.setCallDisconnectCause(disconnectCause);
849         mDisconnectCause = disconnectCause;
850     }
851 
getDisconnectCause()852     public DisconnectCause getDisconnectCause() {
853         return mDisconnectCause;
854     }
855 
856     @VisibleForTesting
isEmergencyCall()857     public boolean isEmergencyCall() {
858         return mIsEmergencyCall;
859     }
860 
861     /**
862      * @return The original handle this call is associated with. In-call services should use this
863      * handle when indicating in their UI the handle that is being called.
864      */
getOriginalHandle()865     public Uri getOriginalHandle() {
866         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
867             return mGatewayInfo.getOriginalAddress();
868         }
869         return getHandle();
870     }
871 
872     @VisibleForTesting
getGatewayInfo()873     public GatewayInfo getGatewayInfo() {
874         return mGatewayInfo;
875     }
876 
setGatewayInfo(GatewayInfo gatewayInfo)877     void setGatewayInfo(GatewayInfo gatewayInfo) {
878         mGatewayInfo = gatewayInfo;
879     }
880 
881     @VisibleForTesting
getConnectionManagerPhoneAccount()882     public PhoneAccountHandle getConnectionManagerPhoneAccount() {
883         return mConnectionManagerPhoneAccountHandle;
884     }
885 
886     @VisibleForTesting
setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle)887     public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
888         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
889             mConnectionManagerPhoneAccountHandle = accountHandle;
890             for (Listener l : mListeners) {
891                 l.onConnectionManagerPhoneAccountChanged(this);
892             }
893         }
894 
895     }
896 
897     @VisibleForTesting
getTargetPhoneAccount()898     public PhoneAccountHandle getTargetPhoneAccount() {
899         return mTargetPhoneAccountHandle;
900     }
901 
902     @VisibleForTesting
setTargetPhoneAccount(PhoneAccountHandle accountHandle)903     public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
904         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
905             mTargetPhoneAccountHandle = accountHandle;
906             for (Listener l : mListeners) {
907                 l.onTargetPhoneAccountChanged(this);
908             }
909             configureIsWorkCall();
910             checkIfVideoCapable();
911         }
912     }
913 
914     @VisibleForTesting
isIncoming()915     public boolean isIncoming() {
916         return mCallDirection == CALL_DIRECTION_INCOMING;
917     }
918 
isExternalCall()919     public boolean isExternalCall() {
920         return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
921                 Connection.PROPERTY_IS_EXTERNAL_CALL;
922     }
923 
isWorkCall()924     public boolean isWorkCall() {
925         return mIsWorkCall;
926     }
927 
isVideoCallingSupported()928     public boolean isVideoCallingSupported() {
929         return mIsVideoCallingSupported;
930     }
931 
configureIsWorkCall()932     private void configureIsWorkCall() {
933         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
934         boolean isWorkCall = false;
935         PhoneAccount phoneAccount =
936                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
937         if (phoneAccount != null) {
938             final UserHandle userHandle;
939             if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
940                 userHandle = mInitiatingUser;
941             } else {
942                 userHandle = mTargetPhoneAccountHandle.getUserHandle();
943             }
944             if (userHandle != null) {
945                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
946             }
947         }
948         mIsWorkCall = isWorkCall;
949     }
950 
951     /**
952      * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
953      * capability.
954      */
checkIfVideoCapable()955     private void checkIfVideoCapable() {
956         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
957         PhoneAccount phoneAccount =
958                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
959         mIsVideoCallingSupported = phoneAccount != null && phoneAccount.hasCapabilities(
960                     PhoneAccount.CAPABILITY_VIDEO_CALLING);
961     }
962 
shouldAttachToExistingConnection()963     boolean shouldAttachToExistingConnection() {
964         return mShouldAttachToExistingConnection;
965     }
966 
967     /**
968      * @return The "age" of this call object in milliseconds, which typically also represents the
969      *     period since this call was added to the set pending outgoing calls, see
970      *     mCreationTimeMillis.
971      */
972     @VisibleForTesting
getAgeMillis()973     public long getAgeMillis() {
974         if (mState == CallState.DISCONNECTED &&
975                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
976                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
977             // Rejected and missed calls have no age. They're immortal!!
978             return 0;
979         } else if (mConnectTimeMillis == 0) {
980             // Age is measured in the amount of time the call was active. A zero connect time
981             // indicates that we never went active, so return 0 for the age.
982             return 0;
983         } else if (mDisconnectTimeMillis == 0) {
984             // We connected, but have not yet disconnected
985             return System.currentTimeMillis() - mConnectTimeMillis;
986         }
987 
988         return mDisconnectTimeMillis - mConnectTimeMillis;
989     }
990 
991     /**
992      * @return The time when this call object was created and added to the set of pending outgoing
993      *     calls.
994      */
getCreationTimeMillis()995     public long getCreationTimeMillis() {
996         return mCreationTimeMillis;
997     }
998 
setCreationTimeMillis(long time)999     public void setCreationTimeMillis(long time) {
1000         mCreationTimeMillis = time;
1001     }
1002 
getConnectTimeMillis()1003     long getConnectTimeMillis() {
1004         return mConnectTimeMillis;
1005     }
1006 
getConnectionCapabilities()1007     int getConnectionCapabilities() {
1008         return mConnectionCapabilities;
1009     }
1010 
getConnectionProperties()1011     int getConnectionProperties() {
1012         return mConnectionProperties;
1013     }
1014 
setConnectionCapabilities(int connectionCapabilities)1015     void setConnectionCapabilities(int connectionCapabilities) {
1016         setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
1017     }
1018 
setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate)1019     void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
1020         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
1021                 connectionCapabilities));
1022         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
1023             // If the phone account does not support video calling, and the connection capabilities
1024             // passed in indicate that the call supports video, remove those video capabilities.
1025             if (!isVideoCallingSupported() && doesCallSupportVideo(connectionCapabilities)) {
1026                 Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
1027                         "capable when not supported by the phone account.");
1028                 connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
1029             }
1030 
1031             int previousCapabilities = mConnectionCapabilities;
1032             mConnectionCapabilities = connectionCapabilities;
1033             for (Listener l : mListeners) {
1034                 l.onConnectionCapabilitiesChanged(this);
1035             }
1036 
1037             int xorCaps = previousCapabilities ^ mConnectionCapabilities;
1038             Log.event(this, Log.Events.CAPABILITY_CHANGE,
1039                     "Current: [%s], Removed [%s], Added [%s]",
1040                     Connection.capabilitiesToStringShort(mConnectionCapabilities),
1041                     Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
1042                     Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
1043         }
1044     }
1045 
setConnectionProperties(int connectionProperties)1046     void setConnectionProperties(int connectionProperties) {
1047         Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
1048                 connectionProperties));
1049         if (mConnectionProperties != connectionProperties) {
1050             int previousProperties = mConnectionProperties;
1051             mConnectionProperties = connectionProperties;
1052             for (Listener l : mListeners) {
1053                 l.onConnectionPropertiesChanged(this);
1054             }
1055 
1056             boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1057                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
1058             boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1059                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
1060             if (wasExternal != isExternal) {
1061                 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
1062                         isExternal);
1063                 Log.event(this, Log.Events.IS_EXTERNAL, isExternal);
1064                 for (Listener l : mListeners) {
1065                     l.onExternalCallChanged(this, isExternal);
1066                 }
1067 
1068             }
1069 
1070             mAnalytics.addCallProperties(mConnectionProperties);
1071 
1072             int xorProps = previousProperties ^ mConnectionProperties;
1073             Log.event(this, Log.Events.PROPERTY_CHANGE,
1074                     "Current: [%s], Removed [%s], Added [%s]",
1075                     Connection.propertiesToStringShort(mConnectionProperties),
1076                     Connection.propertiesToStringShort(previousProperties & xorProps),
1077                     Connection.propertiesToStringShort(mConnectionProperties & xorProps));
1078         }
1079     }
1080 
getSupportedAudioRoutes()1081     public int getSupportedAudioRoutes() {
1082         return mSupportedAudioRoutes;
1083     }
1084 
setSupportedAudioRoutes(int audioRoutes)1085     void setSupportedAudioRoutes(int audioRoutes) {
1086         if (mSupportedAudioRoutes != audioRoutes) {
1087             mSupportedAudioRoutes = audioRoutes;
1088         }
1089     }
1090 
1091     @VisibleForTesting
getParentCall()1092     public Call getParentCall() {
1093         return mParentCall;
1094     }
1095 
1096     @VisibleForTesting
getChildCalls()1097     public List<Call> getChildCalls() {
1098         return mChildCalls;
1099     }
1100 
1101     @VisibleForTesting
wasConferencePreviouslyMerged()1102     public boolean wasConferencePreviouslyMerged() {
1103         return mWasConferencePreviouslyMerged;
1104     }
1105 
1106     @VisibleForTesting
getConferenceLevelActiveCall()1107     public Call getConferenceLevelActiveCall() {
1108         return mConferenceLevelActiveCall;
1109     }
1110 
1111     @VisibleForTesting
getConnectionService()1112     public ConnectionServiceWrapper getConnectionService() {
1113         return mConnectionService;
1114     }
1115 
1116     /**
1117      * Retrieves the {@link Context} for the call.
1118      *
1119      * @return The {@link Context}.
1120      */
getContext()1121     Context getContext() {
1122         return mContext;
1123     }
1124 
1125     @VisibleForTesting
setConnectionService(ConnectionServiceWrapper service)1126     public void setConnectionService(ConnectionServiceWrapper service) {
1127         Preconditions.checkNotNull(service);
1128 
1129         clearConnectionService();
1130 
1131         service.incrementAssociatedCallCount();
1132         mConnectionService = service;
1133         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1134         mConnectionService.addCall(this);
1135     }
1136 
1137     /**
1138      * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
1139      * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
1140      * ConnectionService is NOT unbound if the call count hits zero.
1141      * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
1142      * {@link Conference} additions via a ConnectionManager.
1143      * The original {@link android.telecom.ConnectionService} will directly add external calls and
1144      * conferences to Telecom as well as the ConnectionManager, which will add to Telecom.  In these
1145      * cases since its first added to via the original CS, we want to change the CS responsible for
1146      * the call to the ConnectionManager rather than adding it again as another call/conference.
1147      *
1148      * @param service The new {@link ConnectionServiceWrapper}.
1149      */
replaceConnectionService(ConnectionServiceWrapper service)1150     public void replaceConnectionService(ConnectionServiceWrapper service) {
1151         Preconditions.checkNotNull(service);
1152 
1153         if (mConnectionService != null) {
1154             ConnectionServiceWrapper serviceTemp = mConnectionService;
1155             mConnectionService = null;
1156             serviceTemp.removeCall(this);
1157             serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
1158         }
1159 
1160         service.incrementAssociatedCallCount();
1161         mConnectionService = service;
1162         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1163     }
1164 
1165     /**
1166      * Clears the associated connection service.
1167      */
clearConnectionService()1168     void clearConnectionService() {
1169         if (mConnectionService != null) {
1170             ConnectionServiceWrapper serviceTemp = mConnectionService;
1171             mConnectionService = null;
1172             serviceTemp.removeCall(this);
1173 
1174             // Decrementing the count can cause the service to unbind, which itself can trigger the
1175             // service-death code.  Since the service death code tries to clean up any associated
1176             // calls, we need to make sure to remove that information (e.g., removeCall()) before
1177             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
1178             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
1179             // to do.
1180             decrementAssociatedCallCount(serviceTemp);
1181         }
1182     }
1183 
1184     /**
1185      * Starts the create connection sequence. Upon completion, there should exist an active
1186      * connection through a connection service (or the call will have failed).
1187      *
1188      * @param phoneAccountRegistrar The phone account registrar.
1189      */
startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)1190     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
1191         if (mCreateConnectionProcessor != null) {
1192             Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
1193                     " due to a race between NewOutgoingCallIntentBroadcaster and " +
1194                     "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
1195                     "invocation.");
1196             return;
1197         }
1198         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
1199                 phoneAccountRegistrar, mContext);
1200         mCreateConnectionProcessor.process();
1201     }
1202 
1203     @Override
handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection)1204     public void handleCreateConnectionSuccess(
1205             CallIdMapper idMapper,
1206             ParcelableConnection connection) {
1207         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
1208         setTargetPhoneAccount(connection.getPhoneAccount());
1209         setHandle(connection.getHandle(), connection.getHandlePresentation());
1210         setCallerDisplayName(
1211                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
1212 
1213         setConnectionCapabilities(connection.getConnectionCapabilities());
1214         setConnectionProperties(connection.getConnectionProperties());
1215         setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
1216         setVideoProvider(connection.getVideoProvider());
1217         setVideoState(connection.getVideoState());
1218         setRingbackRequested(connection.isRingbackRequested());
1219         setIsVoipAudioMode(connection.getIsVoipAudioMode());
1220         setStatusHints(connection.getStatusHints());
1221         putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());
1222 
1223         mConferenceableCalls.clear();
1224         for (String id : connection.getConferenceableConnectionIds()) {
1225             mConferenceableCalls.add(idMapper.getCall(id));
1226         }
1227 
1228         switch (mCallDirection) {
1229             case CALL_DIRECTION_INCOMING:
1230                 // Listeners (just CallsManager for now) will be responsible for checking whether
1231                 // the call should be blocked.
1232                 for (Listener l : mListeners) {
1233                     l.onSuccessfulIncomingCall(this);
1234                 }
1235                 break;
1236             case CALL_DIRECTION_OUTGOING:
1237                 for (Listener l : mListeners) {
1238                     l.onSuccessfulOutgoingCall(this,
1239                             getStateFromConnectionState(connection.getState()));
1240                 }
1241                 break;
1242             case CALL_DIRECTION_UNKNOWN:
1243                 for (Listener l : mListeners) {
1244                     l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
1245                             .getState()));
1246                 }
1247                 break;
1248         }
1249     }
1250 
1251     @Override
handleCreateConnectionFailure(DisconnectCause disconnectCause)1252     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
1253         clearConnectionService();
1254         setDisconnectCause(disconnectCause);
1255         mCallsManager.markCallAsDisconnected(this, disconnectCause);
1256 
1257         switch (mCallDirection) {
1258             case CALL_DIRECTION_INCOMING:
1259                 for (Listener listener : mListeners) {
1260                     listener.onFailedIncomingCall(this);
1261                 }
1262                 break;
1263             case CALL_DIRECTION_OUTGOING:
1264                 for (Listener listener : mListeners) {
1265                     listener.onFailedOutgoingCall(this, disconnectCause);
1266                 }
1267                 break;
1268             case CALL_DIRECTION_UNKNOWN:
1269                 for (Listener listener : mListeners) {
1270                     listener.onFailedUnknownCall(this);
1271                 }
1272                 break;
1273         }
1274     }
1275 
1276     /**
1277      * Plays the specified DTMF tone.
1278      */
playDtmfTone(char digit)1279     void playDtmfTone(char digit) {
1280         if (mConnectionService == null) {
1281             Log.w(this, "playDtmfTone() request on a call without a connection service.");
1282         } else {
1283             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
1284             mConnectionService.playDtmfTone(this, digit);
1285             Log.event(this, Log.Events.START_DTMF, Log.pii(digit));
1286         }
1287     }
1288 
1289     /**
1290      * Stops playing any currently playing DTMF tone.
1291      */
stopDtmfTone()1292     void stopDtmfTone() {
1293         if (mConnectionService == null) {
1294             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
1295         } else {
1296             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
1297             Log.event(this, Log.Events.STOP_DTMF);
1298             mConnectionService.stopDtmfTone(this);
1299         }
1300     }
1301 
1302     /**
1303      * Silences the ringer.
1304      */
silence()1305     void silence() {
1306         if (mConnectionService == null) {
1307             Log.w(this, "silence() request on a call without a connection service.");
1308         } else {
1309             Log.i(this, "Send silence to connection service for call %s", this);
1310             Log.event(this, Log.Events.SILENCE);
1311             mConnectionService.silence(this);
1312         }
1313     }
1314 
1315     @VisibleForTesting
disconnect()1316     public void disconnect() {
1317         disconnect(0);
1318     }
1319 
1320     /**
1321      * Attempts to disconnect the call through the connection service.
1322      */
1323     @VisibleForTesting
disconnect(long disconnectionTimeout)1324     public void disconnect(long disconnectionTimeout) {
1325         Log.event(this, Log.Events.REQUEST_DISCONNECT);
1326 
1327         // Track that the call is now locally disconnecting.
1328         setLocallyDisconnecting(true);
1329 
1330         if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
1331                 mState == CallState.CONNECTING) {
1332             Log.v(this, "Aborting call %s", this);
1333             abort(disconnectionTimeout);
1334         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
1335             if (mConnectionService == null) {
1336                 Log.e(this, new Exception(), "disconnect() request on a call without a"
1337                         + " connection service.");
1338             } else {
1339                 Log.i(this, "Send disconnect to connection service for call: %s", this);
1340                 // The call isn't officially disconnected until the connection service
1341                 // confirms that the call was actually disconnected. Only then is the
1342                 // association between call and connection service severed, see
1343                 // {@link CallsManager#markCallAsDisconnected}.
1344                 mConnectionService.disconnect(this);
1345             }
1346         }
1347     }
1348 
abort(long disconnectionTimeout)1349     void abort(long disconnectionTimeout) {
1350         if (mCreateConnectionProcessor != null &&
1351                 !mCreateConnectionProcessor.isProcessingComplete()) {
1352             mCreateConnectionProcessor.abort();
1353         } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
1354                 || mState == CallState.CONNECTING) {
1355             if (disconnectionTimeout > 0) {
1356                 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
1357                 // milliseconds, do not destroy the call.
1358                 // Instead, we announce the cancellation and CallsManager handles
1359                 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
1360                 // then re-dial them quickly using a gateway, allowing the first call to end
1361                 // causes jank. This timeout allows CallsManager to transition the first call into
1362                 // the second call so that in-call only ever sees a single call...eliminating the
1363                 // jank altogether. The app will also be able to set the timeout via an extra on
1364                 // the ordered broadcast.
1365                 for (Listener listener : mListeners) {
1366                     if (listener.onCanceledViaNewOutgoingCallBroadcast(
1367                             this, disconnectionTimeout)) {
1368                         // The first listener to handle this wins. A return value of true means that
1369                         // the listener will handle the disconnection process later and so we
1370                         // should not continue it here.
1371                         setLocallyDisconnecting(false);
1372                         return;
1373                     }
1374                 }
1375             }
1376 
1377             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
1378         } else {
1379             Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
1380         }
1381     }
1382 
1383     /**
1384      * Answers the call if it is ringing.
1385      *
1386      * @param videoState The video state in which to answer the call.
1387      */
1388     @VisibleForTesting
answer(int videoState)1389     public void answer(int videoState) {
1390         // Check to verify that the call is still in the ringing state. A call can change states
1391         // between the time the user hits 'answer' and Telecom receives the command.
1392         if (isRinging("answer")) {
1393             if (!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) {
1394                 // Video calling is not supported, yet the InCallService is attempting to answer as
1395                 // video.  We will simply answer as audio-only.
1396                 videoState = VideoProfile.STATE_AUDIO_ONLY;
1397             }
1398             // At this point, we are asking the connection service to answer but we don't assume
1399             // that it will work. Instead, we wait until confirmation from the connectino service
1400             // that the call is in a non-STATE_RINGING state before changing the UI. See
1401             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1402             if (mConnectionService != null) {
1403                 mConnectionService.answer(this, videoState);
1404             } else {
1405                 Log.e(this, new NullPointerException(),
1406                         "answer call failed due to null CS callId=%s", getId());
1407             }
1408             Log.event(this, Log.Events.REQUEST_ACCEPT);
1409         }
1410     }
1411 
1412     /**
1413      * Rejects the call if it is ringing.
1414      *
1415      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1416      * @param textMessage An optional text message to send as part of the rejection.
1417      */
1418     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage)1419     public void reject(boolean rejectWithMessage, String textMessage) {
1420         // Check to verify that the call is still in the ringing state. A call can change states
1421         // between the time the user hits 'reject' and Telecomm receives the command.
1422         if (isRinging("reject")) {
1423             // Ensure video state history tracks video state at time of rejection.
1424             mVideoStateHistory |= mVideoState;
1425 
1426             if (mConnectionService != null) {
1427                 mConnectionService.reject(this, rejectWithMessage, textMessage);
1428             } else {
1429                 Log.e(this, new NullPointerException(),
1430                         "reject call failed due to null CS callId=%s", getId());
1431             }
1432             Log.event(this, Log.Events.REQUEST_REJECT);
1433 
1434         }
1435     }
1436 
1437     /**
1438      * Puts the call on hold if it is currently active.
1439      */
hold()1440     void hold() {
1441         if (mState == CallState.ACTIVE) {
1442             if (mConnectionService != null) {
1443                 mConnectionService.hold(this);
1444             } else {
1445                 Log.e(this, new NullPointerException(),
1446                         "hold call failed due to null CS callId=%s", getId());
1447             }
1448             Log.event(this, Log.Events.REQUEST_HOLD);
1449         }
1450     }
1451 
1452     /**
1453      * Releases the call from hold if it is currently active.
1454      */
unhold()1455     void unhold() {
1456         if (mState == CallState.ON_HOLD) {
1457             if (mConnectionService != null) {
1458                 mConnectionService.unhold(this);
1459             } else {
1460                 Log.e(this, new NullPointerException(),
1461                         "unhold call failed due to null CS callId=%s", getId());
1462             }
1463             Log.event(this, Log.Events.REQUEST_UNHOLD);
1464         }
1465     }
1466 
1467     /** Checks if this is a live call or not. */
1468     @VisibleForTesting
isAlive()1469     public boolean isAlive() {
1470         switch (mState) {
1471             case CallState.NEW:
1472             case CallState.RINGING:
1473             case CallState.DISCONNECTED:
1474             case CallState.ABORTED:
1475                 return false;
1476             default:
1477                 return true;
1478         }
1479     }
1480 
isActive()1481     boolean isActive() {
1482         return mState == CallState.ACTIVE;
1483     }
1484 
getExtras()1485     Bundle getExtras() {
1486         return mExtras;
1487     }
1488 
1489     /**
1490      * Adds extras to the extras bundle associated with this {@link Call}.
1491      *
1492      * Note: this method needs to know the source of the extras change (see
1493      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
1494      * originate from a connection service will only be notified to incall services.  Likewise,
1495      * changes originating from the incall services will only notify the connection service of the
1496      * change.
1497      *
1498      * @param source The source of the extras addition.
1499      * @param extras The extras.
1500      */
putExtras(int source, Bundle extras)1501     void putExtras(int source, Bundle extras) {
1502         if (extras == null) {
1503             return;
1504         }
1505         if (mExtras == null) {
1506             mExtras = new Bundle();
1507         }
1508         mExtras.putAll(extras);
1509 
1510         for (Listener l : mListeners) {
1511             l.onExtrasChanged(this, source, extras);
1512         }
1513 
1514         // If the change originated from an InCallService, notify the connection service.
1515         if (source == SOURCE_INCALL_SERVICE) {
1516             if (mConnectionService != null) {
1517                 mConnectionService.onExtrasChanged(this, mExtras);
1518             } else {
1519                 Log.e(this, new NullPointerException(),
1520                         "putExtras failed due to null CS callId=%s", getId());
1521             }
1522         }
1523     }
1524 
1525     /**
1526      * Removes extras from the extras bundle associated with this {@link Call}.
1527      *
1528      * Note: this method needs to know the source of the extras change (see
1529      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
1530      * originate from a connection service will only be notified to incall services.  Likewise,
1531      * changes originating from the incall services will only notify the connection service of the
1532      * change.
1533      *
1534      * @param source The source of the extras removal.
1535      * @param keys The extra keys to remove.
1536      */
removeExtras(int source, List<String> keys)1537     void removeExtras(int source, List<String> keys) {
1538         if (mExtras == null) {
1539             return;
1540         }
1541         for (String key : keys) {
1542             mExtras.remove(key);
1543         }
1544 
1545         for (Listener l : mListeners) {
1546             l.onExtrasRemoved(this, source, keys);
1547         }
1548 
1549         // If the change originated from an InCallService, notify the connection service.
1550         if (source == SOURCE_INCALL_SERVICE) {
1551             if (mConnectionService != null) {
1552                 mConnectionService.onExtrasChanged(this, mExtras);
1553             } else {
1554                 Log.e(this, new NullPointerException(),
1555                         "removeExtras failed due to null CS callId=%s", getId());
1556             }
1557         }
1558     }
1559 
1560     @VisibleForTesting
getIntentExtras()1561     public Bundle getIntentExtras() {
1562         return mIntentExtras;
1563     }
1564 
setIntentExtras(Bundle extras)1565     void setIntentExtras(Bundle extras) {
1566         mIntentExtras = extras;
1567     }
1568 
1569     /**
1570      * @return the uri of the contact associated with this call.
1571      */
1572     @VisibleForTesting
getContactUri()1573     public Uri getContactUri() {
1574         if (mCallerInfo == null || !mCallerInfo.contactExists) {
1575             return getHandle();
1576         }
1577         return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
1578     }
1579 
getRingtone()1580     Uri getRingtone() {
1581         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
1582     }
1583 
onPostDialWait(String remaining)1584     void onPostDialWait(String remaining) {
1585         for (Listener l : mListeners) {
1586             l.onPostDialWait(this, remaining);
1587         }
1588     }
1589 
onPostDialChar(char nextChar)1590     void onPostDialChar(char nextChar) {
1591         for (Listener l : mListeners) {
1592             l.onPostDialChar(this, nextChar);
1593         }
1594     }
1595 
postDialContinue(boolean proceed)1596     void postDialContinue(boolean proceed) {
1597         if (mConnectionService != null) {
1598             mConnectionService.onPostDialContinue(this, proceed);
1599         } else {
1600             Log.e(this, new NullPointerException(),
1601                     "postDialContinue failed due to null CS callId=%s", getId());
1602         }
1603     }
1604 
conferenceWith(Call otherCall)1605     void conferenceWith(Call otherCall) {
1606         if (mConnectionService == null) {
1607             Log.w(this, "conference requested on a call without a connection service.");
1608         } else {
1609             Log.event(this, Log.Events.CONFERENCE_WITH, otherCall);
1610             mConnectionService.conference(this, otherCall);
1611         }
1612     }
1613 
splitFromConference()1614     void splitFromConference() {
1615         if (mConnectionService == null) {
1616             Log.w(this, "splitting from conference call without a connection service");
1617         } else {
1618             Log.event(this, Log.Events.SPLIT_FROM_CONFERENCE);
1619             mConnectionService.splitFromConference(this);
1620         }
1621     }
1622 
1623     @VisibleForTesting
mergeConference()1624     public void mergeConference() {
1625         if (mConnectionService == null) {
1626             Log.w(this, "merging conference calls without a connection service.");
1627         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
1628             Log.event(this, Log.Events.CONFERENCE_WITH);
1629             mConnectionService.mergeConference(this);
1630             mWasConferencePreviouslyMerged = true;
1631         }
1632     }
1633 
1634     @VisibleForTesting
swapConference()1635     public void swapConference() {
1636         if (mConnectionService == null) {
1637             Log.w(this, "swapping conference calls without a connection service.");
1638         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
1639             Log.event(this, Log.Events.SWAP);
1640             mConnectionService.swapConference(this);
1641             switch (mChildCalls.size()) {
1642                 case 1:
1643                     mConferenceLevelActiveCall = mChildCalls.get(0);
1644                     break;
1645                 case 2:
1646                     // swap
1647                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
1648                             mChildCalls.get(1) : mChildCalls.get(0);
1649                     break;
1650                 default:
1651                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
1652                     mConferenceLevelActiveCall = null;
1653                     break;
1654             }
1655         }
1656     }
1657 
1658     /**
1659      * Initiates a request to the connection service to pull this call.
1660      * <p>
1661      * This method can only be used for calls that have the
1662      * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
1663      * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
1664      * <p>
1665      * An external call is a representation of a call which is taking place on another device
1666      * associated with a PhoneAccount on this device.  Issuing a request to pull the external call
1667      * tells the {@link android.telecom.ConnectionService} that it should move the call from the
1668      * other device to this one.  An example of this is the IMS multi-endpoint functionality.  A
1669      * user may have two phones with the same phone number.  If the user is engaged in an active
1670      * call on their first device, the network will inform the second device of that ongoing call in
1671      * the form of an external call.  The user may wish to continue their conversation on the second
1672      * device, so will issue a request to pull the call to the second device.
1673      * <p>
1674      * Requests to pull a call which is not external, or a call which is not pullable are ignored.
1675      */
pullExternalCall()1676     public void pullExternalCall() {
1677         if (mConnectionService == null) {
1678             Log.w(this, "pulling a call without a connection service.");
1679         }
1680 
1681         if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
1682             Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
1683             return;
1684         }
1685 
1686         if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
1687             Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
1688             return;
1689         }
1690 
1691         Log.event(this, Log.Events.REQUEST_PULL);
1692         mConnectionService.pullExternalCall(this);
1693     }
1694 
1695     /**
1696      * Sends a call event to the {@link ConnectionService} for this call.
1697      *
1698      * See {@link Call#sendCallEvent(String, Bundle)}.
1699      *
1700      * @param event The call event.
1701      * @param extras Associated extras.
1702      */
sendCallEvent(String event, Bundle extras)1703     public void sendCallEvent(String event, Bundle extras) {
1704         if (mConnectionService != null) {
1705             mConnectionService.sendCallEvent(this, event, extras);
1706         } else {
1707             Log.e(this, new NullPointerException(),
1708                     "sendCallEvent failed due to null CS callId=%s", getId());
1709         }
1710     }
1711 
setParentCall(Call parentCall)1712     void setParentCall(Call parentCall) {
1713         if (parentCall == this) {
1714             Log.e(this, new Exception(), "setting the parent to self");
1715             return;
1716         }
1717         if (parentCall == mParentCall) {
1718             // nothing to do
1719             return;
1720         }
1721         Preconditions.checkState(parentCall == null || mParentCall == null);
1722 
1723         Call oldParent = mParentCall;
1724         if (mParentCall != null) {
1725             mParentCall.removeChildCall(this);
1726         }
1727         mParentCall = parentCall;
1728         if (mParentCall != null) {
1729             mParentCall.addChildCall(this);
1730         }
1731 
1732         Log.event(this, Log.Events.SET_PARENT, mParentCall);
1733         for (Listener l : mListeners) {
1734             l.onParentChanged(this);
1735         }
1736     }
1737 
setConferenceableCalls(List<Call> conferenceableCalls)1738     void setConferenceableCalls(List<Call> conferenceableCalls) {
1739         mConferenceableCalls.clear();
1740         mConferenceableCalls.addAll(conferenceableCalls);
1741 
1742         for (Listener l : mListeners) {
1743             l.onConferenceableCallsChanged(this);
1744         }
1745     }
1746 
1747     @VisibleForTesting
getConferenceableCalls()1748     public List<Call> getConferenceableCalls() {
1749         return mConferenceableCalls;
1750     }
1751 
1752     @VisibleForTesting
can(int capability)1753     public boolean can(int capability) {
1754         return (mConnectionCapabilities & capability) == capability;
1755     }
1756 
1757     @VisibleForTesting
hasProperty(int property)1758     public boolean hasProperty(int property) {
1759         return (mConnectionProperties & property) == property;
1760     }
1761 
addChildCall(Call call)1762     private void addChildCall(Call call) {
1763         if (!mChildCalls.contains(call)) {
1764             // Set the pseudo-active call to the latest child added to the conference.
1765             // See definition of mConferenceLevelActiveCall for more detail.
1766             mConferenceLevelActiveCall = call;
1767             mChildCalls.add(call);
1768 
1769             Log.event(this, Log.Events.ADD_CHILD, call);
1770 
1771             for (Listener l : mListeners) {
1772                 l.onChildrenChanged(this);
1773             }
1774         }
1775     }
1776 
removeChildCall(Call call)1777     private void removeChildCall(Call call) {
1778         if (mChildCalls.remove(call)) {
1779             Log.event(this, Log.Events.REMOVE_CHILD, call);
1780             for (Listener l : mListeners) {
1781                 l.onChildrenChanged(this);
1782             }
1783         }
1784     }
1785 
1786     /**
1787      * Return whether the user can respond to this {@code Call} via an SMS message.
1788      *
1789      * @return true if the "Respond via SMS" feature should be enabled
1790      * for this incoming call.
1791      *
1792      * The general rule is that we *do* allow "Respond via SMS" except for
1793      * the few (relatively rare) cases where we know for sure it won't
1794      * work, namely:
1795      *   - a bogus or blank incoming number
1796      *   - a call from a SIP address
1797      *   - a "call presentation" that doesn't allow the number to be revealed
1798      *
1799      * In all other cases, we allow the user to respond via SMS.
1800      *
1801      * Note that this behavior isn't perfect; for example we have no way
1802      * to detect whether the incoming call is from a landline (with most
1803      * networks at least), so we still enable this feature even though
1804      * SMSes to that number will silently fail.
1805      */
isRespondViaSmsCapable()1806     boolean isRespondViaSmsCapable() {
1807         if (mState != CallState.RINGING) {
1808             return false;
1809         }
1810 
1811         if (getHandle() == null) {
1812             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
1813             // other words, the user should not be able to see the incoming phone number.
1814             return false;
1815         }
1816 
1817         if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
1818             // The incoming number is actually a URI (i.e. a SIP address),
1819             // not a regular PSTN phone number, and we can't send SMSes to
1820             // SIP addresses.
1821             // (TODO: That might still be possible eventually, though. Is
1822             // there some SIP-specific equivalent to sending a text message?)
1823             return false;
1824         }
1825 
1826         // Is there a valid SMS application on the phone?
1827         if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
1828                 true /*updateIfNeeded*/) == null) {
1829             return false;
1830         }
1831 
1832         // TODO: with some carriers (in certain countries) you *can* actually
1833         // tell whether a given number is a mobile phone or not. So in that
1834         // case we could potentially return false here if the incoming call is
1835         // from a land line.
1836 
1837         // If none of the above special cases apply, it's OK to enable the
1838         // "Respond via SMS" feature.
1839         return true;
1840     }
1841 
getCannedSmsResponses()1842     List<String> getCannedSmsResponses() {
1843         return mCannedSmsResponses;
1844     }
1845 
1846     /**
1847      * We need to make sure that before we move a call to the disconnected state, it no
1848      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
1849      * Service always has the right data in the right order.  We also want to do it in telecom so
1850      * that the insurance policy lives in the framework side of things.
1851      */
fixParentAfterDisconnect()1852     private void fixParentAfterDisconnect() {
1853         setParentCall(null);
1854     }
1855 
1856     /**
1857      * @return True if the call is ringing, else logs the action name.
1858      */
isRinging(String actionName)1859     private boolean isRinging(String actionName) {
1860         if (mState == CallState.RINGING) {
1861             return true;
1862         }
1863 
1864         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
1865         return false;
1866     }
1867 
1868     @SuppressWarnings("rawtypes")
decrementAssociatedCallCount(ServiceBinder binder)1869     private void decrementAssociatedCallCount(ServiceBinder binder) {
1870         if (binder != null) {
1871             binder.decrementAssociatedCallCount();
1872         }
1873     }
1874 
1875     /**
1876      * Looks up contact information based on the current handle.
1877      */
startCallerInfoLookup()1878     private void startCallerInfoLookup() {
1879         mCallerInfo = null;
1880         mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
1881     }
1882 
1883     /**
1884      * Saves the specified caller info if the specified token matches that of the last query
1885      * that was made.
1886      *
1887      * @param callerInfo The new caller information to set.
1888      */
setCallerInfo(Uri handle, CallerInfo callerInfo)1889     private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
1890         Trace.beginSection("setCallerInfo");
1891         if (callerInfo == null) {
1892             Log.i(this, "CallerInfo lookup returned null, skipping update");
1893             return;
1894         }
1895 
1896         if (!handle.equals(mHandle)) {
1897             Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
1898             return;
1899         }
1900 
1901         mCallerInfo = callerInfo;
1902         Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
1903 
1904         if (mCallerInfo.contactDisplayPhotoUri == null ||
1905                 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) {
1906             for (Listener l : mListeners) {
1907                 l.onCallerInfoChanged(this);
1908             }
1909         }
1910 
1911         Trace.endSection();
1912     }
1913 
getCallerInfo()1914     public CallerInfo getCallerInfo() {
1915         return mCallerInfo;
1916     }
1917 
maybeLoadCannedSmsResponses()1918     private void maybeLoadCannedSmsResponses() {
1919         if (mCallDirection == CALL_DIRECTION_INCOMING
1920                 && isRespondViaSmsCapable()
1921                 && !mCannedSmsResponsesLoadingStarted) {
1922             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
1923             mCannedSmsResponsesLoadingStarted = true;
1924             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
1925                     new Response<Void, List<String>>() {
1926                         @Override
1927                         public void onResult(Void request, List<String>... result) {
1928                             if (result.length > 0) {
1929                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
1930                                 mCannedSmsResponses = result[0];
1931                                 for (Listener l : mListeners) {
1932                                     l.onCannedSmsResponsesLoaded(Call.this);
1933                                 }
1934                             }
1935                         }
1936 
1937                         @Override
1938                         public void onError(Void request, int code, String msg) {
1939                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
1940                                     msg);
1941                         }
1942                     },
1943                     mContext
1944             );
1945         } else {
1946             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
1947         }
1948     }
1949 
1950     /**
1951      * Sets speakerphone option on when call begins.
1952      */
setStartWithSpeakerphoneOn(boolean startWithSpeakerphone)1953     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
1954         mSpeakerphoneOn = startWithSpeakerphone;
1955     }
1956 
1957     /**
1958      * Returns speakerphone option.
1959      *
1960      * @return Whether or not speakerphone should be set automatically when call begins.
1961      */
getStartWithSpeakerphoneOn()1962     public boolean getStartWithSpeakerphoneOn() {
1963         return mSpeakerphoneOn;
1964     }
1965 
1966     /**
1967      * Sets a video call provider for the call.
1968      */
setVideoProvider(IVideoProvider videoProvider)1969     public void setVideoProvider(IVideoProvider videoProvider) {
1970         Log.v(this, "setVideoProvider");
1971 
1972         if (videoProvider != null ) {
1973             try {
1974                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this);
1975             } catch (RemoteException ignored) {
1976                 // Ignore RemoteException.
1977             }
1978         } else {
1979             mVideoProviderProxy = null;
1980         }
1981 
1982         mVideoProvider = videoProvider;
1983 
1984         for (Listener l : mListeners) {
1985             l.onVideoCallProviderChanged(Call.this);
1986         }
1987     }
1988 
1989     /**
1990      * @return The {@link Connection.VideoProvider} binder.
1991      */
getVideoProvider()1992     public IVideoProvider getVideoProvider() {
1993         if (mVideoProviderProxy == null) {
1994             return null;
1995         }
1996 
1997         return mVideoProviderProxy.getInterface();
1998     }
1999 
2000     /**
2001      * @return The {@link VideoProviderProxy} for this call.
2002      */
getVideoProviderProxy()2003     public VideoProviderProxy getVideoProviderProxy() {
2004         return mVideoProviderProxy;
2005     }
2006 
2007     /**
2008      * The current video state for the call.
2009      * See {@link VideoProfile} for a list of valid video states.
2010      */
getVideoState()2011     public int getVideoState() {
2012         return mVideoState;
2013     }
2014 
2015     /**
2016      * Returns the video states which were applicable over the duration of a call.
2017      * See {@link VideoProfile} for a list of valid video states.
2018      *
2019      * @return The video states applicable over the duration of the call.
2020      */
getVideoStateHistory()2021     public int getVideoStateHistory() {
2022         return mVideoStateHistory;
2023     }
2024 
2025     /**
2026      * Determines the current video state for the call.
2027      * For an outgoing call determines the desired video state for the call.
2028      * Valid values: see {@link VideoProfile}
2029      *
2030      * @param videoState The video state for the call.
2031      */
setVideoState(int videoState)2032     public void setVideoState(int videoState) {
2033         // If the phone account associated with this call does not support video calling, then we
2034         // will automatically set the video state to audio-only.
2035         if (!isVideoCallingSupported()) {
2036             videoState = VideoProfile.STATE_AUDIO_ONLY;
2037         }
2038 
2039         // Track which video states were applicable over the duration of the call.
2040         // Only track the call state when the call is active or disconnected.  This ensures we do
2041         // not include the video state when:
2042         // - Call is incoming (but not answered).
2043         // - Call it outgoing (but not answered).
2044         // We include the video state when disconnected to ensure that rejected calls reflect the
2045         // appropriate video state.
2046         if (isActive() || getState() == CallState.DISCONNECTED) {
2047             mVideoStateHistory = mVideoStateHistory | videoState;
2048         }
2049 
2050         int previousVideoState = mVideoState;
2051         mVideoState = videoState;
2052         if (mVideoState != previousVideoState) {
2053             Log.event(this, Log.Events.VIDEO_STATE_CHANGED,
2054                     VideoProfile.videoStateToString(videoState));
2055             for (Listener l : mListeners) {
2056                 l.onVideoStateChanged(this, previousVideoState, mVideoState);
2057             }
2058         }
2059 
2060         if (VideoProfile.isVideo(videoState)) {
2061             mAnalytics.setCallIsVideo(true);
2062         }
2063     }
2064 
getIsVoipAudioMode()2065     public boolean getIsVoipAudioMode() {
2066         return mIsVoipAudioMode;
2067     }
2068 
setIsVoipAudioMode(boolean audioModeIsVoip)2069     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
2070         mIsVoipAudioMode = audioModeIsVoip;
2071         for (Listener l : mListeners) {
2072             l.onIsVoipAudioModeChanged(this);
2073         }
2074     }
2075 
getStatusHints()2076     public StatusHints getStatusHints() {
2077         return mStatusHints;
2078     }
2079 
setStatusHints(StatusHints statusHints)2080     public void setStatusHints(StatusHints statusHints) {
2081         mStatusHints = statusHints;
2082         for (Listener l : mListeners) {
2083             l.onStatusHintsChanged(this);
2084         }
2085     }
2086 
isUnknown()2087     public boolean isUnknown() {
2088         return mCallDirection == CALL_DIRECTION_UNKNOWN;
2089     }
2090 
2091     /**
2092      * Determines if this call is in a disconnecting state.
2093      *
2094      * @return {@code true} if this call is locally disconnecting.
2095      */
isLocallyDisconnecting()2096     public boolean isLocallyDisconnecting() {
2097         return mIsLocallyDisconnecting;
2098     }
2099 
2100     /**
2101      * Sets whether this call is in a disconnecting state.
2102      *
2103      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
2104      */
setLocallyDisconnecting(boolean isLocallyDisconnecting)2105     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
2106         mIsLocallyDisconnecting = isLocallyDisconnecting;
2107     }
2108 
2109     /**
2110      * @return user handle of user initiating the outgoing call.
2111      */
getInitiatingUser()2112     public UserHandle getInitiatingUser() {
2113         return mInitiatingUser;
2114     }
2115 
2116     /**
2117      * Set the user handle of user initiating the outgoing call.
2118      * @param initiatingUser
2119      */
setInitiatingUser(UserHandle initiatingUser)2120     public void setInitiatingUser(UserHandle initiatingUser) {
2121         Preconditions.checkNotNull(initiatingUser);
2122         mInitiatingUser = initiatingUser;
2123     }
2124 
getStateFromConnectionState(int state)2125     static int getStateFromConnectionState(int state) {
2126         switch (state) {
2127             case Connection.STATE_INITIALIZING:
2128                 return CallState.CONNECTING;
2129             case Connection.STATE_ACTIVE:
2130                 return CallState.ACTIVE;
2131             case Connection.STATE_DIALING:
2132                 return CallState.DIALING;
2133             case Connection.STATE_PULLING_CALL:
2134                 return CallState.PULLING;
2135             case Connection.STATE_DISCONNECTED:
2136                 return CallState.DISCONNECTED;
2137             case Connection.STATE_HOLDING:
2138                 return CallState.ON_HOLD;
2139             case Connection.STATE_NEW:
2140                 return CallState.NEW;
2141             case Connection.STATE_RINGING:
2142                 return CallState.RINGING;
2143         }
2144         return CallState.DISCONNECTED;
2145     }
2146 
2147     /**
2148      * Determines if this call is in disconnected state and waiting to be destroyed.
2149      *
2150      * @return {@code true} if this call is disconected.
2151      */
isDisconnected()2152     public boolean isDisconnected() {
2153         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
2154     }
2155 
2156     /**
2157      * Determines if this call has just been created and has not been configured properly yet.
2158      *
2159      * @return {@code true} if this call is new.
2160      */
isNew()2161     public boolean isNew() {
2162         return getState() == CallState.NEW;
2163     }
2164 
2165     /**
2166      * Sets the call data usage for the call.
2167      *
2168      * @param callDataUsage The new call data usage (in bytes).
2169      */
setCallDataUsage(long callDataUsage)2170     public void setCallDataUsage(long callDataUsage) {
2171         mCallDataUsage = callDataUsage;
2172     }
2173 
2174     /**
2175      * Returns the call data usage for the call.
2176      *
2177      * @return The call data usage (in bytes).
2178      */
getCallDataUsage()2179     public long getCallDataUsage() {
2180         return mCallDataUsage;
2181     }
2182 
2183     /**
2184      * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
2185      * has come back to telecom and was processed.
2186      */
isNewOutgoingCallIntentBroadcastDone()2187     public boolean isNewOutgoingCallIntentBroadcastDone() {
2188         return mIsNewOutgoingCallIntentBroadcastDone;
2189     }
2190 
setNewOutgoingCallIntentBroadcastIsDone()2191     public void setNewOutgoingCallIntentBroadcastIsDone() {
2192         mIsNewOutgoingCallIntentBroadcastDone = true;
2193     }
2194 
2195     /**
2196      * Determines if the call has been held by the remote party.
2197      *
2198      * @return {@code true} if the call is remotely held, {@code false} otherwise.
2199      */
isRemotelyHeld()2200     public boolean isRemotelyHeld() {
2201         return mIsRemotelyHeld;
2202     }
2203 
2204     /**
2205      * Handles Connection events received from a {@link ConnectionService}.
2206      *
2207      * @param event The event.
2208      * @param extras The extras.
2209      */
onConnectionEvent(String event, Bundle extras)2210     public void onConnectionEvent(String event, Bundle extras) {
2211         Log.event(this, Log.Events.CONNECTION_EVENT, event);
2212         if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
2213             mIsRemotelyHeld = true;
2214             Log.event(this, Log.Events.REMOTELY_HELD);
2215             // Inform listeners of the fact that a call hold tone was received.  This will trigger
2216             // the CallAudioManager to play a tone via the InCallTonePlayer.
2217             for (Listener l : mListeners) {
2218                 l.onHoldToneRequested(this);
2219             }
2220         } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
2221             mIsRemotelyHeld = false;
2222             Log.event(this, Log.Events.REMOTELY_UNHELD);
2223             for (Listener l : mListeners) {
2224                 l.onHoldToneRequested(this);
2225             }
2226         } else {
2227             for (Listener l : mListeners) {
2228                 l.onConnectionEvent(this, event, extras);
2229             }
2230         }
2231     }
2232 
setOriginalConnectionId(String originalConnectionId)2233     public void setOriginalConnectionId(String originalConnectionId) {
2234         mOriginalConnectionId = originalConnectionId;
2235     }
2236 
2237     /**
2238      * For calls added via a ConnectionManager using the
2239      * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
2240      * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
2241      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
2242      * originally created it.
2243      *
2244      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
2245      * @return The original connection ID.
2246      */
getOriginalConnectionId()2247     public String getOriginalConnectionId() {
2248         return mOriginalConnectionId;
2249     }
2250 
2251     /**
2252      * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
2253      * remotely or locally.
2254      *
2255      * @param capabilities The {@link Connection} capabilities for the call.
2256      * @return {@code true} if video is supported, {@code false} otherwise.
2257      */
doesCallSupportVideo(int capabilities)2258     private boolean doesCallSupportVideo(int capabilities) {
2259         return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
2260                 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
2261     }
2262 
2263     /**
2264      * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
2265      *
2266      * @param capabilities The capabilities.
2267      * @return The bitmask with video capabilities removed.
2268      */
removeVideoCapabilities(int capabilities)2269     private int removeVideoCapabilities(int capabilities) {
2270         return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
2271                 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
2272     }
2273 }
2274