• 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.content.Intent;
21 import android.graphics.Bitmap;
22 import android.graphics.drawable.Drawable;
23 import android.net.Uri;
24 import android.os.Build;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.os.ParcelFileDescriptor;
29 import android.os.Parcelable;
30 import android.os.RemoteException;
31 import android.os.SystemClock;
32 import android.os.Trace;
33 import android.provider.ContactsContract.Contacts;
34 import android.telecom.CallAudioState;
35 import android.telecom.Conference;
36 import android.telecom.ConnectionService;
37 import android.telecom.DisconnectCause;
38 import android.telecom.Connection;
39 import android.telecom.GatewayInfo;
40 import android.telecom.Log;
41 import android.telecom.Logging.EventManager;
42 import android.telecom.ParcelableConnection;
43 import android.telecom.PhoneAccount;
44 import android.telecom.PhoneAccountHandle;
45 import android.telecom.Response;
46 import android.telecom.StatusHints;
47 import android.telecom.TelecomManager;
48 import android.telecom.VideoProfile;
49 import android.telephony.PhoneNumberUtils;
50 import android.text.TextUtils;
51 import android.util.StatsLog;
52 import android.os.UserHandle;
53 import android.widget.Toast;
54 
55 import com.android.internal.annotations.VisibleForTesting;
56 import com.android.internal.telecom.IVideoProvider;
57 import com.android.internal.telephony.CallerInfo;
58 import com.android.internal.telephony.SmsApplication;
59 import com.android.internal.util.Preconditions;
60 
61 import java.io.IOException;
62 import java.lang.String;
63 import java.text.SimpleDateFormat;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.Date;
67 import java.util.LinkedList;
68 import java.util.List;
69 import java.util.Locale;
70 import java.util.Objects;
71 import java.util.Set;
72 import java.util.concurrent.ConcurrentHashMap;
73 
74 /**
75  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
76  *  from the time the call intent was received by Telecom (vs. the time the call was
77  *  connected etc).
78  */
79 @VisibleForTesting
80 public class Call implements CreateConnectionResponse, EventManager.Loggable,
81         ConnectionServiceFocusManager.CallFocus {
82     public final static String CALL_ID_UNKNOWN = "-1";
83     public final static long DATA_USAGE_NOT_SET = -1;
84 
85     public static final int CALL_DIRECTION_UNDEFINED = 0;
86     public static final int CALL_DIRECTION_OUTGOING = 1;
87     public static final int CALL_DIRECTION_INCOMING = 2;
88     public static final int CALL_DIRECTION_UNKNOWN = 3;
89 
90     /** Identifies extras changes which originated from a connection service. */
91     public static final int SOURCE_CONNECTION_SERVICE = 1;
92     /** Identifies extras changes which originated from an incall service. */
93     public static final int SOURCE_INCALL_SERVICE = 2;
94 
95     private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
96     private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
97 
98     private static final int INVALID_RTT_REQUEST_ID = -1;
99 
100     private static final char NO_DTMF_TONE = '\0';
101 
102     /**
103      * Listener for events on the call.
104      */
105     @VisibleForTesting
106     public interface Listener {
onSuccessfulOutgoingCall(Call call, int callState)107         void onSuccessfulOutgoingCall(Call call, int callState);
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)108         void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause);
onSuccessfulIncomingCall(Call call)109         void onSuccessfulIncomingCall(Call call);
onFailedIncomingCall(Call call)110         void onFailedIncomingCall(Call call);
onSuccessfulUnknownCall(Call call, int callState)111         void onSuccessfulUnknownCall(Call call, int callState);
onFailedUnknownCall(Call call)112         void onFailedUnknownCall(Call call);
onRingbackRequested(Call call, boolean ringbackRequested)113         void onRingbackRequested(Call call, boolean ringbackRequested);
onPostDialWait(Call call, String remaining)114         void onPostDialWait(Call call, String remaining);
onPostDialChar(Call call, char nextChar)115         void onPostDialChar(Call call, char nextChar);
onConnectionCapabilitiesChanged(Call call)116         void onConnectionCapabilitiesChanged(Call call);
onConnectionPropertiesChanged(Call call, boolean didRttChange)117         void onConnectionPropertiesChanged(Call call, boolean didRttChange);
onParentChanged(Call call)118         void onParentChanged(Call call);
onChildrenChanged(Call call)119         void onChildrenChanged(Call call);
onCannedSmsResponsesLoaded(Call call)120         void onCannedSmsResponsesLoaded(Call call);
onVideoCallProviderChanged(Call call)121         void onVideoCallProviderChanged(Call call);
onCallerInfoChanged(Call call)122         void onCallerInfoChanged(Call call);
onIsVoipAudioModeChanged(Call call)123         void onIsVoipAudioModeChanged(Call call);
onStatusHintsChanged(Call call)124         void onStatusHintsChanged(Call call);
onExtrasChanged(Call c, int source, Bundle extras)125         void onExtrasChanged(Call c, int source, Bundle extras);
onExtrasRemoved(Call c, int source, List<String> keys)126         void onExtrasRemoved(Call c, int source, List<String> keys);
onHandleChanged(Call call)127         void onHandleChanged(Call call);
onCallerDisplayNameChanged(Call call)128         void onCallerDisplayNameChanged(Call call);
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)129         void onVideoStateChanged(Call call, int previousVideoState, int newVideoState);
onTargetPhoneAccountChanged(Call call)130         void onTargetPhoneAccountChanged(Call call);
onConnectionManagerPhoneAccountChanged(Call call)131         void onConnectionManagerPhoneAccountChanged(Call call);
onPhoneAccountChanged(Call call)132         void onPhoneAccountChanged(Call call);
onConferenceableCallsChanged(Call call)133         void onConferenceableCallsChanged(Call call);
onConferenceStateChanged(Call call, boolean isConference)134         void onConferenceStateChanged(Call call, boolean isConference);
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)135         boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout);
onHoldToneRequested(Call call)136         void onHoldToneRequested(Call call);
onCallHoldFailed(Call call)137         void onCallHoldFailed(Call call);
onConnectionEvent(Call call, String event, Bundle extras)138         void onConnectionEvent(Call call, String event, Bundle extras);
onExternalCallChanged(Call call, boolean isExternalCall)139         void onExternalCallChanged(Call call, boolean isExternalCall);
onRttInitiationFailure(Call call, int reason)140         void onRttInitiationFailure(Call call, int reason);
onRemoteRttRequest(Call call, int requestId)141         void onRemoteRttRequest(Call call, int requestId);
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)142         void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
143                                  Bundle extras, boolean isLegacy);
onHandoverFailed(Call call, int error)144         void onHandoverFailed(Call call, int error);
onHandoverComplete(Call call)145         void onHandoverComplete(Call call);
146     }
147 
148     public abstract static class ListenerBase implements Listener {
149         @Override
onSuccessfulOutgoingCall(Call call, int callState)150         public void onSuccessfulOutgoingCall(Call call, int callState) {}
151         @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)152         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
153         @Override
onSuccessfulIncomingCall(Call call)154         public void onSuccessfulIncomingCall(Call call) {}
155         @Override
onFailedIncomingCall(Call call)156         public void onFailedIncomingCall(Call call) {}
157         @Override
onSuccessfulUnknownCall(Call call, int callState)158         public void onSuccessfulUnknownCall(Call call, int callState) {}
159         @Override
onFailedUnknownCall(Call call)160         public void onFailedUnknownCall(Call call) {}
161         @Override
onRingbackRequested(Call call, boolean ringbackRequested)162         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
163         @Override
onPostDialWait(Call call, String remaining)164         public void onPostDialWait(Call call, String remaining) {}
165         @Override
onPostDialChar(Call call, char nextChar)166         public void onPostDialChar(Call call, char nextChar) {}
167         @Override
onConnectionCapabilitiesChanged(Call call)168         public void onConnectionCapabilitiesChanged(Call call) {}
169         @Override
onConnectionPropertiesChanged(Call call, boolean didRttChange)170         public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}
171         @Override
onParentChanged(Call call)172         public void onParentChanged(Call call) {}
173         @Override
onChildrenChanged(Call call)174         public void onChildrenChanged(Call call) {}
175         @Override
onCannedSmsResponsesLoaded(Call call)176         public void onCannedSmsResponsesLoaded(Call call) {}
177         @Override
onVideoCallProviderChanged(Call call)178         public void onVideoCallProviderChanged(Call call) {}
179         @Override
onCallerInfoChanged(Call call)180         public void onCallerInfoChanged(Call call) {}
181         @Override
onIsVoipAudioModeChanged(Call call)182         public void onIsVoipAudioModeChanged(Call call) {}
183         @Override
onStatusHintsChanged(Call call)184         public void onStatusHintsChanged(Call call) {}
185         @Override
onExtrasChanged(Call c, int source, Bundle extras)186         public void onExtrasChanged(Call c, int source, Bundle extras) {}
187         @Override
onExtrasRemoved(Call c, int source, List<String> keys)188         public void onExtrasRemoved(Call c, int source, List<String> keys) {}
189         @Override
onHandleChanged(Call call)190         public void onHandleChanged(Call call) {}
191         @Override
onCallerDisplayNameChanged(Call call)192         public void onCallerDisplayNameChanged(Call call) {}
193         @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)194         public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}
195         @Override
onTargetPhoneAccountChanged(Call call)196         public void onTargetPhoneAccountChanged(Call call) {}
197         @Override
onConnectionManagerPhoneAccountChanged(Call call)198         public void onConnectionManagerPhoneAccountChanged(Call call) {}
199         @Override
onPhoneAccountChanged(Call call)200         public void onPhoneAccountChanged(Call call) {}
201         @Override
onConferenceableCallsChanged(Call call)202         public void onConferenceableCallsChanged(Call call) {}
203         @Override
onConferenceStateChanged(Call call, boolean isConference)204         public void onConferenceStateChanged(Call call, boolean isConference) {}
205         @Override
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)206         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
207             return false;
208         }
209         @Override
onHoldToneRequested(Call call)210         public void onHoldToneRequested(Call call) {}
211         @Override
onCallHoldFailed(Call call)212         public void onCallHoldFailed(Call call) {}
213         @Override
onConnectionEvent(Call call, String event, Bundle extras)214         public void onConnectionEvent(Call call, String event, Bundle extras) {}
215         @Override
onExternalCallChanged(Call call, boolean isExternalCall)216         public void onExternalCallChanged(Call call, boolean isExternalCall) {}
217         @Override
onRttInitiationFailure(Call call, int reason)218         public void onRttInitiationFailure(Call call, int reason) {}
219         @Override
onRemoteRttRequest(Call call, int requestId)220         public void onRemoteRttRequest(Call call, int requestId) {}
221         @Override
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)222         public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
223                                         Bundle extras, boolean isLegacy) {}
224         @Override
onHandoverFailed(Call call, int error)225         public void onHandoverFailed(Call call, int error) {}
226         @Override
onHandoverComplete(Call call)227         public void onHandoverComplete(Call call) {}
228     }
229 
230     private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
231             new CallerInfoLookupHelper.OnQueryCompleteListener() {
232                 /** ${inheritDoc} */
233                 @Override
234                 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) {
235                     synchronized (mLock) {
236                         Call.this.setCallerInfo(handle, callerInfo);
237                     }
238                 }
239 
240                 @Override
241                 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) {
242                     synchronized (mLock) {
243                         Call.this.setCallerInfo(handle, callerInfo);
244                     }
245                 }
246             };
247 
248     /**
249      * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
250      */
251     private final int mCallDirection;
252 
253     /**
254      * The post-dial digits that were dialed after the network portion of the number
255      */
256     private String mPostDialDigits;
257 
258     /**
259      * The secondary line number that an incoming call has been received on if the SIM subscription
260      * has multiple associated numbers.
261      */
262     private String mViaNumber = "";
263 
264     /**
265      * The wall clock time this call was created. Beyond logging and such, may also be used for
266      * bookkeeping and specifically for marking certain call attempts as failed attempts.
267      * Note: This timestamp should NOT be used for calculating call duration.
268      */
269     private long mCreationTimeMillis;
270 
271     /** The time this call was made active. */
272     private long mConnectTimeMillis = 0;
273 
274     /**
275      * The time, in millis, since boot when this call was connected.  This should ONLY be used when
276      * calculating the duration of the call.
277      *
278      * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the
279      * elapsed time since the device was booted.  Changes to the system clock (e.g. due to NITZ
280      * time sync, time zone changes user initiated clock changes) would cause a duration calculated
281      * based on {@link #mConnectTimeMillis} to change based on the delta in the time.
282      * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do
283      * not impact the call duration.
284      */
285     private long mConnectElapsedTimeMillis = 0;
286 
287     /** The wall clock time this call was disconnected. */
288     private long mDisconnectTimeMillis = 0;
289 
290     /**
291      * The elapsed time since boot when this call was disconnected.  Recorded as the
292      * {@link SystemClock#elapsedRealtime()}.  This ensures that the call duration is not impacted
293      * by changes in the wall time clock.
294      */
295     private long mDisconnectElapsedTimeMillis = 0;
296 
297     /** The gateway information associated with this call. This stores the original call handle
298      * that the user is attempting to connect to via the gateway, the actual handle to dial in
299      * order to connect the call via the gateway, as well as the package name of the gateway
300      * service. */
301     private GatewayInfo mGatewayInfo;
302 
303     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
304 
305     private PhoneAccountHandle mTargetPhoneAccountHandle;
306 
307     private UserHandle mInitiatingUser;
308 
309     private final Handler mHandler = new Handler(Looper.getMainLooper());
310 
311     private final List<Call> mConferenceableCalls = new ArrayList<>();
312 
313     /** The state of the call. */
314     private int mState;
315 
316     /** The handle with which to establish this call. */
317     private Uri mHandle;
318 
319     /**
320      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
321      */
322     private int mHandlePresentation;
323 
324     /** The caller display name (CNAP) set by the connection service. */
325     private String mCallerDisplayName;
326 
327     /**
328      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
329      */
330     private int mCallerDisplayNamePresentation;
331 
332     /**
333      * The connection service which is attempted or already connecting this call.
334      */
335     private ConnectionServiceWrapper mConnectionService;
336 
337     private boolean mIsEmergencyCall;
338 
339     private boolean mSpeakerphoneOn;
340 
341     private boolean mIsDisconnectingChildCall = false;
342 
343     /**
344      * Tracks the video states which were applicable over the duration of a call.
345      * See {@link VideoProfile} for a list of valid video states.
346      * <p>
347      * Video state history is tracked when the call is active, and when a call is rejected or
348      * missed.
349      */
350     private int mVideoStateHistory;
351 
352     private int mVideoState;
353 
354     /**
355      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
356      * See {@link android.telecom.DisconnectCause}.
357      */
358     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
359 
360     private Bundle mIntentExtras = new Bundle();
361 
362     /**
363      * The {@link Intent} which originally created this call.  Only populated when we are putting a
364      * call into a pending state and need to pick up initiation of the call later.
365      */
366     private Intent mOriginalCallIntent = null;
367 
368     /** Set of listeners on this call.
369      *
370      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
371      * load factor before resizing, 1 means we only expect a single thread to
372      * access the map so make only a single shard
373      */
374     private final Set<Listener> mListeners = Collections.newSetFromMap(
375             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
376 
377     private CreateConnectionProcessor mCreateConnectionProcessor;
378 
379     /** Caller information retrieved from the latest contact query. */
380     private CallerInfo mCallerInfo;
381 
382     /** The latest token used with a contact info query. */
383     private int mQueryToken = 0;
384 
385     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
386     private boolean mRingbackRequested = false;
387 
388     /** Whether this call is requesting to be silently ringing. */
389     private boolean mSilentRingingRequested = false;
390 
391     /** Whether direct-to-voicemail query is pending. */
392     private boolean mDirectToVoicemailQueryPending;
393 
394     private int mConnectionCapabilities;
395 
396     private int mConnectionProperties;
397 
398     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
399 
400     private boolean mIsConference = false;
401 
402     private boolean mHadChildren = false;
403 
404     private final boolean mShouldAttachToExistingConnection;
405 
406     private Call mParentCall = null;
407 
408     private List<Call> mChildCalls = new LinkedList<>();
409 
410     /** Set of text message responses allowed for this call, if applicable. */
411     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
412 
413     /** Whether an attempt has been made to load the text message responses. */
414     private boolean mCannedSmsResponsesLoadingStarted = false;
415 
416     private IVideoProvider mVideoProvider;
417     private VideoProviderProxy mVideoProviderProxy;
418 
419     private boolean mIsVoipAudioMode;
420     private StatusHints mStatusHints;
421     private Bundle mExtras;
422     private final ConnectionServiceRepository mRepository;
423     private final Context mContext;
424     private final CallsManager mCallsManager;
425     private final ClockProxy mClockProxy;
426     private final TelecomSystem.SyncRoot mLock;
427     private final String mId;
428     private String mConnectionId;
429     private Analytics.CallInfo mAnalytics = new Analytics.CallInfo();
430     private char mPlayingDtmfTone;
431 
432     private boolean mWasConferencePreviouslyMerged = false;
433     private boolean mWasHighDefAudio = false;
434 
435     // For conferences which support merge/swap at their level, we retain a notion of an active
436     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
437     // the notion of the current "active" call within the conference call. This maintains the
438     // "active" call and switches every time the user hits "swap".
439     private Call mConferenceLevelActiveCall = null;
440 
441     private boolean mIsLocallyDisconnecting = false;
442 
443     /**
444      * Tracks the current call data usage as reported by the video provider.
445      */
446     private long mCallDataUsage = DATA_USAGE_NOT_SET;
447 
448     private boolean mIsWorkCall;
449 
450     /**
451      * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
452      * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
453      */
454     private boolean mUseCallRecordingTone;
455 
456     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
457     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
458 
459     /**
460      * Indicates whether the call is remotely held.  A call is considered remotely held when
461      * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
462      * event.
463      */
464     private boolean mIsRemotelyHeld = false;
465 
466     /**
467      * Indicates whether the {@link PhoneAccount} associated with this call is self-managed.
468      * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED} for more information.
469      */
470     private boolean mIsSelfManaged = false;
471 
472     /**
473      * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
474      * {@code True} if the phone account supports video calling, {@code false} otherwise.
475      */
476     private boolean mIsVideoCallingSupportedByPhoneAccount = false;
477 
478     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
479 
480     /**
481      * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
482      * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
483      * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
484      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
485      * originally created it.
486      *
487      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
488      */
489     private String mOriginalConnectionId;
490 
491     /**
492      * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication
493      * between the in-call app and the connection service. If both non-null, this call should be
494      * treated as an RTT call.
495      * Each array should be of size 2. First one is the read side and the second one is the write
496      * side.
497      */
498     private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
499     private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
500 
501     /**
502      * True if we're supposed to start this call with RTT, either due to the master switch or due
503      * to an extra.
504      */
505     private boolean mDidRequestToStartWithRtt = false;
506     /**
507      * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
508      */
509     private int mRttMode;
510     /**
511      * True if the call was ever an RTT call.
512      */
513     private boolean mWasEverRtt = false;
514 
515     /**
516      * Integer indicating the remote RTT request ID that is pending a response from the user.
517      */
518     private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID;
519 
520     /**
521      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
522      * int, Bundle, boolean)}, contains the call which this call is being handed over to.
523      */
524     private Call mHandoverDestinationCall = null;
525 
526     /**
527      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
528      * int, Bundle, boolean)}, contains the call which this call is being handed over from.
529      */
530     private Call mHandoverSourceCall = null;
531 
532     /**
533      * Indicates the current state of this call if it is in the process of a handover.
534      */
535     private int mHandoverState = HandoverState.HANDOVER_NONE;
536 
537     /**
538      * Indicates whether this call is using one of the
539      * {@link com.android.server.telecom.callfiltering.IncomingCallFilter.CallFilter} modules.
540      */
541     private boolean mIsUsingCallFiltering = false;
542 
543     /**
544      * Persists the specified parameters and initializes the new instance.
545      * @param context The context.
546      * @param repository The connection service repository.
547      * @param handle The handle to dial.
548      * @param gatewayInfo Gateway information to use for the call.
549      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
550      *         This account must be one that was registered with the
551      *           {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
552      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
553      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
554      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
555      *         or CALL_DIRECTION_UNKNOWN.
556      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
557      * @param clockProxy
558      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy)559     public Call(
560             String callId,
561             Context context,
562             CallsManager callsManager,
563             TelecomSystem.SyncRoot lock,
564             ConnectionServiceRepository repository,
565             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
566             Uri handle,
567             GatewayInfo gatewayInfo,
568             PhoneAccountHandle connectionManagerPhoneAccountHandle,
569             PhoneAccountHandle targetPhoneAccountHandle,
570             int callDirection,
571             boolean shouldAttachToExistingConnection,
572             boolean isConference,
573             ClockProxy clockProxy) {
574         mId = callId;
575         mConnectionId = callId;
576         mState = isConference ? CallState.ACTIVE : CallState.NEW;
577         mContext = context;
578         mCallsManager = callsManager;
579         mLock = lock;
580         mRepository = repository;
581         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
582         setHandle(handle);
583         mPostDialDigits = handle != null
584                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
585         mGatewayInfo = gatewayInfo;
586         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
587         setTargetPhoneAccount(targetPhoneAccountHandle);
588         mCallDirection = callDirection;
589         mIsConference = isConference;
590         mShouldAttachToExistingConnection = shouldAttachToExistingConnection
591                 || callDirection == CALL_DIRECTION_INCOMING;
592         maybeLoadCannedSmsResponses();
593         mClockProxy = clockProxy;
594         mCreationTimeMillis = mClockProxy.currentTimeMillis();
595     }
596 
597     /**
598      * Persists the specified parameters and initializes the new instance.
599      * @param context The context.
600      * @param repository The connection service repository.
601      * @param handle The handle to dial.
602      * @param gatewayInfo Gateway information to use for the call.
603      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
604 *         This account must be one that was registered with the
605 *         {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
606      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
607 *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
608      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
609 *         or CALL_DIRECTION_UNKNOWN
610      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
611 *         connection, regardless of whether it's incoming or outgoing.
612      * @param connectTimeMillis The connection time of the call.
613      * @param clockProxy
614      */
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, long connectTimeMillis, long connectElapsedTimeMillis, ClockProxy clockProxy)615     Call(
616             String callId,
617             Context context,
618             CallsManager callsManager,
619             TelecomSystem.SyncRoot lock,
620             ConnectionServiceRepository repository,
621             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
622             Uri handle,
623             GatewayInfo gatewayInfo,
624             PhoneAccountHandle connectionManagerPhoneAccountHandle,
625             PhoneAccountHandle targetPhoneAccountHandle,
626             int callDirection,
627             boolean shouldAttachToExistingConnection,
628             boolean isConference,
629             long connectTimeMillis,
630             long connectElapsedTimeMillis,
631             ClockProxy clockProxy) {
632         this(callId, context, callsManager, lock, repository,
633                 phoneNumberUtilsAdapter, handle, gatewayInfo,
634                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
635                 shouldAttachToExistingConnection, isConference, clockProxy);
636 
637         mConnectTimeMillis = connectTimeMillis;
638         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
639         mAnalytics.setCallStartTime(connectTimeMillis);
640     }
641 
addListener(Listener listener)642     public void addListener(Listener listener) {
643         mListeners.add(listener);
644     }
645 
removeListener(Listener listener)646     public void removeListener(Listener listener) {
647         if (listener != null) {
648             mListeners.remove(listener);
649         }
650     }
651 
initAnalytics()652     public void initAnalytics() {
653         initAnalytics(null);
654     }
655 
initAnalytics(String callingPackage)656     public void initAnalytics(String callingPackage) {
657         int analyticsDirection;
658         switch (mCallDirection) {
659             case CALL_DIRECTION_OUTGOING:
660                 analyticsDirection = Analytics.OUTGOING_DIRECTION;
661                 break;
662             case CALL_DIRECTION_INCOMING:
663                 analyticsDirection = Analytics.INCOMING_DIRECTION;
664                 break;
665             case CALL_DIRECTION_UNKNOWN:
666             case CALL_DIRECTION_UNDEFINED:
667             default:
668                 analyticsDirection = Analytics.UNKNOWN_DIRECTION;
669         }
670         mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
671         mAnalytics.setCallIsEmergency(mIsEmergencyCall);
672         Log.addEvent(this, LogUtils.Events.CREATED, callingPackage);
673     }
674 
getAnalytics()675     public Analytics.CallInfo getAnalytics() {
676         return mAnalytics;
677     }
678 
destroy()679     public void destroy() {
680         // We should not keep these bitmaps around because the Call objects may be held for logging
681         // purposes.
682         // TODO: Make a container object that only stores the information we care about for Logging.
683         if (mCallerInfo != null) {
684             mCallerInfo.cachedPhotoIcon = null;
685             mCallerInfo.cachedPhoto = null;
686         }
687         closeRttStreams();
688 
689         Log.addEvent(this, LogUtils.Events.DESTROYED);
690     }
691 
closeRttStreams()692     private void closeRttStreams() {
693         if (mConnectionServiceToInCallStreams != null) {
694             for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) {
695                 if (fd != null) {
696                     try {
697                         fd.close();
698                     } catch (IOException e) {
699                         // ignore
700                     }
701                 }
702             }
703         }
704         if (mInCallToConnectionServiceStreams != null) {
705             for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) {
706                 if (fd != null) {
707                     try {
708                         fd.close();
709                     } catch (IOException e) {
710                         // ignore
711                     }
712                 }
713             }
714         }
715     }
716 
717     /** {@inheritDoc} */
718     @Override
toString()719     public String toString() {
720         String component = null;
721         if (mConnectionService != null && mConnectionService.getComponentName() != null) {
722             component = mConnectionService.getComponentName().flattenToShortString();
723         }
724 
725         return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]",
726                 mId,
727                 CallState.toString(mState),
728                 component,
729                 Log.piiHandle(mHandle),
730                 getVideoStateDescription(getVideoState()),
731                 getChildCalls().size(),
732                 getParentCall() != null,
733                 Connection.capabilitiesToString(getConnectionCapabilities()),
734                 Connection.propertiesToString(getConnectionProperties()));
735     }
736 
737     @Override
getDescription()738     public String getDescription() {
739         StringBuilder s = new StringBuilder();
740         if (isSelfManaged()) {
741             s.append("SelfMgd Call");
742         } else if (isExternalCall()) {
743             s.append("External Call");
744         } else {
745             s.append("Call");
746         }
747         s.append(getId());
748         s.append(" [");
749         s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
750         s.append("]");
751         s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
752         s.append("\n\tVia PhoneAccount: ");
753         PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
754         if (targetPhoneAccountHandle != null) {
755             s.append(targetPhoneAccountHandle);
756             s.append(" (");
757             s.append(getTargetPhoneAccountLabel());
758             s.append(")");
759         } else {
760             s.append("not set");
761         }
762 
763         s.append("\n\tTo address: ");
764         s.append(Log.piiHandle(getHandle()));
765         s.append(" Presentation: ");
766         switch (getHandlePresentation()) {
767             case TelecomManager.PRESENTATION_ALLOWED:
768                 s.append("Allowed");
769                 break;
770             case TelecomManager.PRESENTATION_PAYPHONE:
771                 s.append("Payphone");
772                 break;
773             case TelecomManager.PRESENTATION_RESTRICTED:
774                 s.append("Restricted");
775                 break;
776             case TelecomManager.PRESENTATION_UNKNOWN:
777                 s.append("Unknown");
778                 break;
779             default:
780                 s.append("<undefined>");
781         }
782         s.append("\n");
783         return s.toString();
784     }
785 
786     /**
787      * Builds a debug-friendly description string for a video state.
788      * <p>
789      * A = audio active, T = video transmission active, R = video reception active, P = video
790      * paused.
791      *
792      * @param videoState The video state.
793      * @return A string indicating which bits are set in the video state.
794      */
getVideoStateDescription(int videoState)795     private String getVideoStateDescription(int videoState) {
796         StringBuilder sb = new StringBuilder();
797         sb.append("A");
798 
799         if (VideoProfile.isTransmissionEnabled(videoState)) {
800             sb.append("T");
801         }
802 
803         if (VideoProfile.isReceptionEnabled(videoState)) {
804             sb.append("R");
805         }
806 
807         if (VideoProfile.isPaused(videoState)) {
808             sb.append("P");
809         }
810 
811         return sb.toString();
812     }
813 
814     @Override
getConnectionServiceWrapper()815     public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() {
816         return mConnectionService;
817     }
818 
819     @VisibleForTesting
getState()820     public int getState() {
821         return mState;
822     }
823 
824     /**
825      * Determines if this {@link Call} can receive call focus via the
826      * {@link ConnectionServiceFocusManager}.
827      * Only top-level calls and non-external calls are eligible.
828      * @return {@code true} if this call is focusable, {@code false} otherwise.
829      */
830     @Override
isFocusable()831     public boolean isFocusable() {
832         boolean isChild = getParentCall() != null;
833         return !isChild && !isExternalCall();
834     }
835 
shouldContinueProcessingAfterDisconnect()836     private boolean shouldContinueProcessingAfterDisconnect() {
837         // Stop processing once the call is active.
838         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
839             return false;
840         }
841 
842         // Only Redial a Call in the case of it being an Emergency Call.
843         if(!isEmergencyCall()) {
844             return false;
845         }
846 
847         // Make sure that there are additional connection services to process.
848         if (mCreateConnectionProcessor == null
849             || !mCreateConnectionProcessor.isProcessingComplete()
850             || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
851             return false;
852         }
853 
854         if (mDisconnectCause == null) {
855             return false;
856         }
857 
858         // Continue processing if the current attempt failed or timed out.
859         return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
860             mCreateConnectionProcessor.isCallTimedOut();
861     }
862 
863     /**
864      * Returns the unique ID for this call as it exists in Telecom.
865      * @return The call ID.
866      */
getId()867     public String getId() {
868         return mId;
869     }
870 
871     /**
872      * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
873      * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
874      * @return The call ID with an appended attempt id.
875      */
getConnectionId()876     public String getConnectionId() {
877         if(mCreateConnectionProcessor != null) {
878             mConnectionId = mId + "_" +
879                     String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
880             return mConnectionId;
881         } else {
882             return mConnectionId;
883         }
884     }
885 
886     /**
887      * Sets the call state. Although there exists the notion of appropriate state transitions
888      * (see {@link CallState}), in practice those expectations break down when cellular systems
889      * misbehave and they do this very often. The result is that we do not enforce state transitions
890      * and instead keep the code resilient to unexpected state changes.
891      * @return true indicates if setState succeeded in setting the state to newState,
892      * else it is failed, and the call is still in its original state.
893      */
setState(int newState, String tag)894     public boolean setState(int newState, String tag) {
895         if (mState != newState) {
896             Log.v(this, "setState %s -> %s", CallState.toString(mState),
897                     CallState.toString(newState));
898 
899             if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
900                 Log.w(this, "continuing processing disconnected call with another service");
901                 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
902                 return false;
903             } else if (newState == CallState.ANSWERED && mState == CallState.ACTIVE) {
904                 Log.w(this, "setState %s -> %s; call already active.", CallState.toString(mState),
905                         CallState.toString(newState));
906                 return false;
907             }
908 
909             updateVideoHistoryViaState(mState, newState);
910 
911             mState = newState;
912             maybeLoadCannedSmsResponses();
913 
914             if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
915                 if (mConnectTimeMillis == 0) {
916                     // We check to see if mConnectTime is already set to prevent the
917                     // call from resetting active time when it goes in and out of
918                     // ACTIVE/ON_HOLD
919                     mConnectTimeMillis = mClockProxy.currentTimeMillis();
920                     mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
921                     mAnalytics.setCallStartTime(mConnectTimeMillis);
922                 }
923 
924                 // We're clearly not disconnected, so reset the disconnected time.
925                 mDisconnectTimeMillis = 0;
926                 mDisconnectElapsedTimeMillis = 0;
927             } else if (mState == CallState.DISCONNECTED) {
928                 mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
929                 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
930                 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
931                 setLocallyDisconnecting(false);
932                 fixParentAfterDisconnect();
933             }
934 
935             // Log the state transition event
936             String event = null;
937             Object data = null;
938             switch (newState) {
939                 case CallState.ACTIVE:
940                     event = LogUtils.Events.SET_ACTIVE;
941                     break;
942                 case CallState.CONNECTING:
943                     event = LogUtils.Events.SET_CONNECTING;
944                     break;
945                 case CallState.DIALING:
946                     event = LogUtils.Events.SET_DIALING;
947                     break;
948                 case CallState.PULLING:
949                     event = LogUtils.Events.SET_PULLING;
950                     break;
951                 case CallState.DISCONNECTED:
952                     event = LogUtils.Events.SET_DISCONNECTED;
953                     data = getDisconnectCause();
954                     break;
955                 case CallState.DISCONNECTING:
956                     event = LogUtils.Events.SET_DISCONNECTING;
957                     break;
958                 case CallState.ON_HOLD:
959                     event = LogUtils.Events.SET_HOLD;
960                     break;
961                 case CallState.SELECT_PHONE_ACCOUNT:
962                     event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
963                     break;
964                 case CallState.RINGING:
965                     event = LogUtils.Events.SET_RINGING;
966                     break;
967                 case CallState.ANSWERED:
968                     event = LogUtils.Events.SET_ANSWERED;
969                     break;
970             }
971             if (event != null) {
972                 // The string data should be just the tag.
973                 String stringData = tag;
974                 if (data != null) {
975                     // If data exists, add it to tag.  If no tag, just use data.toString().
976                     stringData = stringData == null ? data.toString() : stringData + "> " + data;
977                 }
978                 Log.addEvent(this, event, stringData);
979             }
980             int statsdDisconnectCause = (newState == CallState.DISCONNECTED) ?
981                     getDisconnectCause().getCode() : DisconnectCause.UNKNOWN;
982             StatsLog.write(StatsLog.CALL_STATE_CHANGED, newState, statsdDisconnectCause,
983                     isSelfManaged(), isExternalCall());
984         }
985         return true;
986     }
987 
setRingbackRequested(boolean ringbackRequested)988     void setRingbackRequested(boolean ringbackRequested) {
989         mRingbackRequested = ringbackRequested;
990         for (Listener l : mListeners) {
991             l.onRingbackRequested(this, mRingbackRequested);
992         }
993     }
994 
isRingbackRequested()995     boolean isRingbackRequested() {
996         return mRingbackRequested;
997     }
998 
setSilentRingingRequested(boolean silentRingingRequested)999     public void setSilentRingingRequested(boolean silentRingingRequested) {
1000         mSilentRingingRequested = silentRingingRequested;
1001         Bundle bundle = new Bundle();
1002         bundle.putBoolean(android.telecom.Call.EXTRA_SILENT_RINGING_REQUESTED,
1003                 silentRingingRequested);
1004         putExtras(SOURCE_CONNECTION_SERVICE, bundle);
1005     }
1006 
isSilentRingingRequested()1007     public boolean isSilentRingingRequested() {
1008         return mSilentRingingRequested;
1009     }
1010 
1011     @VisibleForTesting
isConference()1012     public boolean isConference() {
1013         return mIsConference;
1014     }
1015 
1016     /**
1017      * @return {@code true} if this call had children at some point, {@code false} otherwise.
1018      */
hadChildren()1019     public boolean hadChildren() {
1020         return mHadChildren;
1021     }
1022 
getHandle()1023     public Uri getHandle() {
1024         return mHandle;
1025     }
1026 
getPostDialDigits()1027     public String getPostDialDigits() {
1028         return mPostDialDigits;
1029     }
1030 
clearPostDialDigits()1031     public void clearPostDialDigits() {
1032         mPostDialDigits = null;
1033     }
1034 
getViaNumber()1035     public String getViaNumber() {
1036         return mViaNumber;
1037     }
1038 
setViaNumber(String viaNumber)1039     public void setViaNumber(String viaNumber) {
1040         // If at any point the via number is not empty throughout the call, save that via number.
1041         if (!TextUtils.isEmpty(viaNumber)) {
1042             mViaNumber = viaNumber;
1043         }
1044     }
1045 
getHandlePresentation()1046     public int getHandlePresentation() {
1047         return mHandlePresentation;
1048     }
1049 
1050 
setHandle(Uri handle)1051     void setHandle(Uri handle) {
1052         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
1053     }
1054 
setHandle(Uri handle, int presentation)1055     public void setHandle(Uri handle, int presentation) {
1056         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
1057             mHandlePresentation = presentation;
1058             if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
1059                     mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
1060                 mHandle = null;
1061             } else {
1062                 mHandle = handle;
1063                 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
1064                         && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
1065                     // If the number is actually empty, set it to null, unless this is a
1066                     // SCHEME_VOICEMAIL uri which always has an empty number.
1067                     mHandle = null;
1068                 }
1069             }
1070 
1071             // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
1072             // call, it will remain so for the rest of it's lifetime.
1073             if (!mIsEmergencyCall) {
1074                 mIsEmergencyCall = mHandle != null &&
1075                         mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
1076                                 mHandle.getSchemeSpecificPart());
1077                 mAnalytics.setCallIsEmergency(mIsEmergencyCall);
1078             }
1079             startCallerInfoLookup();
1080             for (Listener l : mListeners) {
1081                 l.onHandleChanged(this);
1082             }
1083         }
1084     }
1085 
getCallerDisplayName()1086     public String getCallerDisplayName() {
1087         return mCallerDisplayName;
1088     }
1089 
getCallerDisplayNamePresentation()1090     public int getCallerDisplayNamePresentation() {
1091         return mCallerDisplayNamePresentation;
1092     }
1093 
setCallerDisplayName(String callerDisplayName, int presentation)1094     void setCallerDisplayName(String callerDisplayName, int presentation) {
1095         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
1096                 presentation != mCallerDisplayNamePresentation) {
1097             mCallerDisplayName = callerDisplayName;
1098             mCallerDisplayNamePresentation = presentation;
1099             for (Listener l : mListeners) {
1100                 l.onCallerDisplayNameChanged(this);
1101             }
1102         }
1103     }
1104 
getName()1105     public String getName() {
1106         return mCallerInfo == null ? null : mCallerInfo.name;
1107     }
1108 
getPhoneNumber()1109     public String getPhoneNumber() {
1110         return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
1111     }
1112 
getPhotoIcon()1113     public Bitmap getPhotoIcon() {
1114         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
1115     }
1116 
getPhoto()1117     public Drawable getPhoto() {
1118         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
1119     }
1120 
1121     /**
1122      * @param disconnectCause The reason for the disconnection, represented by
1123      *         {@link android.telecom.DisconnectCause}.
1124      */
setDisconnectCause(DisconnectCause disconnectCause)1125     public void setDisconnectCause(DisconnectCause disconnectCause) {
1126         // TODO: Consider combining this method with a setDisconnected() method that is totally
1127         // separate from setState.
1128         mAnalytics.setCallDisconnectCause(disconnectCause);
1129         mDisconnectCause = disconnectCause;
1130     }
1131 
getDisconnectCause()1132     public DisconnectCause getDisconnectCause() {
1133         return mDisconnectCause;
1134     }
1135 
1136     /**
1137      * @return {@code true} if this is an outgoing call to emergency services. An outgoing call is
1138      * identified as an emergency call by the dialer phone number.
1139      */
1140     @VisibleForTesting
isEmergencyCall()1141     public boolean isEmergencyCall() {
1142         return mIsEmergencyCall;
1143     }
1144 
1145     /**
1146      * @return {@code true} if the network has identified this call as an emergency call.
1147      */
isNetworkIdentifiedEmergencyCall()1148     public boolean isNetworkIdentifiedEmergencyCall() {
1149         return hasProperty(Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL);
1150     }
1151 
1152     /**
1153      * @return The original handle this call is associated with. In-call services should use this
1154      * handle when indicating in their UI the handle that is being called.
1155      */
getOriginalHandle()1156     public Uri getOriginalHandle() {
1157         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
1158             return mGatewayInfo.getOriginalAddress();
1159         }
1160         return getHandle();
1161     }
1162 
1163     @VisibleForTesting
getGatewayInfo()1164     public GatewayInfo getGatewayInfo() {
1165         return mGatewayInfo;
1166     }
1167 
setGatewayInfo(GatewayInfo gatewayInfo)1168     void setGatewayInfo(GatewayInfo gatewayInfo) {
1169         mGatewayInfo = gatewayInfo;
1170     }
1171 
1172     @VisibleForTesting
getConnectionManagerPhoneAccount()1173     public PhoneAccountHandle getConnectionManagerPhoneAccount() {
1174         return mConnectionManagerPhoneAccountHandle;
1175     }
1176 
1177     @VisibleForTesting
setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle)1178     public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
1179         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
1180             mConnectionManagerPhoneAccountHandle = accountHandle;
1181             for (Listener l : mListeners) {
1182                 l.onConnectionManagerPhoneAccountChanged(this);
1183             }
1184         }
1185         checkIfRttCapable();
1186     }
1187 
1188     @VisibleForTesting
getTargetPhoneAccount()1189     public PhoneAccountHandle getTargetPhoneAccount() {
1190         return mTargetPhoneAccountHandle;
1191     }
1192 
1193     @VisibleForTesting
setTargetPhoneAccount(PhoneAccountHandle accountHandle)1194     public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
1195         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
1196             mTargetPhoneAccountHandle = accountHandle;
1197             for (Listener l : mListeners) {
1198                 l.onTargetPhoneAccountChanged(this);
1199             }
1200             configureCallAttributes();
1201         }
1202         checkIfVideoCapable();
1203         checkIfRttCapable();
1204     }
1205 
getTargetPhoneAccountLabel()1206     public CharSequence getTargetPhoneAccountLabel() {
1207         if (getTargetPhoneAccount() == null) {
1208             return null;
1209         }
1210         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1211                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1212 
1213         if (phoneAccount == null) {
1214             return null;
1215         }
1216 
1217         return phoneAccount.getLabel();
1218     }
1219 
1220     /**
1221      * Determines if this Call should be written to the call log.
1222      * @return {@code true} for managed calls or for self-managed calls which have the
1223      * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set.
1224      */
isLoggedSelfManaged()1225     public boolean isLoggedSelfManaged() {
1226         if (!isSelfManaged()) {
1227             // Managed calls are always logged.
1228             return true;
1229         }
1230         if (getTargetPhoneAccount() == null) {
1231             return false;
1232         }
1233         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1234                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1235 
1236         if (phoneAccount == null) {
1237             return false;
1238         }
1239 
1240         if (getHandle() == null) {
1241             // No point in logging a null-handle call. Some self-managed calls will have this.
1242             return false;
1243         }
1244 
1245         if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) &&
1246                 !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) {
1247             // Can't log schemes other than SIP or TEL for now.
1248             return false;
1249         }
1250 
1251         return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
1252                 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
1253     }
1254 
1255     @VisibleForTesting
isIncoming()1256     public boolean isIncoming() {
1257         return mCallDirection == CALL_DIRECTION_INCOMING;
1258     }
1259 
isExternalCall()1260     public boolean isExternalCall() {
1261         return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
1262                 Connection.PROPERTY_IS_EXTERNAL_CALL;
1263     }
1264 
isWorkCall()1265     public boolean isWorkCall() {
1266         return mIsWorkCall;
1267     }
1268 
isUsingCallRecordingTone()1269     public boolean isUsingCallRecordingTone() {
1270         return mUseCallRecordingTone;
1271     }
1272 
1273     /**
1274      * @return {@code true} if the {@link Call}'s {@link #getTargetPhoneAccount()} supports video.
1275      */
isVideoCallingSupportedByPhoneAccount()1276     public boolean isVideoCallingSupportedByPhoneAccount() {
1277         return mIsVideoCallingSupportedByPhoneAccount;
1278     }
1279 
1280     /**
1281      * Sets whether video calling is supported by the current phone account. Since video support
1282      * can change during a call, this method facilitates updating call video state.
1283      * @param isVideoCallingSupported Sets whether video calling is supported.
1284      */
setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported)1285     public void setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported) {
1286         if (mIsVideoCallingSupportedByPhoneAccount == isVideoCallingSupported) {
1287             return;
1288         }
1289         Log.i(this, "setVideoCallingSupportedByPhoneAccount: isSupp=%b", isVideoCallingSupported);
1290         mIsVideoCallingSupportedByPhoneAccount = isVideoCallingSupported;
1291 
1292         // Force an update of the connection capabilities so that the dialer is informed of the new
1293         // video capabilities based on the phone account's support for video.
1294         setConnectionCapabilities(getConnectionCapabilities(), true /* force */);
1295     }
1296 
1297     /**
1298      * @return {@code true} if the {@link Call} locally supports video.
1299      */
isLocallyVideoCapable()1300     public boolean isLocallyVideoCapable() {
1301         return (getConnectionCapabilities() & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
1302                 == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL;
1303     }
1304 
isSelfManaged()1305     public boolean isSelfManaged() {
1306         return mIsSelfManaged;
1307     }
1308 
setIsSelfManaged(boolean isSelfManaged)1309     public void setIsSelfManaged(boolean isSelfManaged) {
1310         mIsSelfManaged = isSelfManaged;
1311 
1312         // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1313         setConnectionProperties(getConnectionProperties());
1314     }
1315 
markFinishedHandoverStateAndCleanup(int handoverState)1316     public void markFinishedHandoverStateAndCleanup(int handoverState) {
1317         if (mHandoverSourceCall != null) {
1318             mHandoverSourceCall.setHandoverState(handoverState);
1319         } else if (mHandoverDestinationCall != null) {
1320             mHandoverDestinationCall.setHandoverState(handoverState);
1321         }
1322         setHandoverState(handoverState);
1323         maybeCleanupHandover();
1324     }
1325 
maybeCleanupHandover()1326     public void maybeCleanupHandover() {
1327         if (mHandoverSourceCall != null) {
1328             mHandoverSourceCall.setHandoverSourceCall(null);
1329             mHandoverSourceCall.setHandoverDestinationCall(null);
1330             mHandoverSourceCall = null;
1331         } else if (mHandoverDestinationCall != null) {
1332             mHandoverDestinationCall.setHandoverSourceCall(null);
1333             mHandoverDestinationCall.setHandoverDestinationCall(null);
1334             mHandoverDestinationCall = null;
1335         }
1336     }
1337 
isHandoverInProgress()1338     public boolean isHandoverInProgress() {
1339         return mHandoverSourceCall != null || mHandoverDestinationCall != null;
1340     }
1341 
getHandoverDestinationCall()1342     public Call getHandoverDestinationCall() {
1343         return mHandoverDestinationCall;
1344     }
1345 
setHandoverDestinationCall(Call call)1346     public void setHandoverDestinationCall(Call call) {
1347         mHandoverDestinationCall = call;
1348     }
1349 
getHandoverSourceCall()1350     public Call getHandoverSourceCall() {
1351         return mHandoverSourceCall;
1352     }
1353 
setHandoverSourceCall(Call call)1354     public void setHandoverSourceCall(Call call) {
1355         mHandoverSourceCall = call;
1356     }
1357 
setHandoverState(int handoverState)1358     public void setHandoverState(int handoverState) {
1359         Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
1360                 HandoverState.stateToString(handoverState));
1361         mHandoverState = handoverState;
1362     }
1363 
getHandoverState()1364     public int getHandoverState() {
1365         return mHandoverState;
1366     }
1367 
configureCallAttributes()1368     private void configureCallAttributes() {
1369         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1370         boolean isWorkCall = false;
1371         boolean isCallRecordingToneSupported = false;
1372         PhoneAccount phoneAccount =
1373                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1374         if (phoneAccount != null) {
1375             final UserHandle userHandle;
1376             if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
1377                 userHandle = mInitiatingUser;
1378             } else {
1379                 userHandle = mTargetPhoneAccountHandle.getUserHandle();
1380             }
1381             if (userHandle != null) {
1382                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
1383             }
1384 
1385             isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
1386                     PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
1387                     && phoneAccount.getExtras().getBoolean(
1388                     PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
1389         }
1390         mIsWorkCall = isWorkCall;
1391         mUseCallRecordingTone = isCallRecordingToneSupported;
1392     }
1393 
1394     /**
1395      * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
1396      * capability and ensures that the video state is updated if the phone account does not support
1397      * video calling.
1398      */
checkIfVideoCapable()1399     private void checkIfVideoCapable() {
1400         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1401         if (mTargetPhoneAccountHandle == null) {
1402             // If no target phone account handle is specified, assume we can potentially perform a
1403             // video call; once the phone account is set, we can confirm that it is video capable.
1404             mIsVideoCallingSupportedByPhoneAccount = true;
1405             Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
1406             return;
1407         }
1408         PhoneAccount phoneAccount =
1409                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1410         mIsVideoCallingSupportedByPhoneAccount = phoneAccount != null &&
1411                 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
1412 
1413         if (!mIsVideoCallingSupportedByPhoneAccount && VideoProfile.isVideo(getVideoState())) {
1414             // The PhoneAccount for the Call was set to one which does not support video calling,
1415             // and the current call is configured to be a video call; downgrade to audio-only.
1416             setVideoState(VideoProfile.STATE_AUDIO_ONLY);
1417             Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
1418         }
1419     }
1420 
checkIfRttCapable()1421     private void checkIfRttCapable() {
1422         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1423         if (mTargetPhoneAccountHandle == null) {
1424             return;
1425         }
1426 
1427         // Check both the target phone account and the connection manager phone account -- if
1428         // either support RTT, just set the streams and have them set/unset the RTT property as
1429         // needed.
1430         PhoneAccount phoneAccount =
1431                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1432         PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
1433                         mConnectionManagerPhoneAccountHandle);
1434         boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1435                 PhoneAccount.CAPABILITY_RTT);
1436         boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
1437                 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
1438 
1439         if ((isConnectionManagerRttSupported || isRttSupported)
1440                 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
1441             // If the phone account got set to an RTT capable one and we haven't set the streams
1442             // yet, do so now.
1443             createRttStreams();
1444             Log.i(this, "Setting RTT streams after target phone account selected");
1445         }
1446     }
1447 
shouldAttachToExistingConnection()1448     boolean shouldAttachToExistingConnection() {
1449         return mShouldAttachToExistingConnection;
1450     }
1451 
1452     /**
1453      * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
1454      * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
1455      * change due to clock changes).
1456      * @return The "age" of this call object in milliseconds, which typically also represents the
1457      *     period since this call was added to the set pending outgoing calls.
1458      */
1459     @VisibleForTesting
getAgeMillis()1460     public long getAgeMillis() {
1461         if (mState == CallState.DISCONNECTED &&
1462                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
1463                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
1464             // Rejected and missed calls have no age. They're immortal!!
1465             return 0;
1466         } else if (mConnectElapsedTimeMillis == 0) {
1467             // Age is measured in the amount of time the call was active. A zero connect time
1468             // indicates that we never went active, so return 0 for the age.
1469             return 0;
1470         } else if (mDisconnectElapsedTimeMillis == 0) {
1471             // We connected, but have not yet disconnected
1472             return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
1473         }
1474 
1475         return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
1476     }
1477 
1478     /**
1479      * @return The time when this call object was created and added to the set of pending outgoing
1480      *     calls.
1481      */
getCreationTimeMillis()1482     public long getCreationTimeMillis() {
1483         return mCreationTimeMillis;
1484     }
1485 
setCreationTimeMillis(long time)1486     public void setCreationTimeMillis(long time) {
1487         mCreationTimeMillis = time;
1488     }
1489 
getConnectTimeMillis()1490     long getConnectTimeMillis() {
1491         return mConnectTimeMillis;
1492     }
1493 
setConnectTimeMillis(long connectTimeMillis)1494     public void setConnectTimeMillis(long connectTimeMillis) {
1495         mConnectTimeMillis = connectTimeMillis;
1496     }
1497 
setConnectElapsedTimeMillis(long connectElapsedTimeMillis)1498     public void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
1499         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
1500     }
1501 
getConnectionCapabilities()1502     public int getConnectionCapabilities() {
1503         return mConnectionCapabilities;
1504     }
1505 
getConnectionProperties()1506     int getConnectionProperties() {
1507         return mConnectionProperties;
1508     }
1509 
setConnectionCapabilities(int connectionCapabilities)1510     public void setConnectionCapabilities(int connectionCapabilities) {
1511         setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
1512     }
1513 
setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate)1514     void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
1515         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
1516                 connectionCapabilities));
1517         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
1518             // If the phone account does not support video calling, and the connection capabilities
1519             // passed in indicate that the call supports video, remove those video capabilities.
1520             if (!isVideoCallingSupportedByPhoneAccount()
1521                     && doesCallSupportVideo(connectionCapabilities)) {
1522                 Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
1523                         "capable when not supported by the phone account.");
1524                 connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
1525             }
1526             int previousCapabilities = mConnectionCapabilities;
1527             mConnectionCapabilities = connectionCapabilities;
1528             for (Listener l : mListeners) {
1529                 l.onConnectionCapabilitiesChanged(this);
1530             }
1531 
1532             int xorCaps = previousCapabilities ^ mConnectionCapabilities;
1533             Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
1534                     "Current: [%s], Removed [%s], Added [%s]",
1535                     Connection.capabilitiesToStringShort(mConnectionCapabilities),
1536                     Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
1537                     Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
1538         }
1539     }
1540 
setConnectionProperties(int connectionProperties)1541     public void setConnectionProperties(int connectionProperties) {
1542         Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
1543                 connectionProperties));
1544 
1545         // Ensure the ConnectionService can't change the state of the self-managed property.
1546         if (isSelfManaged()) {
1547             connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
1548         } else {
1549             connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
1550         }
1551 
1552         int changedProperties = mConnectionProperties ^ connectionProperties;
1553 
1554         if (changedProperties != 0) {
1555             int previousProperties = mConnectionProperties;
1556             mConnectionProperties = connectionProperties;
1557             boolean didRttChange =
1558                     (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
1559             if (didRttChange && (mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
1560                     Connection.PROPERTY_IS_RTT) {
1561                 createRttStreams();
1562                 // Call startRtt to pass the RTT pipes down to the connection service.
1563                 // They already turned on the RTT property so no request should be sent.
1564                 mConnectionService.startRtt(this,
1565                         getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
1566                 mWasEverRtt = true;
1567                 if (isEmergencyCall()) {
1568                     mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
1569                     mCallsManager.mute(false);
1570                 }
1571             }
1572             mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
1573                     Connection.PROPERTY_HIGH_DEF_AUDIO;
1574             for (Listener l : mListeners) {
1575                 l.onConnectionPropertiesChanged(this, didRttChange);
1576             }
1577 
1578             boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1579                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
1580             boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1581                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
1582             if (wasExternal != isExternal) {
1583                 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
1584                         isExternal);
1585                 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
1586                 for (Listener l : mListeners) {
1587                     l.onExternalCallChanged(this, isExternal);
1588                 }
1589             }
1590 
1591             mAnalytics.addCallProperties(mConnectionProperties);
1592 
1593             int xorProps = previousProperties ^ mConnectionProperties;
1594             Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
1595                     "Current: [%s], Removed [%s], Added [%s]",
1596                     Connection.propertiesToStringShort(mConnectionProperties),
1597                     Connection.propertiesToStringShort(previousProperties & xorProps),
1598                     Connection.propertiesToStringShort(mConnectionProperties & xorProps));
1599         }
1600     }
1601 
getSupportedAudioRoutes()1602     public int getSupportedAudioRoutes() {
1603         return mSupportedAudioRoutes;
1604     }
1605 
setSupportedAudioRoutes(int audioRoutes)1606     void setSupportedAudioRoutes(int audioRoutes) {
1607         if (mSupportedAudioRoutes != audioRoutes) {
1608             mSupportedAudioRoutes = audioRoutes;
1609         }
1610     }
1611 
1612     @VisibleForTesting
getParentCall()1613     public Call getParentCall() {
1614         return mParentCall;
1615     }
1616 
1617     @VisibleForTesting
getChildCalls()1618     public List<Call> getChildCalls() {
1619         return mChildCalls;
1620     }
1621 
1622     @VisibleForTesting
wasConferencePreviouslyMerged()1623     public boolean wasConferencePreviouslyMerged() {
1624         return mWasConferencePreviouslyMerged;
1625     }
1626 
isDisconnectingChildCall()1627     public boolean isDisconnectingChildCall() {
1628         return mIsDisconnectingChildCall;
1629     }
1630 
1631     /**
1632      * Sets whether this call is a child call.
1633      */
maybeSetCallAsDisconnectingChild()1634     private void maybeSetCallAsDisconnectingChild() {
1635         if (mParentCall != null) {
1636             mIsDisconnectingChildCall = true;
1637         }
1638     }
1639 
1640     @VisibleForTesting
getConferenceLevelActiveCall()1641     public Call getConferenceLevelActiveCall() {
1642         return mConferenceLevelActiveCall;
1643     }
1644 
1645     @VisibleForTesting
getConnectionService()1646     public ConnectionServiceWrapper getConnectionService() {
1647         return mConnectionService;
1648     }
1649 
1650     /**
1651      * Retrieves the {@link Context} for the call.
1652      *
1653      * @return The {@link Context}.
1654      */
getContext()1655     public Context getContext() {
1656         return mContext;
1657     }
1658 
1659     @VisibleForTesting
setConnectionService(ConnectionServiceWrapper service)1660     public void setConnectionService(ConnectionServiceWrapper service) {
1661         Preconditions.checkNotNull(service);
1662 
1663         clearConnectionService();
1664 
1665         service.incrementAssociatedCallCount();
1666         mConnectionService = service;
1667         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1668         mConnectionService.addCall(this);
1669     }
1670 
1671     /**
1672      * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
1673      * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
1674      * ConnectionService is NOT unbound if the call count hits zero.
1675      * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
1676      * {@link Conference} additions via a ConnectionManager.
1677      * The original {@link android.telecom.ConnectionService} will directly add external calls and
1678      * conferences to Telecom as well as the ConnectionManager, which will add to Telecom.  In these
1679      * cases since its first added to via the original CS, we want to change the CS responsible for
1680      * the call to the ConnectionManager rather than adding it again as another call/conference.
1681      *
1682      * @param service The new {@link ConnectionServiceWrapper}.
1683      */
replaceConnectionService(ConnectionServiceWrapper service)1684     public void replaceConnectionService(ConnectionServiceWrapper service) {
1685         Preconditions.checkNotNull(service);
1686 
1687         if (mConnectionService != null) {
1688             ConnectionServiceWrapper serviceTemp = mConnectionService;
1689             mConnectionService = null;
1690             serviceTemp.removeCall(this);
1691             serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
1692         }
1693 
1694         service.incrementAssociatedCallCount();
1695         mConnectionService = service;
1696         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1697     }
1698 
1699     /**
1700      * Clears the associated connection service.
1701      */
clearConnectionService()1702     void clearConnectionService() {
1703         if (mConnectionService != null) {
1704             ConnectionServiceWrapper serviceTemp = mConnectionService;
1705             mConnectionService = null;
1706             serviceTemp.removeCall(this);
1707 
1708             // Decrementing the count can cause the service to unbind, which itself can trigger the
1709             // service-death code.  Since the service death code tries to clean up any associated
1710             // calls, we need to make sure to remove that information (e.g., removeCall()) before
1711             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
1712             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
1713             // to do.
1714             decrementAssociatedCallCount(serviceTemp);
1715         }
1716     }
1717 
1718     /**
1719      * Starts the create connection sequence. Upon completion, there should exist an active
1720      * connection through a connection service (or the call will have failed).
1721      *
1722      * @param phoneAccountRegistrar The phone account registrar.
1723      */
startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)1724     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
1725         if (mCreateConnectionProcessor != null) {
1726             Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
1727                     " due to a race between NewOutgoingCallIntentBroadcaster and " +
1728                     "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
1729                     "invocation.");
1730             return;
1731         }
1732         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
1733                 phoneAccountRegistrar, mContext);
1734         mCreateConnectionProcessor.process();
1735     }
1736 
1737     @Override
handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection)1738     public void handleCreateConnectionSuccess(
1739             CallIdMapper idMapper,
1740             ParcelableConnection connection) {
1741         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
1742         setTargetPhoneAccount(connection.getPhoneAccount());
1743         setHandle(connection.getHandle(), connection.getHandlePresentation());
1744         setCallerDisplayName(
1745                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
1746 
1747         setConnectionCapabilities(connection.getConnectionCapabilities());
1748         setConnectionProperties(connection.getConnectionProperties());
1749         setIsVoipAudioMode(connection.getIsVoipAudioMode());
1750         setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
1751         setVideoProvider(connection.getVideoProvider());
1752         setVideoState(connection.getVideoState());
1753         setRingbackRequested(connection.isRingbackRequested());
1754         setStatusHints(connection.getStatusHints());
1755         putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());
1756 
1757         mConferenceableCalls.clear();
1758         for (String id : connection.getConferenceableConnectionIds()) {
1759             mConferenceableCalls.add(idMapper.getCall(id));
1760         }
1761 
1762         switch (mCallDirection) {
1763             case CALL_DIRECTION_INCOMING:
1764                 // Listeners (just CallsManager for now) will be responsible for checking whether
1765                 // the call should be blocked.
1766                 for (Listener l : mListeners) {
1767                     l.onSuccessfulIncomingCall(this);
1768                 }
1769                 break;
1770             case CALL_DIRECTION_OUTGOING:
1771                 for (Listener l : mListeners) {
1772                     l.onSuccessfulOutgoingCall(this,
1773                             getStateFromConnectionState(connection.getState()));
1774                 }
1775                 break;
1776             case CALL_DIRECTION_UNKNOWN:
1777                 for (Listener l : mListeners) {
1778                     l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
1779                             .getState()));
1780                 }
1781                 break;
1782         }
1783     }
1784 
1785     @Override
handleCreateConnectionFailure(DisconnectCause disconnectCause)1786     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
1787         clearConnectionService();
1788         setDisconnectCause(disconnectCause);
1789         mCallsManager.markCallAsDisconnected(this, disconnectCause);
1790 
1791         switch (mCallDirection) {
1792             case CALL_DIRECTION_INCOMING:
1793                 for (Listener listener : mListeners) {
1794                     listener.onFailedIncomingCall(this);
1795                 }
1796                 break;
1797             case CALL_DIRECTION_OUTGOING:
1798                 for (Listener listener : mListeners) {
1799                     listener.onFailedOutgoingCall(this, disconnectCause);
1800                 }
1801                 break;
1802             case CALL_DIRECTION_UNKNOWN:
1803                 for (Listener listener : mListeners) {
1804                     listener.onFailedUnknownCall(this);
1805                 }
1806                 break;
1807         }
1808     }
1809 
1810     /**
1811      * Plays the specified DTMF tone.
1812      */
1813     @VisibleForTesting
playDtmfTone(char digit)1814     public void playDtmfTone(char digit) {
1815         if (mConnectionService == null) {
1816             Log.w(this, "playDtmfTone() request on a call without a connection service.");
1817         } else {
1818             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
1819             mConnectionService.playDtmfTone(this, digit);
1820             Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
1821         }
1822         mPlayingDtmfTone = digit;
1823     }
1824 
1825     /**
1826      * Stops playing any currently playing DTMF tone.
1827      */
1828     @VisibleForTesting
stopDtmfTone()1829     public void stopDtmfTone() {
1830         if (mConnectionService == null) {
1831             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
1832         } else {
1833             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
1834             Log.addEvent(this, LogUtils.Events.STOP_DTMF);
1835             mConnectionService.stopDtmfTone(this);
1836         }
1837         mPlayingDtmfTone = NO_DTMF_TONE;
1838     }
1839 
1840     /**
1841      * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
1842      * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
1843      */
isDtmfTonePlaying()1844     boolean isDtmfTonePlaying() {
1845         return mPlayingDtmfTone != NO_DTMF_TONE;
1846     }
1847 
1848     /**
1849      * Silences the ringer.
1850      */
silence()1851     void silence() {
1852         if (mConnectionService == null) {
1853             Log.w(this, "silence() request on a call without a connection service.");
1854         } else {
1855             Log.i(this, "Send silence to connection service for call %s", this);
1856             Log.addEvent(this, LogUtils.Events.SILENCE);
1857             mConnectionService.silence(this);
1858         }
1859     }
1860 
1861     @VisibleForTesting
disconnect()1862     public void disconnect() {
1863         disconnect(0);
1864     }
1865 
1866     @VisibleForTesting
disconnect(String reason)1867     public void disconnect(String reason) {
1868         disconnect(0, reason);
1869     }
1870 
1871     /**
1872      * Attempts to disconnect the call through the connection service.
1873      */
1874     @VisibleForTesting
disconnect(long disconnectionTimeout)1875     public void disconnect(long disconnectionTimeout) {
1876         disconnect(disconnectionTimeout, "internal" /** reason */);
1877     }
1878 
1879     /**
1880      * Attempts to disconnect the call through the connection service.
1881      * @param reason the reason for the disconnect; used for logging purposes only.  In some cases
1882      *               this can be a package name if the disconnect was initiated through an API such
1883      *               as TelecomManager.
1884      */
1885     @VisibleForTesting
disconnect(long disconnectionTimeout, String reason)1886     public void disconnect(long disconnectionTimeout, String reason) {
1887         Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
1888 
1889         // Track that the call is now locally disconnecting.
1890         setLocallyDisconnecting(true);
1891         maybeSetCallAsDisconnectingChild();
1892 
1893         if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
1894                 mState == CallState.CONNECTING) {
1895             Log.v(this, "Aborting call %s", this);
1896             abort(disconnectionTimeout);
1897         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
1898             if (mConnectionService == null) {
1899                 Log.e(this, new Exception(), "disconnect() request on a call without a"
1900                         + " connection service.");
1901             } else {
1902                 Log.i(this, "Send disconnect to connection service for call: %s", this);
1903                 // The call isn't officially disconnected until the connection service
1904                 // confirms that the call was actually disconnected. Only then is the
1905                 // association between call and connection service severed, see
1906                 // {@link CallsManager#markCallAsDisconnected}.
1907                 mConnectionService.disconnect(this);
1908             }
1909         }
1910     }
1911 
abort(long disconnectionTimeout)1912     void abort(long disconnectionTimeout) {
1913         if (mCreateConnectionProcessor != null &&
1914                 !mCreateConnectionProcessor.isProcessingComplete()) {
1915             mCreateConnectionProcessor.abort();
1916         } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
1917                 || mState == CallState.CONNECTING) {
1918             if (disconnectionTimeout > 0) {
1919                 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
1920                 // milliseconds, do not destroy the call.
1921                 // Instead, we announce the cancellation and CallsManager handles
1922                 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
1923                 // then re-dial them quickly using a gateway, allowing the first call to end
1924                 // causes jank. This timeout allows CallsManager to transition the first call into
1925                 // the second call so that in-call only ever sees a single call...eliminating the
1926                 // jank altogether. The app will also be able to set the timeout via an extra on
1927                 // the ordered broadcast.
1928                 for (Listener listener : mListeners) {
1929                     if (listener.onCanceledViaNewOutgoingCallBroadcast(
1930                             this, disconnectionTimeout)) {
1931                         // The first listener to handle this wins. A return value of true means that
1932                         // the listener will handle the disconnection process later and so we
1933                         // should not continue it here.
1934                         setLocallyDisconnecting(false);
1935                         return;
1936                     }
1937                 }
1938             }
1939 
1940             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
1941         } else {
1942             Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
1943         }
1944     }
1945 
1946     /**
1947      * Answers the call if it is ringing.
1948      *
1949      * @param videoState The video state in which to answer the call.
1950      */
1951     @VisibleForTesting
answer(int videoState)1952     public void answer(int videoState) {
1953         // Check to verify that the call is still in the ringing state. A call can change states
1954         // between the time the user hits 'answer' and Telecom receives the command.
1955         if (isRinging("answer")) {
1956             if (!isVideoCallingSupportedByPhoneAccount() && VideoProfile.isVideo(videoState)) {
1957                 // Video calling is not supported, yet the InCallService is attempting to answer as
1958                 // video.  We will simply answer as audio-only.
1959                 videoState = VideoProfile.STATE_AUDIO_ONLY;
1960             }
1961             // At this point, we are asking the connection service to answer but we don't assume
1962             // that it will work. Instead, we wait until confirmation from the connectino service
1963             // that the call is in a non-STATE_RINGING state before changing the UI. See
1964             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1965             if (mConnectionService != null) {
1966                 mConnectionService.answer(this, videoState);
1967             } else {
1968                 Log.e(this, new NullPointerException(),
1969                         "answer call failed due to null CS callId=%s", getId());
1970             }
1971             Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
1972         }
1973     }
1974 
1975     /**
1976      * Deflects the call if it is ringing.
1977      *
1978      * @param address address to be deflected to.
1979      */
1980     @VisibleForTesting
deflect(Uri address)1981     public void deflect(Uri address) {
1982         // Check to verify that the call is still in the ringing state. A call can change states
1983         // between the time the user hits 'deflect' and Telecomm receives the command.
1984         if (isRinging("deflect")) {
1985             // At this point, we are asking the connection service to deflect but we don't assume
1986             // that it will work. Instead, we wait until confirmation from the connection service
1987             // that the call is in a non-STATE_RINGING state before changing the UI. See
1988             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1989             mVideoStateHistory |= mVideoState;
1990             if (mConnectionService != null) {
1991                 mConnectionService.deflect(this, address);
1992             } else {
1993                 Log.e(this, new NullPointerException(),
1994                         "deflect call failed due to null CS callId=%s", getId());
1995             }
1996             Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
1997         }
1998     }
1999 
2000     /**
2001      * Rejects the call if it is ringing.
2002      *
2003      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
2004      * @param textMessage An optional text message to send as part of the rejection.
2005      */
2006     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage)2007     public void reject(boolean rejectWithMessage, String textMessage) {
2008         reject(rejectWithMessage, textMessage, "internal" /** reason */);
2009     }
2010 
2011     /**
2012      * Rejects the call if it is ringing.
2013      *
2014      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
2015      * @param textMessage An optional text message to send as part of the rejection.
2016      * @param reason The reason for the reject; used for logging purposes.  May be a package name
2017      *               if the reject is initiated from an API such as TelecomManager.
2018      */
2019     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage, String reason)2020     public void reject(boolean rejectWithMessage, String textMessage, String reason) {
2021         // Check to verify that the call is still in the ringing state. A call can change states
2022         // between the time the user hits 'reject' and Telecomm receives the command.
2023         if (isRinging("reject")) {
2024             // Ensure video state history tracks video state at time of rejection.
2025             mVideoStateHistory |= mVideoState;
2026 
2027             if (mConnectionService != null) {
2028                 mConnectionService.reject(this, rejectWithMessage, textMessage);
2029             } else {
2030                 Log.e(this, new NullPointerException(),
2031                         "reject call failed due to null CS callId=%s", getId());
2032             }
2033             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
2034         }
2035     }
2036 
2037     /**
2038      * Puts the call on hold if it is currently active.
2039      */
2040     @VisibleForTesting
hold()2041     public void hold() {
2042         hold(null /* reason */);
2043     }
2044 
hold(String reason)2045     public void hold(String reason) {
2046         if (mState == CallState.ACTIVE) {
2047             if (mConnectionService != null) {
2048                 mConnectionService.hold(this);
2049             } else {
2050                 Log.e(this, new NullPointerException(),
2051                         "hold call failed due to null CS callId=%s", getId());
2052             }
2053             Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
2054         }
2055     }
2056 
2057     /**
2058      * Releases the call from hold if it is currently active.
2059      */
2060     @VisibleForTesting
unhold()2061     public void unhold() {
2062         unhold(null /* reason */);
2063     }
2064 
unhold(String reason)2065     public void unhold(String reason) {
2066         if (mState == CallState.ON_HOLD) {
2067             if (mConnectionService != null) {
2068                 mConnectionService.unhold(this);
2069             } else {
2070                 Log.e(this, new NullPointerException(),
2071                         "unhold call failed due to null CS callId=%s", getId());
2072             }
2073             Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
2074         }
2075     }
2076 
2077     /** Checks if this is a live call or not. */
2078     @VisibleForTesting
isAlive()2079     public boolean isAlive() {
2080         switch (mState) {
2081             case CallState.NEW:
2082             case CallState.RINGING:
2083             case CallState.ANSWERED:
2084             case CallState.DISCONNECTED:
2085             case CallState.ABORTED:
2086                 return false;
2087             default:
2088                 return true;
2089         }
2090     }
2091 
isActive()2092     boolean isActive() {
2093         return mState == CallState.ACTIVE;
2094     }
2095 
getExtras()2096     Bundle getExtras() {
2097         return mExtras;
2098     }
2099 
2100     /**
2101      * Adds extras to the extras bundle associated with this {@link Call}.
2102      *
2103      * Note: this method needs to know the source of the extras change (see
2104      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
2105      * originate from a connection service will only be notified to incall services.  Likewise,
2106      * changes originating from the incall services will only notify the connection service of the
2107      * change.
2108      *
2109      * @param source The source of the extras addition.
2110      * @param extras The extras.
2111      */
putExtras(int source, Bundle extras)2112     public void putExtras(int source, Bundle extras) {
2113         if (extras == null) {
2114             return;
2115         }
2116         if (mExtras == null) {
2117             mExtras = new Bundle();
2118         }
2119         mExtras.putAll(extras);
2120 
2121         for (Listener l : mListeners) {
2122             l.onExtrasChanged(this, source, extras);
2123         }
2124 
2125         // If the change originated from an InCallService, notify the connection service.
2126         if (source == SOURCE_INCALL_SERVICE) {
2127             if (mConnectionService != null) {
2128                 mConnectionService.onExtrasChanged(this, mExtras);
2129             } else {
2130                 Log.e(this, new NullPointerException(),
2131                         "putExtras failed due to null CS callId=%s", getId());
2132             }
2133         }
2134     }
2135 
2136     /**
2137      * Removes extras from the extras bundle associated with this {@link Call}.
2138      *
2139      * Note: this method needs to know the source of the extras change (see
2140      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
2141      * originate from a connection service will only be notified to incall services.  Likewise,
2142      * changes originating from the incall services will only notify the connection service of the
2143      * change.
2144      *
2145      * @param source The source of the extras removal.
2146      * @param keys The extra keys to remove.
2147      */
removeExtras(int source, List<String> keys)2148     void removeExtras(int source, List<String> keys) {
2149         if (mExtras == null) {
2150             return;
2151         }
2152         for (String key : keys) {
2153             mExtras.remove(key);
2154         }
2155 
2156         for (Listener l : mListeners) {
2157             l.onExtrasRemoved(this, source, keys);
2158         }
2159 
2160         // If the change originated from an InCallService, notify the connection service.
2161         if (source == SOURCE_INCALL_SERVICE) {
2162             if (mConnectionService != null) {
2163                 mConnectionService.onExtrasChanged(this, mExtras);
2164             } else {
2165                 Log.e(this, new NullPointerException(),
2166                         "removeExtras failed due to null CS callId=%s", getId());
2167             }
2168         }
2169     }
2170 
2171     @VisibleForTesting
getIntentExtras()2172     public Bundle getIntentExtras() {
2173         return mIntentExtras;
2174     }
2175 
setIntentExtras(Bundle extras)2176     void setIntentExtras(Bundle extras) {
2177         mIntentExtras = extras;
2178     }
2179 
getOriginalCallIntent()2180     public Intent getOriginalCallIntent() {
2181         return mOriginalCallIntent;
2182     }
2183 
setOriginalCallIntent(Intent intent)2184     public void setOriginalCallIntent(Intent intent) {
2185         mOriginalCallIntent = intent;
2186     }
2187 
2188     /**
2189      * @return the uri of the contact associated with this call.
2190      */
2191     @VisibleForTesting
getContactUri()2192     public Uri getContactUri() {
2193         if (mCallerInfo == null || !mCallerInfo.contactExists) {
2194             return getHandle();
2195         }
2196         return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
2197     }
2198 
getRingtone()2199     Uri getRingtone() {
2200         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
2201     }
2202 
onPostDialWait(String remaining)2203     void onPostDialWait(String remaining) {
2204         for (Listener l : mListeners) {
2205             l.onPostDialWait(this, remaining);
2206         }
2207     }
2208 
onPostDialChar(char nextChar)2209     void onPostDialChar(char nextChar) {
2210         for (Listener l : mListeners) {
2211             l.onPostDialChar(this, nextChar);
2212         }
2213     }
2214 
postDialContinue(boolean proceed)2215     void postDialContinue(boolean proceed) {
2216         if (mConnectionService != null) {
2217             mConnectionService.onPostDialContinue(this, proceed);
2218         } else {
2219             Log.e(this, new NullPointerException(),
2220                     "postDialContinue failed due to null CS callId=%s", getId());
2221         }
2222     }
2223 
conferenceWith(Call otherCall)2224     void conferenceWith(Call otherCall) {
2225         if (mConnectionService == null) {
2226             Log.w(this, "conference requested on a call without a connection service.");
2227         } else {
2228             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
2229             mConnectionService.conference(this, otherCall);
2230         }
2231     }
2232 
splitFromConference()2233     void splitFromConference() {
2234         if (mConnectionService == null) {
2235             Log.w(this, "splitting from conference call without a connection service");
2236         } else {
2237             Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
2238             mConnectionService.splitFromConference(this);
2239         }
2240     }
2241 
2242     @VisibleForTesting
mergeConference()2243     public void mergeConference() {
2244         if (mConnectionService == null) {
2245             Log.w(this, "merging conference calls without a connection service.");
2246         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
2247             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
2248             mConnectionService.mergeConference(this);
2249             mWasConferencePreviouslyMerged = true;
2250         }
2251     }
2252 
2253     @VisibleForTesting
swapConference()2254     public void swapConference() {
2255         if (mConnectionService == null) {
2256             Log.w(this, "swapping conference calls without a connection service.");
2257         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
2258             Log.addEvent(this, LogUtils.Events.SWAP);
2259             mConnectionService.swapConference(this);
2260             switch (mChildCalls.size()) {
2261                 case 1:
2262                     mConferenceLevelActiveCall = mChildCalls.get(0);
2263                     break;
2264                 case 2:
2265                     // swap
2266                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
2267                             mChildCalls.get(1) : mChildCalls.get(0);
2268                     break;
2269                 default:
2270                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
2271                     mConferenceLevelActiveCall = null;
2272                     break;
2273             }
2274         }
2275     }
2276 
2277     /**
2278      * Initiates a request to the connection service to pull this call.
2279      * <p>
2280      * This method can only be used for calls that have the
2281      * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
2282      * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
2283      * <p>
2284      * An external call is a representation of a call which is taking place on another device
2285      * associated with a PhoneAccount on this device.  Issuing a request to pull the external call
2286      * tells the {@link android.telecom.ConnectionService} that it should move the call from the
2287      * other device to this one.  An example of this is the IMS multi-endpoint functionality.  A
2288      * user may have two phones with the same phone number.  If the user is engaged in an active
2289      * call on their first device, the network will inform the second device of that ongoing call in
2290      * the form of an external call.  The user may wish to continue their conversation on the second
2291      * device, so will issue a request to pull the call to the second device.
2292      * <p>
2293      * Requests to pull a call which is not external, or a call which is not pullable are ignored.
2294      */
pullExternalCall()2295     public void pullExternalCall() {
2296         if (mConnectionService == null) {
2297             Log.w(this, "pulling a call without a connection service.");
2298         }
2299 
2300         if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
2301             Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
2302             return;
2303         }
2304 
2305         if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
2306             Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
2307             return;
2308         }
2309         Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
2310         mConnectionService.pullExternalCall(this);
2311     }
2312 
2313     /**
2314      * Sends a call event to the {@link ConnectionService} for this call. This function is
2315      * called for event other than {@link Call#EVENT_REQUEST_HANDOVER}
2316      *
2317      * @param event The call event.
2318      * @param extras Associated extras.
2319      */
sendCallEvent(String event, Bundle extras)2320     public void sendCallEvent(String event, Bundle extras) {
2321         sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras);
2322     }
2323 
2324     /**
2325      * Sends a call event to the {@link ConnectionService} for this call.
2326      *
2327      * See {@link Call#sendCallEvent(String, Bundle)}.
2328      *
2329      * @param event The call event.
2330      * @param targetSdkVer SDK version of the app calling this api
2331      * @param extras Associated extras.
2332      */
sendCallEvent(String event, int targetSdkVer, Bundle extras)2333     public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
2334         if (mConnectionService != null) {
2335             if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
2336                 if (targetSdkVer > Build.VERSION_CODES.P) {
2337                     Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
2338                             " for API > 28(P)");
2339                     // Event-based Handover APIs are deprecated, so inform the user.
2340                     mHandler.post(new Runnable() {
2341                         @Override
2342                         public void run() {
2343                             Toast.makeText(mContext, "WARNING: Event-based handover APIs are deprecated "
2344                                             + "and will no longer function in Android Q.",
2345                                     Toast.LENGTH_LONG).show();
2346                         }
2347                     });
2348 
2349                     // Uncomment and remove toast at feature complete: return;
2350                 }
2351 
2352                 // Handover requests are targeted at Telecom, not the ConnectionService.
2353                 if (extras == null) {
2354                     Log.w(this, "sendCallEvent: %s event received with null extras.",
2355                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
2356                     mConnectionService.sendCallEvent(this,
2357                             android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2358                     return;
2359                 }
2360                 Parcelable parcelable = extras.getParcelable(
2361                         android.telecom.Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE);
2362                 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
2363                     Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
2364                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
2365                     mConnectionService.sendCallEvent(this,
2366                             android.telecom.Call.EVENT_HANDOVER_FAILED, null);
2367                     return;
2368                 }
2369                 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
2370                 int videoState = extras.getInt(android.telecom.Call.EXTRA_HANDOVER_VIDEO_STATE,
2371                         VideoProfile.STATE_AUDIO_ONLY);
2372                 Parcelable handoverExtras = extras.getParcelable(
2373                         android.telecom.Call.EXTRA_HANDOVER_EXTRAS);
2374                 Bundle handoverExtrasBundle = null;
2375                 if (handoverExtras instanceof Bundle) {
2376                     handoverExtrasBundle = (Bundle) handoverExtras;
2377                 }
2378                 requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
2379             } else {
2380                 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
2381                 mConnectionService.sendCallEvent(this, event, extras);
2382             }
2383         } else {
2384             Log.e(this, new NullPointerException(),
2385                     "sendCallEvent failed due to null CS callId=%s", getId());
2386         }
2387     }
2388 
2389     /**
2390      * Initiates a handover of this Call to the {@link ConnectionService} identified
2391      * by destAcct.
2392      * @param destAcct ConnectionService to which the call should be handed over.
2393      * @param videoState The video state desired after the handover.
2394      * @param extras Extra information to be passed to ConnectionService
2395      */
handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras)2396     public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
2397         requestHandover(destAcct, videoState, extras, false);
2398     }
2399 
2400     /**
2401      * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
2402      * have this call as a child.
2403      * @param parentCall
2404      */
setParentAndChildCall(Call parentCall)2405     void setParentAndChildCall(Call parentCall) {
2406         boolean isParentChanging = (mParentCall != parentCall);
2407         setParentCall(parentCall);
2408         setChildOf(parentCall);
2409         if (isParentChanging) {
2410             notifyParentChanged(parentCall);
2411         }
2412     }
2413 
2414     /**
2415      * Notifies listeners when the parent call changes.
2416      * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
2417      * @param parentCall The new parent call for this call.
2418      */
notifyParentChanged(Call parentCall)2419     void notifyParentChanged(Call parentCall) {
2420         Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
2421         for (Listener l : mListeners) {
2422             l.onParentChanged(this);
2423         }
2424     }
2425 
2426     /**
2427      * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
2428      * the child.
2429      * TODO: This is only required when adding existing connections as a workaround so that we
2430      * can avoid sending the "onParentChanged" callback until later.
2431      * @param parentCall The new parent call.
2432      */
setParentCall(Call parentCall)2433     void setParentCall(Call parentCall) {
2434         if (parentCall == this) {
2435             Log.e(this, new Exception(), "setting the parent to self");
2436             return;
2437         }
2438         if (parentCall == mParentCall) {
2439             // nothing to do
2440             return;
2441         }
2442         if (mParentCall != null) {
2443             mParentCall.removeChildCall(this);
2444         }
2445         mParentCall = parentCall;
2446     }
2447 
2448     /**
2449      * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
2450      * this call as a child of another call.
2451      * <p>
2452      * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
2453      * ensure the InCall UI is updated with the change in parent.
2454      * @param parentCall The new parent for this call.
2455      */
setChildOf(Call parentCall)2456     void setChildOf(Call parentCall) {
2457         if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
2458             parentCall.addChildCall(this);
2459         }
2460     }
2461 
setConferenceableCalls(List<Call> conferenceableCalls)2462     void setConferenceableCalls(List<Call> conferenceableCalls) {
2463         mConferenceableCalls.clear();
2464         mConferenceableCalls.addAll(conferenceableCalls);
2465 
2466         for (Listener l : mListeners) {
2467             l.onConferenceableCallsChanged(this);
2468         }
2469     }
2470 
2471     @VisibleForTesting
getConferenceableCalls()2472     public List<Call> getConferenceableCalls() {
2473         return mConferenceableCalls;
2474     }
2475 
2476     @VisibleForTesting
can(int capability)2477     public boolean can(int capability) {
2478         return (mConnectionCapabilities & capability) == capability;
2479     }
2480 
2481     @VisibleForTesting
hasProperty(int property)2482     public boolean hasProperty(int property) {
2483         return (mConnectionProperties & property) == property;
2484     }
2485 
addChildCall(Call call)2486     private void addChildCall(Call call) {
2487         if (!mChildCalls.contains(call)) {
2488             mHadChildren = true;
2489             // Set the pseudo-active call to the latest child added to the conference.
2490             // See definition of mConferenceLevelActiveCall for more detail.
2491             mConferenceLevelActiveCall = call;
2492             mChildCalls.add(call);
2493 
2494             Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
2495 
2496             for (Listener l : mListeners) {
2497                 l.onChildrenChanged(this);
2498             }
2499         }
2500     }
2501 
removeChildCall(Call call)2502     private void removeChildCall(Call call) {
2503         if (mChildCalls.remove(call)) {
2504             Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
2505             for (Listener l : mListeners) {
2506                 l.onChildrenChanged(this);
2507             }
2508         }
2509     }
2510 
2511     /**
2512      * Return whether the user can respond to this {@code Call} via an SMS message.
2513      *
2514      * @return true if the "Respond via SMS" feature should be enabled
2515      * for this incoming call.
2516      *
2517      * The general rule is that we *do* allow "Respond via SMS" except for
2518      * the few (relatively rare) cases where we know for sure it won't
2519      * work, namely:
2520      *   - a bogus or blank incoming number
2521      *   - a call from a SIP address
2522      *   - a "call presentation" that doesn't allow the number to be revealed
2523      *
2524      * In all other cases, we allow the user to respond via SMS.
2525      *
2526      * Note that this behavior isn't perfect; for example we have no way
2527      * to detect whether the incoming call is from a landline (with most
2528      * networks at least), so we still enable this feature even though
2529      * SMSes to that number will silently fail.
2530      */
isRespondViaSmsCapable()2531     public boolean isRespondViaSmsCapable() {
2532         if (mState != CallState.RINGING) {
2533             return false;
2534         }
2535 
2536         if (getHandle() == null) {
2537             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
2538             // other words, the user should not be able to see the incoming phone number.
2539             return false;
2540         }
2541 
2542         if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
2543             // The incoming number is actually a URI (i.e. a SIP address),
2544             // not a regular PSTN phone number, and we can't send SMSes to
2545             // SIP addresses.
2546             // (TODO: That might still be possible eventually, though. Is
2547             // there some SIP-specific equivalent to sending a text message?)
2548             return false;
2549         }
2550 
2551         // Is there a valid SMS application on the phone?
2552         if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
2553                 true /*updateIfNeeded*/) == null) {
2554             return false;
2555         }
2556 
2557         // TODO: with some carriers (in certain countries) you *can* actually
2558         // tell whether a given number is a mobile phone or not. So in that
2559         // case we could potentially return false here if the incoming call is
2560         // from a land line.
2561 
2562         // If none of the above special cases apply, it's OK to enable the
2563         // "Respond via SMS" feature.
2564         return true;
2565     }
2566 
getCannedSmsResponses()2567     List<String> getCannedSmsResponses() {
2568         return mCannedSmsResponses;
2569     }
2570 
2571     /**
2572      * We need to make sure that before we move a call to the disconnected state, it no
2573      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
2574      * Service always has the right data in the right order.  We also want to do it in telecom so
2575      * that the insurance policy lives in the framework side of things.
2576      */
fixParentAfterDisconnect()2577     private void fixParentAfterDisconnect() {
2578         setParentAndChildCall(null);
2579     }
2580 
2581     /**
2582      * @return True if the call is ringing, else logs the action name.
2583      */
isRinging(String actionName)2584     private boolean isRinging(String actionName) {
2585         if (mState == CallState.RINGING) {
2586             return true;
2587         }
2588 
2589         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
2590         return false;
2591     }
2592 
2593     @SuppressWarnings("rawtypes")
decrementAssociatedCallCount(ServiceBinder binder)2594     private void decrementAssociatedCallCount(ServiceBinder binder) {
2595         if (binder != null) {
2596             binder.decrementAssociatedCallCount();
2597         }
2598     }
2599 
2600     /**
2601      * Looks up contact information based on the current handle.
2602      */
startCallerInfoLookup()2603     private void startCallerInfoLookup() {
2604         mCallerInfo = null;
2605         mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
2606     }
2607 
2608     /**
2609      * Saves the specified caller info if the specified token matches that of the last query
2610      * that was made.
2611      *
2612      * @param callerInfo The new caller information to set.
2613      */
setCallerInfo(Uri handle, CallerInfo callerInfo)2614     private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
2615         Trace.beginSection("setCallerInfo");
2616         if (callerInfo == null) {
2617             Log.i(this, "CallerInfo lookup returned null, skipping update");
2618             return;
2619         }
2620 
2621         if ((handle != null) && !handle.equals(mHandle)) {
2622             Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
2623             return;
2624         }
2625 
2626         mCallerInfo = callerInfo;
2627         Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
2628 
2629         if (mCallerInfo.contactDisplayPhotoUri == null ||
2630                 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) {
2631             for (Listener l : mListeners) {
2632                 l.onCallerInfoChanged(this);
2633             }
2634         }
2635 
2636         Trace.endSection();
2637     }
2638 
getCallerInfo()2639     public CallerInfo getCallerInfo() {
2640         return mCallerInfo;
2641     }
2642 
maybeLoadCannedSmsResponses()2643     private void maybeLoadCannedSmsResponses() {
2644         if (mCallDirection == CALL_DIRECTION_INCOMING
2645                 && isRespondViaSmsCapable()
2646                 && !mCannedSmsResponsesLoadingStarted) {
2647             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
2648             mCannedSmsResponsesLoadingStarted = true;
2649             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
2650                     new Response<Void, List<String>>() {
2651                         @Override
2652                         public void onResult(Void request, List<String>... result) {
2653                             if (result.length > 0) {
2654                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
2655                                 mCannedSmsResponses = result[0];
2656                                 for (Listener l : mListeners) {
2657                                     l.onCannedSmsResponsesLoaded(Call.this);
2658                                 }
2659                             }
2660                         }
2661 
2662                         @Override
2663                         public void onError(Void request, int code, String msg) {
2664                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
2665                                     msg);
2666                         }
2667                     },
2668                     mContext
2669             );
2670         } else {
2671             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
2672         }
2673     }
2674 
2675     /**
2676      * Sets speakerphone option on when call begins.
2677      */
setStartWithSpeakerphoneOn(boolean startWithSpeakerphone)2678     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
2679         mSpeakerphoneOn = startWithSpeakerphone;
2680     }
2681 
2682     /**
2683      * Returns speakerphone option.
2684      *
2685      * @return Whether or not speakerphone should be set automatically when call begins.
2686      */
getStartWithSpeakerphoneOn()2687     public boolean getStartWithSpeakerphoneOn() {
2688         return mSpeakerphoneOn;
2689     }
2690 
setRequestedToStartWithRtt()2691     public void setRequestedToStartWithRtt() {
2692         mDidRequestToStartWithRtt = true;
2693     }
2694 
stopRtt()2695     public void stopRtt() {
2696         if (mConnectionService != null) {
2697             mConnectionService.stopRtt(this);
2698         } else {
2699             // If this gets called by the in-call app before the connection service is set, we'll
2700             // just ignore it since it's really not supposed to happen.
2701             Log.w(this, "stopRtt() called before connection service is set.");
2702         }
2703     }
2704 
sendRttRequest()2705     public void sendRttRequest() {
2706         createRttStreams();
2707         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2708     }
2709 
areRttStreamsInitialized()2710     private boolean areRttStreamsInitialized() {
2711         return mInCallToConnectionServiceStreams != null
2712                 && mConnectionServiceToInCallStreams != null;
2713     }
2714 
createRttStreams()2715     public void createRttStreams() {
2716         if (!areRttStreamsInitialized()) {
2717             Log.i(this, "Initializing RTT streams");
2718             try {
2719                 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
2720                 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
2721             } catch (IOException e) {
2722                 Log.e(this, e, "Failed to create pipes for RTT call.");
2723             }
2724         }
2725     }
2726 
onRttConnectionFailure(int reason)2727     public void onRttConnectionFailure(int reason) {
2728         Log.i(this, "Got RTT initiation failure with reason %d", reason);
2729         for (Listener l : mListeners) {
2730             l.onRttInitiationFailure(this, reason);
2731         }
2732     }
2733 
onRemoteRttRequest()2734     public void onRemoteRttRequest() {
2735         if (isRttCall()) {
2736             Log.w(this, "Remote RTT request on a call that's already RTT");
2737             return;
2738         }
2739 
2740         mPendingRttRequestId = mCallsManager.getNextRttRequestId();
2741         for (Listener l : mListeners) {
2742             l.onRemoteRttRequest(this, mPendingRttRequestId);
2743         }
2744     }
2745 
handleRttRequestResponse(int id, boolean accept)2746     public void handleRttRequestResponse(int id, boolean accept) {
2747         if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
2748             Log.w(this, "Response received to a nonexistent RTT request: %d", id);
2749             return;
2750         }
2751         if (id != mPendingRttRequestId) {
2752             Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
2753             return;
2754         }
2755         if (accept) {
2756             createRttStreams();
2757             Log.i(this, "RTT request %d accepted.", id);
2758             mConnectionService.respondToRttRequest(
2759                     this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2760         } else {
2761             Log.i(this, "RTT request %d rejected.", id);
2762             mConnectionService.respondToRttRequest(this, null, null);
2763         }
2764     }
2765 
isRttCall()2766     public boolean isRttCall() {
2767         return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2768     }
2769 
wasEverRttCall()2770     public boolean wasEverRttCall() {
2771         return mWasEverRtt;
2772     }
2773 
getCsToInCallRttPipeForCs()2774     public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
2775         return mConnectionServiceToInCallStreams == null ? null
2776                 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2777     }
2778 
getInCallToCsRttPipeForCs()2779     public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
2780         return mInCallToConnectionServiceStreams == null ? null
2781                 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
2782     }
2783 
getCsToInCallRttPipeForInCall()2784     public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
2785         return mConnectionServiceToInCallStreams == null ? null
2786                 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
2787     }
2788 
getInCallToCsRttPipeForInCall()2789     public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
2790         return mInCallToConnectionServiceStreams == null ? null
2791                 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2792     }
2793 
getRttMode()2794     public int getRttMode() {
2795         return mRttMode;
2796     }
2797 
2798     /**
2799      * Sets a video call provider for the call.
2800      */
setVideoProvider(IVideoProvider videoProvider)2801     public void setVideoProvider(IVideoProvider videoProvider) {
2802         Log.v(this, "setVideoProvider");
2803 
2804         if (mVideoProviderProxy != null) {
2805             mVideoProviderProxy.clearVideoCallback();
2806             mVideoProviderProxy = null;
2807         }
2808 
2809         if (videoProvider != null ) {
2810             try {
2811                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
2812                         mCallsManager);
2813             } catch (RemoteException ignored) {
2814                 // Ignore RemoteException.
2815             }
2816         }
2817 
2818         mVideoProvider = videoProvider;
2819 
2820         for (Listener l : mListeners) {
2821             l.onVideoCallProviderChanged(Call.this);
2822         }
2823     }
2824 
2825     /**
2826      * @return The {@link Connection.VideoProvider} binder.
2827      */
getVideoProvider()2828     public IVideoProvider getVideoProvider() {
2829         if (mVideoProviderProxy == null) {
2830             return null;
2831         }
2832 
2833         return mVideoProviderProxy.getInterface();
2834     }
2835 
2836     /**
2837      * @return The {@link VideoProviderProxy} for this call.
2838      */
getVideoProviderProxy()2839     public VideoProviderProxy getVideoProviderProxy() {
2840         return mVideoProviderProxy;
2841     }
2842 
2843     /**
2844      * The current video state for the call.
2845      * See {@link VideoProfile} for a list of valid video states.
2846      */
getVideoState()2847     public int getVideoState() {
2848         return mVideoState;
2849     }
2850 
2851     /**
2852      * Returns the video states which were applicable over the duration of a call.
2853      * See {@link VideoProfile} for a list of valid video states.
2854      *
2855      * @return The video states applicable over the duration of the call.
2856      */
getVideoStateHistory()2857     public int getVideoStateHistory() {
2858         return mVideoStateHistory;
2859     }
2860 
2861     /**
2862      * Determines the current video state for the call.
2863      * For an outgoing call determines the desired video state for the call.
2864      * Valid values: see {@link VideoProfile}
2865      *
2866      * @param videoState The video state for the call.
2867      */
setVideoState(int videoState)2868     public void setVideoState(int videoState) {
2869         // If the phone account associated with this call does not support video calling, then we
2870         // will automatically set the video state to audio-only.
2871         if (!isVideoCallingSupportedByPhoneAccount()) {
2872             Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
2873                     VideoProfile.videoStateToString(videoState));
2874             videoState = VideoProfile.STATE_AUDIO_ONLY;
2875         }
2876 
2877         // Track Video State history during the duration of the call.
2878         // Only update the history when the call is active or disconnected. This ensures we do
2879         // not include the video state history when:
2880         // - Call is incoming (but not answered).
2881         // - Call it outgoing (but not answered).
2882         // We include the video state when disconnected to ensure that rejected calls reflect the
2883         // appropriate video state.
2884         // For all other times we add to the video state history, see #setState.
2885         if (isActive() || getState() == CallState.DISCONNECTED) {
2886             mVideoStateHistory = mVideoStateHistory | videoState;
2887         }
2888 
2889         int previousVideoState = mVideoState;
2890         mVideoState = videoState;
2891         if (mVideoState != previousVideoState) {
2892             Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
2893                     VideoProfile.videoStateToString(videoState));
2894             for (Listener l : mListeners) {
2895                 l.onVideoStateChanged(this, previousVideoState, mVideoState);
2896             }
2897         }
2898 
2899         if (VideoProfile.isVideo(videoState)) {
2900             mAnalytics.setCallIsVideo(true);
2901         }
2902     }
2903 
getIsVoipAudioMode()2904     public boolean getIsVoipAudioMode() {
2905         return mIsVoipAudioMode;
2906     }
2907 
setIsVoipAudioMode(boolean audioModeIsVoip)2908     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
2909         mIsVoipAudioMode = audioModeIsVoip;
2910         for (Listener l : mListeners) {
2911             l.onIsVoipAudioModeChanged(this);
2912         }
2913     }
2914 
getStatusHints()2915     public StatusHints getStatusHints() {
2916         return mStatusHints;
2917     }
2918 
setStatusHints(StatusHints statusHints)2919     public void setStatusHints(StatusHints statusHints) {
2920         mStatusHints = statusHints;
2921         for (Listener l : mListeners) {
2922             l.onStatusHintsChanged(this);
2923         }
2924     }
2925 
isUnknown()2926     public boolean isUnknown() {
2927         return mCallDirection == CALL_DIRECTION_UNKNOWN;
2928     }
2929 
2930     /**
2931      * Determines if this call is in a disconnecting state.
2932      *
2933      * @return {@code true} if this call is locally disconnecting.
2934      */
isLocallyDisconnecting()2935     public boolean isLocallyDisconnecting() {
2936         return mIsLocallyDisconnecting;
2937     }
2938 
2939     /**
2940      * Sets whether this call is in a disconnecting state.
2941      *
2942      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
2943      */
setLocallyDisconnecting(boolean isLocallyDisconnecting)2944     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
2945         mIsLocallyDisconnecting = isLocallyDisconnecting;
2946     }
2947 
2948     /**
2949      * @return user handle of user initiating the outgoing call.
2950      */
getInitiatingUser()2951     public UserHandle getInitiatingUser() {
2952         return mInitiatingUser;
2953     }
2954 
2955     /**
2956      * Set the user handle of user initiating the outgoing call.
2957      * @param initiatingUser
2958      */
setInitiatingUser(UserHandle initiatingUser)2959     public void setInitiatingUser(UserHandle initiatingUser) {
2960         Preconditions.checkNotNull(initiatingUser);
2961         mInitiatingUser = initiatingUser;
2962     }
2963 
getStateFromConnectionState(int state)2964     static int getStateFromConnectionState(int state) {
2965         switch (state) {
2966             case Connection.STATE_INITIALIZING:
2967                 return CallState.CONNECTING;
2968             case Connection.STATE_ACTIVE:
2969                 return CallState.ACTIVE;
2970             case Connection.STATE_DIALING:
2971                 return CallState.DIALING;
2972             case Connection.STATE_PULLING_CALL:
2973                 return CallState.PULLING;
2974             case Connection.STATE_DISCONNECTED:
2975                 return CallState.DISCONNECTED;
2976             case Connection.STATE_HOLDING:
2977                 return CallState.ON_HOLD;
2978             case Connection.STATE_NEW:
2979                 return CallState.NEW;
2980             case Connection.STATE_RINGING:
2981                 return CallState.RINGING;
2982         }
2983         return CallState.DISCONNECTED;
2984     }
2985 
2986     /**
2987      * Determines if this call is in disconnected state and waiting to be destroyed.
2988      *
2989      * @return {@code true} if this call is disconected.
2990      */
isDisconnected()2991     public boolean isDisconnected() {
2992         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
2993     }
2994 
2995     /**
2996      * Determines if this call has just been created and has not been configured properly yet.
2997      *
2998      * @return {@code true} if this call is new.
2999      */
isNew()3000     public boolean isNew() {
3001         return getState() == CallState.NEW;
3002     }
3003 
3004     /**
3005      * Sets the call data usage for the call.
3006      *
3007      * @param callDataUsage The new call data usage (in bytes).
3008      */
setCallDataUsage(long callDataUsage)3009     public void setCallDataUsage(long callDataUsage) {
3010         mCallDataUsage = callDataUsage;
3011     }
3012 
3013     /**
3014      * Returns the call data usage for the call.
3015      *
3016      * @return The call data usage (in bytes).
3017      */
getCallDataUsage()3018     public long getCallDataUsage() {
3019         return mCallDataUsage;
3020     }
3021 
setRttMode(int mode)3022     public void setRttMode(int mode) {
3023         mRttMode = mode;
3024         // TODO: hook this up to CallAudioManager
3025     }
3026 
3027     /**
3028      * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
3029      * has come back to telecom and was processed.
3030      */
isNewOutgoingCallIntentBroadcastDone()3031     public boolean isNewOutgoingCallIntentBroadcastDone() {
3032         return mIsNewOutgoingCallIntentBroadcastDone;
3033     }
3034 
setNewOutgoingCallIntentBroadcastIsDone()3035     public void setNewOutgoingCallIntentBroadcastIsDone() {
3036         mIsNewOutgoingCallIntentBroadcastDone = true;
3037     }
3038 
3039     /**
3040      * Determines if the call has been held by the remote party.
3041      *
3042      * @return {@code true} if the call is remotely held, {@code false} otherwise.
3043      */
isRemotelyHeld()3044     public boolean isRemotelyHeld() {
3045         return mIsRemotelyHeld;
3046     }
3047 
3048     /**
3049      * Handles Connection events received from a {@link ConnectionService}.
3050      *
3051      * @param event The event.
3052      * @param extras The extras.
3053      */
onConnectionEvent(String event, Bundle extras)3054     public void onConnectionEvent(String event, Bundle extras) {
3055         Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
3056         if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
3057             mIsRemotelyHeld = true;
3058             Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
3059             // Inform listeners of the fact that a call hold tone was received.  This will trigger
3060             // the CallAudioManager to play a tone via the InCallTonePlayer.
3061             for (Listener l : mListeners) {
3062                 l.onHoldToneRequested(this);
3063             }
3064         } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
3065             mIsRemotelyHeld = false;
3066             Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
3067             for (Listener l : mListeners) {
3068                 l.onHoldToneRequested(this);
3069             }
3070         } else if (Connection.EVENT_CALL_HOLD_FAILED.equals(event)) {
3071             for (Listener l : mListeners) {
3072                 l.onCallHoldFailed(this);
3073             }
3074         } else {
3075             for (Listener l : mListeners) {
3076                 l.onConnectionEvent(this, event, extras);
3077             }
3078         }
3079     }
3080 
3081     /**
3082      * Notifies interested parties that the handover has completed.
3083      * Notifies:
3084      * 1. {@link InCallController} which communicates this to the
3085      * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
3086      * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
3087      * the successful handover.
3088      */
onHandoverComplete()3089     public void onHandoverComplete() {
3090         Log.i(this, "onHandoverComplete; callId=%s", getId());
3091         if (mConnectionService != null) {
3092             mConnectionService.handoverComplete(this);
3093         }
3094         for (Listener l : mListeners) {
3095             l.onHandoverComplete(this);
3096         }
3097     }
3098 
onHandoverFailed(int handoverError)3099     public void onHandoverFailed(int handoverError) {
3100         Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
3101         for (Listener l : mListeners) {
3102             l.onHandoverFailed(this, handoverError);
3103         }
3104     }
3105 
setOriginalConnectionId(String originalConnectionId)3106     public void setOriginalConnectionId(String originalConnectionId) {
3107         mOriginalConnectionId = originalConnectionId;
3108     }
3109 
3110     /**
3111      * For calls added via a ConnectionManager using the
3112      * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
3113      * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
3114      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
3115      * originally created it.
3116      *
3117      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
3118      * @return The original connection ID.
3119      */
getOriginalConnectionId()3120     public String getOriginalConnectionId() {
3121         return mOriginalConnectionId;
3122     }
3123 
getConnectionServiceFocusManager()3124     public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
3125         return mCallsManager.getConnectionServiceFocusManager();
3126     }
3127 
3128     /**
3129      * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
3130      * remotely or locally.
3131      *
3132      * @param capabilities The {@link Connection} capabilities for the call.
3133      * @return {@code true} if video is supported, {@code false} otherwise.
3134      */
doesCallSupportVideo(int capabilities)3135     private boolean doesCallSupportVideo(int capabilities) {
3136         return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
3137                 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
3138     }
3139 
3140     /**
3141      * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
3142      *
3143      * @param capabilities The capabilities.
3144      * @return The bitmask with video capabilities removed.
3145      */
removeVideoCapabilities(int capabilities)3146     private int removeVideoCapabilities(int capabilities) {
3147         return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
3148                 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
3149     }
3150 
3151     /**
3152      * Initiates a handover of this {@link Call} to another {@link PhoneAccount}.
3153      * @param handoverToHandle The {@link PhoneAccountHandle} to handover to.
3154      * @param videoState The video state of the call when handed over.
3155      * @param extras Optional extras {@link Bundle} provided by the initiating
3156      *      {@link android.telecom.InCallService}.
3157      */
requestHandover(PhoneAccountHandle handoverToHandle, int videoState, Bundle extras, boolean isLegacy)3158     private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
3159                                  Bundle extras, boolean isLegacy) {
3160         for (Listener l : mListeners) {
3161             l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
3162         }
3163     }
3164 
3165     /**
3166      * Sets whether this {@link Call} is a conference or not.
3167      * @param isConference
3168      */
setConferenceState(boolean isConference)3169     public void setConferenceState(boolean isConference) {
3170         mIsConference = isConference;
3171         Log.addEvent(this, LogUtils.Events.CONF_STATE_CHANGED, "isConference=" + isConference);
3172         // Ultimately CallsManager needs to know so it can update the "add call" state and inform
3173         // the UI to update itself.
3174         for (Listener l : mListeners) {
3175             l.onConferenceStateChanged(this, isConference);
3176         }
3177     }
3178 
3179     /**
3180      * Sets the video history based on the state and state transitions of the call. Always add the
3181      * current video state to the video state history during a call transition except for the
3182      * transitions DIALING->ACTIVE and RINGING->ANSWERED. In these cases, clear the history. If a
3183      * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record
3184      * the history as an audio call.
3185      */
updateVideoHistoryViaState(int oldState, int newState)3186     private void updateVideoHistoryViaState(int oldState, int newState) {
3187         if ((oldState == CallState.DIALING && newState == CallState.ACTIVE)
3188                 || (oldState == CallState.RINGING && newState == CallState.ANSWERED)) {
3189             mVideoStateHistory = mVideoState;
3190         }
3191 
3192         mVideoStateHistory |= mVideoState;
3193     }
3194 
3195     /**
3196      * Returns whether or not high definition audio was used.
3197      *
3198      * @return true if high definition audio was used during this call.
3199      */
wasHighDefAudio()3200     boolean wasHighDefAudio() {
3201         return mWasHighDefAudio;
3202     }
3203 
setIsUsingCallFiltering(boolean isUsingCallFiltering)3204     public void setIsUsingCallFiltering(boolean isUsingCallFiltering) {
3205         mIsUsingCallFiltering = isUsingCallFiltering;
3206     }
3207 
3208     /**
3209      * When upgrading a call to video via
3210      * {@link VideoProviderProxy#onSendSessionModifyRequest(VideoProfile, VideoProfile)}, if the
3211      * upgrade is from audio to video, potentially auto-engage the speakerphone.
3212      * @param newVideoState The proposed new video state for the call.
3213      */
maybeEnableSpeakerForVideoUpgrade(@ideoProfile.VideoState int newVideoState)3214     public void maybeEnableSpeakerForVideoUpgrade(@VideoProfile.VideoState int newVideoState) {
3215         if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) {
3216             Log.i(this, "maybeEnableSpeakerForVideoCall; callId=%s, auto-enable speaker for call"
3217                             + " upgraded to video.");
3218             mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
3219         }
3220     }
3221 
3222     /**
3223      * Remaps the call direction as indicated by an {@link android.telecom.Call.Details} direction
3224      * constant to the constants (e.g. {@link #CALL_DIRECTION_INCOMING}) used in this call class.
3225      * @param direction The android.telecom.Call direction.
3226      * @return The direction using the constants in this class.
3227      */
getRemappedCallDirection( @ndroid.telecom.Call.Details.CallDirection int direction)3228     public static int getRemappedCallDirection(
3229             @android.telecom.Call.Details.CallDirection int direction) {
3230         switch(direction) {
3231             case android.telecom.Call.Details.DIRECTION_INCOMING:
3232                 return CALL_DIRECTION_INCOMING;
3233             case android.telecom.Call.Details.DIRECTION_OUTGOING:
3234                 return CALL_DIRECTION_OUTGOING;
3235             case android.telecom.Call.Details.DIRECTION_UNKNOWN:
3236                 return CALL_DIRECTION_UNDEFINED;
3237         }
3238         return CALL_DIRECTION_UNDEFINED;
3239     }
3240 }
3241