• 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 static android.provider.CallLog.Calls.MISSED_REASON_NOT_MISSED;
20 import static android.telecom.Call.EVENT_DISPLAY_SOS_MESSAGE;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.graphics.Bitmap;
27 import android.graphics.drawable.Drawable;
28 import android.net.Uri;
29 import android.os.Build;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.Looper;
33 import android.os.ParcelFileDescriptor;
34 import android.os.Parcelable;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.os.Trace;
38 import android.os.UserHandle;
39 import android.provider.CallLog;
40 import android.provider.ContactsContract.Contacts;
41 import android.telecom.BluetoothCallQualityReport;
42 import android.telecom.CallAttributes;
43 import android.telecom.CallAudioState;
44 import android.telecom.CallDiagnosticService;
45 import android.telecom.CallDiagnostics;
46 import android.telecom.CallerInfo;
47 import android.telecom.Conference;
48 import android.telecom.Connection;
49 import android.telecom.ConnectionService;
50 import android.telecom.DisconnectCause;
51 import android.telecom.GatewayInfo;
52 import android.telecom.Log;
53 import android.telecom.Logging.EventManager;
54 import android.telecom.ParcelableConference;
55 import android.telecom.ParcelableConnection;
56 import android.telecom.PhoneAccount;
57 import android.telecom.PhoneAccountHandle;
58 import android.telecom.Response;
59 import android.telecom.StatusHints;
60 import android.telecom.TelecomManager;
61 import android.telecom.VideoProfile;
62 import android.telephony.CallQuality;
63 import android.telephony.PhoneNumberUtils;
64 import android.telephony.TelephonyManager;
65 import android.telephony.emergency.EmergencyNumber;
66 import android.telephony.ims.ImsReasonInfo;
67 import android.text.TextUtils;
68 import android.widget.Toast;
69 
70 import com.android.internal.annotations.VisibleForTesting;
71 import com.android.internal.telecom.IVideoProvider;
72 import com.android.internal.util.Preconditions;
73 import com.android.server.telecom.stats.CallFailureCause;
74 import com.android.server.telecom.stats.CallStateChangedAtomWriter;
75 import com.android.server.telecom.ui.ToastFactory;
76 
77 import java.io.IOException;
78 import java.text.SimpleDateFormat;
79 import java.util.ArrayList;
80 import java.util.Collection;
81 import java.util.Collections;
82 import java.util.Date;
83 import java.util.LinkedList;
84 import java.util.List;
85 import java.util.Locale;
86 import java.util.Map;
87 import java.util.Objects;
88 import java.util.Set;
89 import java.util.concurrent.CompletableFuture;
90 import java.util.concurrent.ConcurrentHashMap;
91 import java.util.concurrent.TimeUnit;
92 import java.util.stream.Collectors;
93 
94 /**
95  *  Encapsulates all aspects of a given phone call throughout its lifecycle, starting
96  *  from the time the call intent was received by Telecom (vs. the time the call was
97  *  connected etc).
98  */
99 public class Call implements CreateConnectionResponse, EventManager.Loggable,
100         ConnectionServiceFocusManager.CallFocus {
101     public final static String CALL_ID_UNKNOWN = "-1";
102     public final static long DATA_USAGE_NOT_SET = -1;
103 
104     public static final int CALL_DIRECTION_UNDEFINED = 0;
105     public static final int CALL_DIRECTION_OUTGOING = 1;
106     public static final int CALL_DIRECTION_INCOMING = 2;
107     public static final int CALL_DIRECTION_UNKNOWN = 3;
108 
109     /** Identifies extras changes which originated from a connection service. */
110     public static final int SOURCE_CONNECTION_SERVICE = 1;
111     /** Identifies extras changes which originated from an incall service. */
112     public static final int SOURCE_INCALL_SERVICE = 2;
113 
114     private static final int RTT_PIPE_READ_SIDE_INDEX = 0;
115     private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1;
116 
117     private static final int INVALID_RTT_REQUEST_ID = -1;
118 
119     private static final char NO_DTMF_TONE = '\0';
120 
121     /**
122      * Listener for events on the call.
123      */
124     public interface Listener {
onSuccessfulOutgoingCall(Call call, int callState)125         default void onSuccessfulOutgoingCall(Call call, int callState) {};
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)126         default void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {};
onSuccessfulIncomingCall(Call call)127         default void onSuccessfulIncomingCall(Call call) {};
onFailedIncomingCall(Call call)128         default void onFailedIncomingCall(Call call) {};
onSuccessfulUnknownCall(Call call, int callState)129         default void onSuccessfulUnknownCall(Call call, int callState) {};
onFailedUnknownCall(Call call)130         default void onFailedUnknownCall(Call call) {};
onRingbackRequested(Call call, boolean ringbackRequested)131         default void onRingbackRequested(Call call, boolean ringbackRequested) {};
onPostDialWait(Call call, String remaining)132         default void onPostDialWait(Call call, String remaining) {};
onPostDialChar(Call call, char nextChar)133         default void onPostDialChar(Call call, char nextChar) {};
onConnectionCapabilitiesChanged(Call call)134         default void onConnectionCapabilitiesChanged(Call call) {};
onConnectionPropertiesChanged(Call call, boolean didRttChange)135         default void onConnectionPropertiesChanged(Call call, boolean didRttChange) {};
onParentChanged(Call call)136         default void onParentChanged(Call call) {};
onChildrenChanged(Call call)137         default void onChildrenChanged(Call call) {};
onCannedSmsResponsesLoaded(Call call)138         default void onCannedSmsResponsesLoaded(Call call) {};
onVideoCallProviderChanged(Call call)139         default void onVideoCallProviderChanged(Call call) {};
onCallerInfoChanged(Call call)140         default void onCallerInfoChanged(Call call) {};
onIsVoipAudioModeChanged(Call call)141         default void onIsVoipAudioModeChanged(Call call) {};
onStatusHintsChanged(Call call)142         default void onStatusHintsChanged(Call call) {};
onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)143         default void onExtrasChanged(Call c, int source, Bundle extras,
144                 String requestingPackageName) {};
onExtrasRemoved(Call c, int source, List<String> keys)145         default void onExtrasRemoved(Call c, int source, List<String> keys) {};
onHandleChanged(Call call)146         default void onHandleChanged(Call call) {};
onCallerDisplayNameChanged(Call call)147         default void onCallerDisplayNameChanged(Call call) {};
onCallDirectionChanged(Call call)148         default void onCallDirectionChanged(Call call) {};
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)149         default void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {};
onTargetPhoneAccountChanged(Call call)150         default void onTargetPhoneAccountChanged(Call call) {};
onConnectionManagerPhoneAccountChanged(Call call)151         default void onConnectionManagerPhoneAccountChanged(Call call) {};
onPhoneAccountChanged(Call call)152         default void onPhoneAccountChanged(Call call) {};
onConferenceableCallsChanged(Call call)153         default void onConferenceableCallsChanged(Call call) {};
onConferenceStateChanged(Call call, boolean isConference)154         default void onConferenceStateChanged(Call call, boolean isConference) {};
onCdmaConferenceSwap(Call call)155         default void onCdmaConferenceSwap(Call call) {};
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)156         default boolean onCanceledViaNewOutgoingCallBroadcast(Call call,
157                 long disconnectionTimeout) {
158             return false;
159         };
onHoldToneRequested(Call call)160         default void onHoldToneRequested(Call call) {};
onCallHoldFailed(Call call)161         default void onCallHoldFailed(Call call) {};
onCallSwitchFailed(Call call)162         default void onCallSwitchFailed(Call call) {};
onConnectionEvent(Call call, String event, Bundle extras)163         default void onConnectionEvent(Call call, String event, Bundle extras) {};
onCallStreamingStateChanged(Call call, boolean isStreaming)164         default void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
onExternalCallChanged(Call call, boolean isExternalCall)165         default void onExternalCallChanged(Call call, boolean isExternalCall) {};
onRttInitiationFailure(Call call, int reason)166         default void onRttInitiationFailure(Call call, int reason) {};
onRemoteRttRequest(Call call, int requestId)167         default void onRemoteRttRequest(Call call, int requestId) {};
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)168         default void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
169                 Bundle extras, boolean isLegacy)  {};
onHandoverFailed(Call call, int error)170         default void onHandoverFailed(Call call, int error) {};
onHandoverComplete(Call call)171         default void onHandoverComplete(Call call)  {};
onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)172         default void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {};
onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)173         default void onReceivedDeviceToDeviceMessage(Call call, int messageType,
174                 int messageValue) {};
onReceivedCallQualityReport(Call call, CallQuality callQuality)175         default void onReceivedCallQualityReport(Call call, CallQuality callQuality) {};
onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)176         default void onCallerNumberVerificationStatusChanged(Call call,
177                 int callerNumberVerificationStatus) {};
178     }
179 
180     public abstract static class ListenerBase implements Listener {
181         @Override
onSuccessfulOutgoingCall(Call call, int callState)182         public void onSuccessfulOutgoingCall(Call call, int callState) {}
183         @Override
onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)184         public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}
185         @Override
onSuccessfulIncomingCall(Call call)186         public void onSuccessfulIncomingCall(Call call) {}
187         @Override
onFailedIncomingCall(Call call)188         public void onFailedIncomingCall(Call call) {}
189         @Override
onSuccessfulUnknownCall(Call call, int callState)190         public void onSuccessfulUnknownCall(Call call, int callState) {}
191         @Override
onFailedUnknownCall(Call call)192         public void onFailedUnknownCall(Call call) {}
193         @Override
onRingbackRequested(Call call, boolean ringbackRequested)194         public void onRingbackRequested(Call call, boolean ringbackRequested) {}
195         @Override
onPostDialWait(Call call, String remaining)196         public void onPostDialWait(Call call, String remaining) {}
197         @Override
onPostDialChar(Call call, char nextChar)198         public void onPostDialChar(Call call, char nextChar) {}
199         @Override
onConnectionCapabilitiesChanged(Call call)200         public void onConnectionCapabilitiesChanged(Call call) {}
201         @Override
onConnectionPropertiesChanged(Call call, boolean didRttChange)202         public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}
203         @Override
onParentChanged(Call call)204         public void onParentChanged(Call call) {}
205         @Override
onChildrenChanged(Call call)206         public void onChildrenChanged(Call call) {}
207         @Override
onCannedSmsResponsesLoaded(Call call)208         public void onCannedSmsResponsesLoaded(Call call) {}
209         @Override
onVideoCallProviderChanged(Call call)210         public void onVideoCallProviderChanged(Call call) {}
211         @Override
onCallerInfoChanged(Call call)212         public void onCallerInfoChanged(Call call) {}
213         @Override
onIsVoipAudioModeChanged(Call call)214         public void onIsVoipAudioModeChanged(Call call) {}
215         @Override
onStatusHintsChanged(Call call)216         public void onStatusHintsChanged(Call call) {}
217         @Override
onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)218         public void onExtrasChanged(Call c, int source, Bundle extras,
219                 String requestingPackageName) {}
220         @Override
onExtrasRemoved(Call c, int source, List<String> keys)221         public void onExtrasRemoved(Call c, int source, List<String> keys) {}
222         @Override
onHandleChanged(Call call)223         public void onHandleChanged(Call call) {}
224         @Override
onCallerDisplayNameChanged(Call call)225         public void onCallerDisplayNameChanged(Call call) {}
226         @Override
onCallDirectionChanged(Call call)227         public void onCallDirectionChanged(Call call) {}
228         @Override
onVideoStateChanged(Call call, int previousVideoState, int newVideoState)229         public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}
230         @Override
onTargetPhoneAccountChanged(Call call)231         public void onTargetPhoneAccountChanged(Call call) {}
232         @Override
onConnectionManagerPhoneAccountChanged(Call call)233         public void onConnectionManagerPhoneAccountChanged(Call call) {}
234         @Override
onPhoneAccountChanged(Call call)235         public void onPhoneAccountChanged(Call call) {}
236         @Override
onConferenceableCallsChanged(Call call)237         public void onConferenceableCallsChanged(Call call) {}
238         @Override
onConferenceStateChanged(Call call, boolean isConference)239         public void onConferenceStateChanged(Call call, boolean isConference) {}
240         @Override
onCdmaConferenceSwap(Call call)241         public void onCdmaConferenceSwap(Call call) {}
242         @Override
onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)243         public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) {
244             return false;
245         }
246         @Override
onHoldToneRequested(Call call)247         public void onHoldToneRequested(Call call) {}
248         @Override
onCallHoldFailed(Call call)249         public void onCallHoldFailed(Call call) {}
250         @Override
onCallSwitchFailed(Call call)251         public void onCallSwitchFailed(Call call) {}
252         @Override
onConnectionEvent(Call call, String event, Bundle extras)253         public void onConnectionEvent(Call call, String event, Bundle extras) {}
254         @Override
onCallStreamingStateChanged(Call call, boolean isStreaming)255         public void onCallStreamingStateChanged(Call call, boolean isStreaming) {}
256         @Override
onExternalCallChanged(Call call, boolean isExternalCall)257         public void onExternalCallChanged(Call call, boolean isExternalCall) {}
258         @Override
onRttInitiationFailure(Call call, int reason)259         public void onRttInitiationFailure(Call call, int reason) {}
260         @Override
onRemoteRttRequest(Call call, int requestId)261         public void onRemoteRttRequest(Call call, int requestId) {}
262         @Override
onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)263         public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState,
264                                         Bundle extras, boolean isLegacy) {}
265         @Override
onHandoverFailed(Call call, int error)266         public void onHandoverFailed(Call call, int error) {}
267         @Override
onHandoverComplete(Call call)268         public void onHandoverComplete(Call call) {}
269         @Override
onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)270         public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {}
271         @Override
onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)272         public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) {}
273         @Override
onReceivedCallQualityReport(Call call, CallQuality callQuality)274         public void onReceivedCallQualityReport(Call call, CallQuality callQuality) {}
275         @Override
onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)276         public void onCallerNumberVerificationStatusChanged(Call call,
277                 int callerNumberVerificationStatus) {}
278     }
279 
280     private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener =
281             new CallerInfoLookupHelper.OnQueryCompleteListener() {
282                 /** ${inheritDoc} */
283                 @Override
284                 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) {
285                     synchronized (mLock) {
286                         Call.this.setCallerInfo(handle, callerInfo);
287                     }
288                 }
289 
290                 @Override
291                 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) {
292                     synchronized (mLock) {
293                         Call.this.setCallerInfo(handle, callerInfo);
294                     }
295                 }
296             };
297 
298     /**
299      * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN
300      */
301     private int mCallDirection;
302 
303     /**
304      * The post-dial digits that were dialed after the network portion of the number
305      */
306     private String mPostDialDigits;
307 
308     /**
309      * The secondary line number that an incoming call has been received on if the SIM subscription
310      * has multiple associated numbers.
311      */
312     private String mViaNumber = "";
313 
314     /**
315      * The wall clock time this call was created. Beyond logging and such, may also be used for
316      * bookkeeping and specifically for marking certain call attempts as failed attempts.
317      * Note: This timestamp should NOT be used for calculating call duration.
318      */
319     private long mCreationTimeMillis;
320 
321     /**
322      * The elapsed realtime millis when this call was created; this can be used to determine how
323      * long has elapsed since the call was first created.
324      */
325     private long mCreationElapsedRealtimeMillis;
326 
327     /** The time this call was made active. */
328     private long mConnectTimeMillis = 0;
329 
330     /**
331      * The time, in millis, since boot when this call was connected.  This should ONLY be used when
332      * calculating the duration of the call.
333      *
334      * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the
335      * elapsed time since the device was booted.  Changes to the system clock (e.g. due to NITZ
336      * time sync, time zone changes user initiated clock changes) would cause a duration calculated
337      * based on {@link #mConnectTimeMillis} to change based on the delta in the time.
338      * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do
339      * not impact the call duration.
340      */
341     private long mConnectElapsedTimeMillis = 0;
342 
343     /** The wall clock time this call was disconnected. */
344     private long mDisconnectTimeMillis = 0;
345 
346     /**
347      * The elapsed time since boot when this call was disconnected.  Recorded as the
348      * {@link SystemClock#elapsedRealtime()}.  This ensures that the call duration is not impacted
349      * by changes in the wall time clock.
350      */
351     private long mDisconnectElapsedTimeMillis = 0;
352 
353     /** The gateway information associated with this call. This stores the original call handle
354      * that the user is attempting to connect to via the gateway, the actual handle to dial in
355      * order to connect the call via the gateway, as well as the package name of the gateway
356      * service. */
357     private GatewayInfo mGatewayInfo;
358 
359     private PhoneAccountHandle mConnectionManagerPhoneAccountHandle;
360 
361     private PhoneAccountHandle mTargetPhoneAccountHandle;
362 
363     private PhoneAccountHandle mRemotePhoneAccountHandle;
364 
365     private UserHandle mAssociatedUser;
366 
367     private final Handler mHandler = new Handler(Looper.getMainLooper());
368 
369     private final List<Call> mConferenceableCalls = new ArrayList<>();
370 
371     /** The state of the call. */
372     private int mState;
373 
374     /**
375      * Determines whether the {@link ConnectionService} has responded to the initial request to
376      * create the connection.
377      *
378      * {@code false} indicates the {@link Call} has been added to Telecom, but the
379      * {@link Connection} has not yet been returned by the associated {@link ConnectionService}.
380      * {@code true} indicates the {@link Call} has an associated {@link Connection} reported by the
381      * {@link ConnectionService}.
382      */
383     private boolean mIsCreateConnectionComplete = false;
384 
385     /** The handle with which to establish this call. */
386     private Uri mHandle;
387 
388     /** The participants with which to establish adhoc conference call */
389     private List<Uri> mParticipants;
390     /**
391      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
392      */
393     private int mHandlePresentation;
394 
395     /**
396      * The verification status for an incoming call's number.
397      */
398     private @Connection.VerificationStatus int mCallerNumberVerificationStatus;
399 
400     /** The caller display name (CNAP) set by the connection service. */
401     private String mCallerDisplayName;
402 
403     /**
404      * The presentation requirements for the handle. See {@link TelecomManager} for valid values.
405      */
406     private int mCallerDisplayNamePresentation;
407 
408     /**
409      * The connection service which is attempted or already connecting this call.
410      */
411     private ConnectionServiceWrapper mConnectionService;
412 
413     private TransactionalServiceWrapper mTransactionalService;
414 
415     private boolean mIsEmergencyCall;
416 
417     /**
418      * Flag indicating if ECBM is active for the target phone account. This only applies to MT calls
419      * in the scenario of work profiles (when the profile is paused and the user has only registered
420      * a work sim). Normally, MT calls made to the work sim should be rejected when the work apps
421      * are paused. However, when the admin makes a MO ecall, ECBM should be enabled for that sim to
422      * allow non-emergency MT calls. MO calls don't apply because the phone account would be
423      * rejected from selection if the owner is not placing the call.
424      */
425     private boolean mIsInECBM;
426 
427     // The Call is considered an emergency call for testing, but will not actually connect to
428     // emergency services.
429     private boolean mIsTestEmergencyCall;
430 
431     private boolean mSpeakerphoneOn;
432 
433     private boolean mIsDisconnectingChildCall = false;
434 
435     /**
436      * Tracks the video states which were applicable over the duration of a call.
437      * See {@link VideoProfile} for a list of valid video states.
438      * <p>
439      * Video state history is tracked when the call is active, and when a call is rejected or
440      * missed.
441      */
442     private int mVideoStateHistory;
443 
444     private int mVideoState;
445 
446     /**
447      * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED.
448      * See {@link android.telecom.DisconnectCause}.
449      */
450     private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
451 
452     /**
453      * Override the disconnect cause set by the connection service. Used for audio processing and
454      * simulated ringing calls as well as the condition when an emergency call is ended due to
455      * an emergency call being placed.
456      */
457     private DisconnectCause mOverrideDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN);
458 
459     private Bundle mIntentExtras = new Bundle();
460 
461     /**
462      * The {@link Intent} which originally created this call.  Only populated when we are putting a
463      * call into a pending state and need to pick up initiation of the call later.
464      */
465     private Intent mOriginalCallIntent = null;
466 
467     /** Set of listeners on this call.
468      *
469      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
470      * load factor before resizing, 1 means we only expect a single thread to
471      * access the map so make only a single shard
472      */
473     private final Set<Listener> mListeners = Collections.newSetFromMap(
474             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
475 
476     private CreateConnectionProcessor mCreateConnectionProcessor;
477 
478     /** Caller information retrieved from the latest contact query. */
479     private CallerInfo mCallerInfo;
480 
481     /** The latest token used with a contact info query. */
482     private int mQueryToken = 0;
483 
484     /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */
485     private boolean mRingbackRequested = false;
486 
487     /** Whether this call is requesting to be silently ringing. */
488     private boolean mSilentRingingRequested = false;
489 
490     /** Whether direct-to-voicemail query is pending. */
491     private boolean mDirectToVoicemailQueryPending;
492 
493     private int mConnectionCapabilities;
494 
495     private int mConnectionProperties;
496 
497     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
498 
499     private boolean mIsConference = false;
500 
501     private boolean mHadChildren = false;
502 
503     private final boolean mShouldAttachToExistingConnection;
504 
505     private Call mParentCall = null;
506 
507     private List<Call> mChildCalls = new LinkedList<>();
508 
509     /** Set of text message responses allowed for this call, if applicable. */
510     private List<String> mCannedSmsResponses = Collections.EMPTY_LIST;
511 
512     /** Whether an attempt has been made to load the text message responses. */
513     private boolean mCannedSmsResponsesLoadingStarted = false;
514 
515     private VideoProviderProxy mVideoProviderProxy;
516 
517     private boolean mIsVoipAudioMode;
518     private StatusHints mStatusHints;
519     private Bundle mExtras;
520     private final ConnectionServiceRepository mRepository;
521     private final Context mContext;
522     private final CallsManager mCallsManager;
523     private final ClockProxy mClockProxy;
524     private final ToastFactory mToastFactory;
525     private final TelecomSystem.SyncRoot mLock;
526     private final String mId;
527     private String mConnectionId;
528     private Analytics.CallInfo mAnalytics = new Analytics.CallInfo();
529     private CallStateChangedAtomWriter mCallStateChangedAtomWriter =
530             new CallStateChangedAtomWriter();
531     private char mPlayingDtmfTone;
532 
533     private boolean mWasConferencePreviouslyMerged = false;
534     private boolean mWasHighDefAudio = false;
535     private boolean mWasWifi = false;
536     private boolean mWasVolte = false;
537     private boolean mDestroyed = false;
538 
539     // For conferences which support merge/swap at their level, we retain a notion of an active
540     // call. This is used for BluetoothPhoneService.  In order to support hold/merge, it must have
541     // the notion of the current "active" call within the conference call. This maintains the
542     // "active" call and switches every time the user hits "swap".
543     private Call mConferenceLevelActiveCall = null;
544 
545     private boolean mIsLocallyDisconnecting = false;
546 
547     /**
548      * Tracks the current call data usage as reported by the video provider.
549      */
550     private long mCallDataUsage = DATA_USAGE_NOT_SET;
551 
552     private boolean mIsWorkCall;
553 
554     /**
555      * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has
556      * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set.
557      */
558     private boolean mUseCallRecordingTone;
559 
560     // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed.
561     private boolean mIsNewOutgoingCallIntentBroadcastDone = false;
562 
563     /**
564      * Indicates whether the call is remotely held.  A call is considered remotely held when
565      * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START}
566      * event.
567      */
568     private boolean mIsRemotelyHeld = false;
569 
570     /**
571      * Indicates whether the {@link PhoneAccount} associated with this call is self-managed.
572      * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED} for more information.
573      */
574     private boolean mIsSelfManaged = false;
575 
576     private boolean mIsTransactionalCall = false;
577     private CallingPackageIdentity mCallingPackageIdentity = new CallingPackageIdentity();
578 
579     /**
580      * CallingPackageIdentity is responsible for storing properties about the calling package that
581      * initiated the call. For example, if MyVoipApp requests to add a call with Telecom, we can
582      * store their UID and PID when we are still bound to that package.
583      */
584     public static class CallingPackageIdentity {
585         public int mCallingPackageUid = -1;
586         public int mCallingPackagePid = -1;
587 
CallingPackageIdentity()588         public CallingPackageIdentity() {
589         }
590 
CallingPackageIdentity(Bundle extras)591         CallingPackageIdentity(Bundle extras) {
592             mCallingPackageUid = extras.getInt(CallAttributes.CALLER_UID_KEY, -1);
593             mCallingPackagePid = extras.getInt(CallAttributes.CALLER_PID_KEY, -1);
594         }
595     }
596 
597     /**
598      * Indicates whether this call is streaming.
599      */
600     private boolean mIsStreaming = false;
601 
602     /**
603      * Indicates whether the {@link PhoneAccount} associated with an self-managed call want to
604      * expose the call to an {@link android.telecom.InCallService} which declares the metadata
605      * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS},
606      * For calls that {@link #mIsSelfManaged} is {@code false}, this value should be {@code false}
607      * as well.
608      */
609     private boolean mVisibleToInCallService = false;
610 
611     /**
612      * Indicates whether the {@link PhoneAccount} associated with this call supports video calling.
613      * {@code True} if the phone account supports video calling, {@code false} otherwise.
614      */
615     private boolean mIsVideoCallingSupportedByPhoneAccount = false;
616 
617     /**
618      * Indicates whether or not this call can be pulled if it is an external call. If true, respect
619      * the Connection Capability set by the ConnectionService. If false, override the capability
620      * set and always remove the ability to pull this external call.
621      *
622      * See {@link #setIsPullExternalCallSupported(boolean)}
623      */
624     private boolean mIsPullExternalCallSupported = true;
625 
626     private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
627 
628     /**
629      * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager
630      * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
631      * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)},
632      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
633      * originally created it.
634      *
635      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information.
636      */
637     private String mOriginalConnectionId;
638 
639     /**
640      * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication
641      * between the in-call app and the connection service. If both non-null, this call should be
642      * treated as an RTT call.
643      * Each array should be of size 2. First one is the read side and the second one is the write
644      * side.
645      */
646     private ParcelFileDescriptor[] mInCallToConnectionServiceStreams;
647     private ParcelFileDescriptor[] mConnectionServiceToInCallStreams;
648 
649     /**
650      * True if we're supposed to start this call with RTT, either due to the settings switch or due
651      * to an extra.
652      */
653     private boolean mDidRequestToStartWithRtt = false;
654     /**
655      * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode.
656      */
657     private int mRttMode;
658     /**
659      * True if the call was ever an RTT call.
660      */
661     private boolean mWasEverRtt = false;
662 
663     /**
664      * Integer indicating the remote RTT request ID that is pending a response from the user.
665      */
666     private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID;
667 
668     /**
669      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
670      * int, Bundle, boolean)}, contains the call which this call is being handed over to.
671      */
672     private Call mHandoverDestinationCall = null;
673 
674     /**
675      * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle,
676      * int, Bundle, boolean)}, contains the call which this call is being handed over from.
677      */
678     private Call mHandoverSourceCall = null;
679 
680     /**
681      * The user-visible app name of the app that requested for this call to be put into the
682      * AUDIO_PROCESSING state. Used to display a notification to the user.
683      */
684     private CharSequence mAudioProcessingRequestingApp = null;
685 
686     /**
687      * Indicates the current state of this call if it is in the process of a handover.
688      */
689     private int mHandoverState = HandoverState.HANDOVER_NONE;
690 
691     /**
692      * Indicates whether this call is using one of the
693      * {@link com.android.server.telecom.callfiltering.CallFilter} modules.
694      */
695     private boolean mIsUsingCallFiltering = false;
696 
697     /**
698      * Indicates whether or not this call has been active before. This is helpful in detecting
699      * situations where we have moved into {@link CallState#SIMULATED_RINGING} or
700      * {@link CallState#AUDIO_PROCESSING} again after being active. If a call has moved into one
701      * of these states again after being active and the user dials an emergency call, we want to
702      * log these calls normally instead of considering them MISSED. If the emergency call was
703      * dialed during initial screening however, we want to treat those calls as MISSED (because the
704      * user never got the chance to explicitly reject).
705      */
706     private boolean mHasGoneActiveBefore = false;
707 
708     /**
709      * Indicates the package name of the {@link android.telecom.CallScreeningService} which should
710      * be sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} intent upon disconnection
711      * of a call.
712      */
713     private String mPostCallPackageName;
714 
715     /**
716      * Call missed information code.
717      */
718     @CallLog.Calls.MissedReason private long mMissedReason;
719 
720     /**
721      * Time that this call start ringing or simulated ringing.
722      */
723     private long mStartRingTime;
724 
725     /**
726      * The package name of the call screening service that silence this call. If the call is not
727      * silenced, this field will be null.
728      */
729     private CharSequence mCallScreeningAppName;
730 
731     /**
732      * The component name of the call screening service that silence this call. If the call is not
733      * silenced, this field will be null.
734      */
735     private String mCallScreeningComponentName;
736 
737     /**
738      * When {@code true} indicates this call originated from a SIM-based {@link PhoneAccount}.
739      * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
740      * set.
741      */
742     private boolean mIsSimCall;
743 
744     /**
745      * Set to {@code true} if we received a valid response ({@code null} or otherwise) from
746      * the {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or
747      * {@link CallDiagnostics#onCallDisconnected(int, int)} calls.  This is used to detect a timeout
748      * when awaiting a response from the call diagnostic service.
749      */
750     private boolean mReceivedCallDiagnosticPostCallResponse = false;
751 
752     /**
753      * {@link CompletableFuture} used to delay posting disconnection and removal to a call until
754      * after a {@link CallDiagnosticService} is able to handle the disconnection and provide a
755      * disconnect message via {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or
756      * {@link CallDiagnostics#onCallDisconnected(int, int)}.
757      */
758     private CompletableFuture<Boolean> mDisconnectFuture;
759 
760     /**
761      * Persists the specified parameters and initializes the new instance.
762      * @param context The context.
763      * @param repository The connection service repository.
764      * @param handle The handle to dial.
765      * @param gatewayInfo Gateway information to use for the call.
766      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
767      *         This account must be one that was registered with the
768      *           {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
769      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
770      *         one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
771      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
772      *         or CALL_DIRECTION_UNKNOWN.
773      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
774      * @param clockProxy
775      */
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, ToastFactory toastFactory)776     public Call(
777             String callId,
778             Context context,
779             CallsManager callsManager,
780             TelecomSystem.SyncRoot lock,
781             ConnectionServiceRepository repository,
782             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
783             Uri handle,
784             GatewayInfo gatewayInfo,
785             PhoneAccountHandle connectionManagerPhoneAccountHandle,
786             PhoneAccountHandle targetPhoneAccountHandle,
787             int callDirection,
788             boolean shouldAttachToExistingConnection,
789             boolean isConference,
790             ClockProxy clockProxy,
791             ToastFactory toastFactory) {
792         this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter,
793                handle, null, gatewayInfo, connectionManagerPhoneAccountHandle,
794                targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection,
795                isConference, clockProxy, toastFactory);
796 
797     }
798 
Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, List<Uri> participants, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy, ToastFactory toastFactory)799     public Call(
800             String callId,
801             Context context,
802             CallsManager callsManager,
803             TelecomSystem.SyncRoot lock,
804             ConnectionServiceRepository repository,
805             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
806             Uri handle,
807             List<Uri> participants,
808             GatewayInfo gatewayInfo,
809             PhoneAccountHandle connectionManagerPhoneAccountHandle,
810             PhoneAccountHandle targetPhoneAccountHandle,
811             int callDirection,
812             boolean shouldAttachToExistingConnection,
813             boolean isConference,
814             ClockProxy clockProxy,
815             ToastFactory toastFactory) {
816 
817         mId = callId;
818         mConnectionId = callId;
819         mState = (isConference && callDirection != CALL_DIRECTION_INCOMING &&
820                 callDirection != CALL_DIRECTION_OUTGOING) ?
821                 CallState.ACTIVE : CallState.NEW;
822         mContext = context;
823         mCallsManager = callsManager;
824         mLock = lock;
825         mRepository = repository;
826         mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
827         setHandle(handle);
828         mParticipants = participants;
829         mPostDialDigits = handle != null
830                 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : "";
831         mGatewayInfo = gatewayInfo;
832         setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle);
833         mCallDirection = callDirection;
834         setTargetPhoneAccount(targetPhoneAccountHandle);
835         mIsConference = isConference;
836         mShouldAttachToExistingConnection = shouldAttachToExistingConnection
837                 || callDirection == CALL_DIRECTION_INCOMING;
838         maybeLoadCannedSmsResponses();
839         mClockProxy = clockProxy;
840         mToastFactory = toastFactory;
841         mCreationTimeMillis = mClockProxy.currentTimeMillis();
842         mCreationElapsedRealtimeMillis = mClockProxy.elapsedRealtime();
843         mMissedReason = MISSED_REASON_NOT_MISSED;
844         mStartRingTime = 0;
845 
846         mCallStateChangedAtomWriter.setExistingCallCount(callsManager.getCalls().size());
847     }
848 
849     /**
850      * Persists the specified parameters and initializes the new instance.
851      * @param context The context.
852      * @param repository The connection service repository.
853      * @param handle The handle to dial.
854      * @param gatewayInfo Gateway information to use for the call.
855      * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call.
856      * This account must be one that was registered with the
857      * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag.
858      * @param targetPhoneAccountHandle Account information to use for the call. This account must be
859      * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag.
860      * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING,
861      * or CALL_DIRECTION_UNKNOWN
862      * @param shouldAttachToExistingConnection Set to true to attach the call to an existing
863      * connection, regardless of whether it's incoming or outgoing.
864      * @param connectTimeMillis The connection time of the call.
865      * @param clockProxy
866      */
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, ToastFactory toastFactory)867     Call(
868             String callId,
869             Context context,
870             CallsManager callsManager,
871             TelecomSystem.SyncRoot lock,
872             ConnectionServiceRepository repository,
873             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
874             Uri handle,
875             GatewayInfo gatewayInfo,
876             PhoneAccountHandle connectionManagerPhoneAccountHandle,
877             PhoneAccountHandle targetPhoneAccountHandle,
878             int callDirection,
879             boolean shouldAttachToExistingConnection,
880             boolean isConference,
881             long connectTimeMillis,
882             long connectElapsedTimeMillis,
883             ClockProxy clockProxy,
884             ToastFactory toastFactory) {
885         this(callId, context, callsManager, lock, repository,
886                 phoneNumberUtilsAdapter, handle, gatewayInfo,
887                 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection,
888                 shouldAttachToExistingConnection, isConference, clockProxy, toastFactory);
889 
890         mConnectTimeMillis = connectTimeMillis;
891         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
892         mAnalytics.setCallStartTime(connectTimeMillis);
893     }
894 
addListener(Listener listener)895     public void addListener(Listener listener) {
896         mListeners.add(listener);
897     }
898 
removeListener(Listener listener)899     public void removeListener(Listener listener) {
900         if (listener != null) {
901             mListeners.remove(listener);
902         }
903     }
904 
initAnalytics()905     public void initAnalytics() {
906         initAnalytics(null, null);
907     }
908 
initAnalytics(String callingPackage, String extraCreationLogs)909     public void initAnalytics(String callingPackage, String extraCreationLogs) {
910         int analyticsDirection;
911         switch (mCallDirection) {
912             case CALL_DIRECTION_OUTGOING:
913                 analyticsDirection = Analytics.OUTGOING_DIRECTION;
914                 break;
915             case CALL_DIRECTION_INCOMING:
916                 analyticsDirection = Analytics.INCOMING_DIRECTION;
917                 break;
918             case CALL_DIRECTION_UNKNOWN:
919             case CALL_DIRECTION_UNDEFINED:
920             default:
921                 analyticsDirection = Analytics.UNKNOWN_DIRECTION;
922         }
923         mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection);
924         mAnalytics.setCallIsEmergency(mIsEmergencyCall);
925         Log.addEvent(this, LogUtils.Events.CREATED, callingPackage + ";" + extraCreationLogs);
926     }
927 
getAnalytics()928     public Analytics.CallInfo getAnalytics() {
929         return mAnalytics;
930     }
931 
destroy()932     public void destroy() {
933         if (mDestroyed) {
934             return;
935         }
936         // We should not keep these bitmaps around because the Call objects may be held for logging
937         // purposes.
938         // TODO: Make a container object that only stores the information we care about for Logging.
939         if (mCallerInfo != null) {
940             mCallerInfo.cachedPhotoIcon = null;
941             mCallerInfo.cachedPhoto = null;
942         }
943         closeRttStreams();
944 
945         Log.addEvent(this, LogUtils.Events.DESTROYED);
946         mDestroyed = true;
947     }
948 
closeRttStreams()949     private void closeRttStreams() {
950         if (mConnectionServiceToInCallStreams != null) {
951             for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) {
952                 if (fd != null) {
953                     try {
954                         fd.close();
955                     } catch (IOException e) {
956                         // ignore
957                     }
958                 }
959             }
960         }
961         if (mInCallToConnectionServiceStreams != null) {
962             for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) {
963                 if (fd != null) {
964                     try {
965                         fd.close();
966                     } catch (IOException e) {
967                         // ignore
968                     }
969                 }
970             }
971         }
972     }
973 
974     /** {@inheritDoc} */
975     @Override
toString()976     public String toString() {
977         return String.format(Locale.US, "[Call id=%s, state=%s, tpac=%s, cmgr=%s, handle=%s, "
978                         + "vidst=%s, childs(%d), has_parent(%b), cap=%s, prop=%s], voip=%b",
979                 mId,
980                 CallState.toString(getParcelableCallState()),
981                 getTargetPhoneAccount(),
982                 getConnectionManagerPhoneAccount(),
983                 Log.piiHandle(mHandle),
984                 getVideoStateDescription(getVideoState()),
985                 getChildCalls().size(),
986                 getParentCall() != null,
987                 Connection.capabilitiesToStringShort(getConnectionCapabilities()),
988                 Connection.propertiesToStringShort(getConnectionProperties()),
989                 mIsVoipAudioMode);
990     }
991 
992     @Override
getDescription()993     public String getDescription() {
994         StringBuilder s = new StringBuilder();
995         if (isSelfManaged()) {
996             s.append("SelfMgd Call");
997         } else if (isExternalCall()) {
998             s.append("External Call");
999         } else {
1000             s.append("Call");
1001         }
1002         s.append(getId());
1003         s.append(" [");
1004         s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
1005         s.append("]");
1006         s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
1007         s.append("(User=");
1008         s.append(getAssociatedUser());
1009         s.append(")");
1010         s.append("\n\t");
1011 
1012         PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
1013         PhoneAccountHandle remotePhoneAccountHandle = getRemotePhoneAccountHandle();
1014         PhoneAccountHandle connectionMgrAccountHandle = getConnectionManagerPhoneAccount();
1015         PhoneAccountHandle delegatePhoneAccountHandle = getDelegatePhoneAccountHandle();
1016         boolean isTargetSameAsRemote = targetPhoneAccountHandle != null
1017                 && targetPhoneAccountHandle.equals(remotePhoneAccountHandle);
1018         if (Objects.equals(delegatePhoneAccountHandle, targetPhoneAccountHandle)) {
1019             s.append(">>>");
1020         }
1021         s.append("Target");
1022         s.append(" PhoneAccount: ");
1023         if (targetPhoneAccountHandle != null) {
1024             s.append(targetPhoneAccountHandle);
1025             s.append(" (");
1026             s.append(getTargetPhoneAccountLabel());
1027             s.append(")");
1028             if (isTargetSameAsRemote) {
1029                 s.append("(remote)");
1030             }
1031         } else {
1032             s.append("not set");
1033         }
1034         if (!isTargetSameAsRemote && remotePhoneAccountHandle != null) {
1035             // This is a RARE case and will likely not be seen in practice but it is possible.
1036             if (delegatePhoneAccountHandle.equals(remotePhoneAccountHandle)) {
1037                 s.append("\n\t>>>Remote PhoneAccount: ");
1038             } else {
1039                 s.append("\n\tRemote PhoneAccount: ");
1040             }
1041             s.append(remotePhoneAccountHandle);
1042         }
1043         if (connectionMgrAccountHandle != null) {
1044             if (delegatePhoneAccountHandle.equals(connectionMgrAccountHandle)) {
1045                 s.append("\n\t>>>Conn mgr: ");
1046             } else {
1047                 s.append("\n\tConn mgr: ");
1048             }
1049             s.append(connectionMgrAccountHandle);
1050         }
1051 
1052         s.append("\n\tTo address: ");
1053         s.append(Log.piiHandle(getHandle()));
1054         if (isIncoming()) {
1055             switch (mCallerNumberVerificationStatus) {
1056                 case Connection.VERIFICATION_STATUS_FAILED:
1057                     s.append(" Verstat: fail");
1058                     break;
1059                 case Connection.VERIFICATION_STATUS_NOT_VERIFIED:
1060                     s.append(" Verstat: not");
1061                     break;
1062                 case Connection.VERIFICATION_STATUS_PASSED:
1063                     s.append(" Verstat: pass");
1064                     break;
1065             }
1066         }
1067         s.append(" Presentation: ");
1068         switch (getHandlePresentation()) {
1069             case TelecomManager.PRESENTATION_ALLOWED:
1070                 s.append("Allowed");
1071                 break;
1072             case TelecomManager.PRESENTATION_PAYPHONE:
1073                 s.append("Payphone");
1074                 break;
1075             case TelecomManager.PRESENTATION_RESTRICTED:
1076                 s.append("Restricted");
1077                 break;
1078             case TelecomManager.PRESENTATION_UNKNOWN:
1079                 s.append("Unknown");
1080                 break;
1081             case TelecomManager.PRESENTATION_UNAVAILABLE:
1082                 s.append("Unavailable");
1083                 break;
1084             default:
1085                 s.append("<undefined>");
1086         }
1087         s.append("\n");
1088         return s.toString();
1089     }
1090 
1091     /**
1092      * Builds a debug-friendly description string for a video state.
1093      * <p>
1094      * A = audio active, T = video transmission active, R = video reception active, P = video
1095      * paused.
1096      *
1097      * @param videoState The video state.
1098      * @return A string indicating which bits are set in the video state.
1099      */
getVideoStateDescription(int videoState)1100     private String getVideoStateDescription(int videoState) {
1101         StringBuilder sb = new StringBuilder();
1102         sb.append("A");
1103 
1104         if (VideoProfile.isTransmissionEnabled(videoState)) {
1105             sb.append("T");
1106         }
1107 
1108         if (VideoProfile.isReceptionEnabled(videoState)) {
1109             sb.append("R");
1110         }
1111 
1112         if (VideoProfile.isPaused(videoState)) {
1113             sb.append("P");
1114         }
1115 
1116         return sb.toString();
1117     }
1118 
1119     @Override
getConnectionServiceWrapper()1120     public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() {
1121         return (!mIsTransactionalCall ? mConnectionService : mTransactionalService);
1122     }
1123 
getState()1124     public int getState() {
1125         return mState;
1126     }
1127 
1128     /**
1129      * Similar to {@link #getState()}, except will return {@link CallState#DISCONNECTING} if the
1130      * call is locally disconnecting.  This is the call state which is reported to the
1131      * {@link android.telecom.InCallService}s when a call is parcelled.
1132      * @return The parcelable call state.
1133      */
getParcelableCallState()1134     public int getParcelableCallState() {
1135         if (isLocallyDisconnecting() &&
1136                 (mState != android.telecom.Call.STATE_DISCONNECTED)) {
1137             return CallState.DISCONNECTING;
1138         }
1139         return mState;
1140     }
1141 
1142     /**
1143      * Determines if this {@link Call} can receive call focus via the
1144      * {@link ConnectionServiceFocusManager}.
1145      * Only top-level calls and non-external calls are eligible.
1146      * @return {@code true} if this call is focusable, {@code false} otherwise.
1147      */
1148     @Override
isFocusable()1149     public boolean isFocusable() {
1150         boolean isChild = getParentCall() != null;
1151         return !isChild && !isExternalCall();
1152     }
1153 
shouldContinueProcessingAfterDisconnect()1154     private boolean shouldContinueProcessingAfterDisconnect() {
1155         // Stop processing once the call is active.
1156         if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
1157             return false;
1158         }
1159 
1160         // Only Redial a Call in the case of it being an Emergency Call.
1161         if(!isEmergencyCall()) {
1162             return false;
1163         }
1164 
1165         // Make sure that there are additional connection services to process.
1166         if (mCreateConnectionProcessor == null
1167             || !mCreateConnectionProcessor.isProcessingComplete()
1168             || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
1169             return false;
1170         }
1171 
1172         if (mDisconnectCause == null) {
1173             return false;
1174         }
1175 
1176         // Continue processing if the current attempt failed or timed out.
1177         return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
1178             mCreateConnectionProcessor.isCallTimedOut();
1179     }
1180 
1181     /**
1182      * Returns the unique ID for this call as it exists in Telecom.
1183      * @return The call ID.
1184      */
getId()1185     public String getId() {
1186         return mId;
1187     }
1188 
1189     /**
1190      * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
1191      * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
1192      * @return The call ID with an appended attempt id.
1193      */
getConnectionId()1194     public String getConnectionId() {
1195         if(mCreateConnectionProcessor != null) {
1196             mConnectionId = mId + "_" +
1197                     String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
1198             return mConnectionId;
1199         } else {
1200             return mConnectionId;
1201         }
1202     }
1203 
1204     /**
1205      * Handles an incoming overridden disconnect message for this call.
1206      *
1207      * We only care if the disconnect is handled via a future.
1208      * @param message the overridden disconnect message.
1209      */
handleOverrideDisconnectMessage(@ullable CharSequence message)1210     public void handleOverrideDisconnectMessage(@Nullable CharSequence message) {
1211         Log.i(this, "handleOverrideDisconnectMessage; callid=%s, msg=%s", getId(), message);
1212 
1213         if (isDisconnectHandledViaFuture()) {
1214             mReceivedCallDiagnosticPostCallResponse = true;
1215             if (message != null) {
1216                 Log.addEvent(this, LogUtils.Events.OVERRIDE_DISCONNECT_MESSAGE, message);
1217                 // Replace the existing disconnect cause in this call
1218                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.ERROR, message,
1219                         message, null));
1220             }
1221 
1222             mDisconnectFuture.complete(true);
1223         } else {
1224             Log.w(this, "handleOverrideDisconnectMessage; callid=%s - got override when unbound",
1225                     getId());
1226         }
1227     }
1228 
1229     /**
1230      * Sets the call state. Although there exists the notion of appropriate state transitions
1231      * (see {@link CallState}), in practice those expectations break down when cellular systems
1232      * misbehave and they do this very often. The result is that we do not enforce state transitions
1233      * and instead keep the code resilient to unexpected state changes.
1234      * @return true indicates if setState succeeded in setting the state to newState,
1235      * else it is failed, and the call is still in its original state.
1236      */
setState(int newState, String tag)1237     public boolean setState(int newState, String tag) {
1238         if (mState != newState) {
1239             Log.v(this, "setState %s -> %s", CallState.toString(mState),
1240                     CallState.toString(newState));
1241 
1242             if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
1243                 Log.w(this, "continuing processing disconnected call with another service");
1244                 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
1245                 return false;
1246             } else if (newState == CallState.ANSWERED && mState == CallState.ACTIVE) {
1247                 Log.w(this, "setState %s -> %s; call already active.", CallState.toString(mState),
1248                         CallState.toString(newState));
1249                 return false;
1250             }
1251 
1252             updateVideoHistoryViaState(mState, newState);
1253 
1254             mState = newState;
1255             maybeLoadCannedSmsResponses();
1256 
1257             if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
1258                 if (mConnectTimeMillis == 0) {
1259                     // We check to see if mConnectTime is already set to prevent the
1260                     // call from resetting active time when it goes in and out of
1261                     // ACTIVE/ON_HOLD
1262                     mConnectTimeMillis = mClockProxy.currentTimeMillis();
1263                     mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
1264                     mAnalytics.setCallStartTime(mConnectTimeMillis);
1265                 }
1266 
1267                 // We're clearly not disconnected, so reset the disconnected time.
1268                 mDisconnectTimeMillis = 0;
1269                 mDisconnectElapsedTimeMillis = 0;
1270                 mHasGoneActiveBefore = true;
1271             } else if (mState == CallState.DISCONNECTED) {
1272                 mDisconnectTimeMillis = mClockProxy.currentTimeMillis();
1273                 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime();
1274                 mAnalytics.setCallEndTime(mDisconnectTimeMillis);
1275                 setLocallyDisconnecting(false);
1276                 fixParentAfterDisconnect();
1277             }
1278 
1279             // Log the state transition event
1280             String event = null;
1281             Object data = null;
1282             switch (newState) {
1283                 case CallState.ACTIVE:
1284                     event = LogUtils.Events.SET_ACTIVE;
1285                     break;
1286                 case CallState.CONNECTING:
1287                     event = LogUtils.Events.SET_CONNECTING;
1288                     break;
1289                 case CallState.DIALING:
1290                     event = LogUtils.Events.SET_DIALING;
1291                     break;
1292                 case CallState.PULLING:
1293                     event = LogUtils.Events.SET_PULLING;
1294                     break;
1295                 case CallState.DISCONNECTED:
1296                     event = LogUtils.Events.SET_DISCONNECTED;
1297                     data = getDisconnectCause();
1298                     break;
1299                 case CallState.DISCONNECTING:
1300                     event = LogUtils.Events.SET_DISCONNECTING;
1301                     break;
1302                 case CallState.ON_HOLD:
1303                     event = LogUtils.Events.SET_HOLD;
1304                     break;
1305                 case CallState.SELECT_PHONE_ACCOUNT:
1306                     event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
1307                     break;
1308                 case CallState.RINGING:
1309                     event = LogUtils.Events.SET_RINGING;
1310                     break;
1311                 case CallState.ANSWERED:
1312                     event = LogUtils.Events.SET_ANSWERED;
1313                     break;
1314                 case CallState.AUDIO_PROCESSING:
1315                     event = LogUtils.Events.SET_AUDIO_PROCESSING;
1316                     break;
1317                 case CallState.SIMULATED_RINGING:
1318                     event = LogUtils.Events.SET_SIMULATED_RINGING;
1319                     break;
1320             }
1321             if (event != null) {
1322                 // The string data should be just the tag.
1323                 String stringData = tag;
1324                 if (data != null) {
1325                     // If data exists, add it to tag.  If no tag, just use data.toString().
1326                     stringData = stringData == null ? data.toString() : stringData + "> " + data;
1327                 }
1328                 Log.addEvent(this, event, stringData);
1329             }
1330 
1331             mCallStateChangedAtomWriter
1332                     .setDisconnectCause(getDisconnectCause())
1333                     .setSelfManaged(isSelfManaged())
1334                     .setExternalCall(isExternalCall())
1335                     .setEmergencyCall(isEmergencyCall())
1336                     .setDurationSeconds(Long.valueOf(
1337                         (mDisconnectTimeMillis - mConnectTimeMillis) / 1000).intValue())
1338                     .write(newState);
1339         }
1340         return true;
1341     }
1342 
setRingbackRequested(boolean ringbackRequested)1343     void setRingbackRequested(boolean ringbackRequested) {
1344         mRingbackRequested = ringbackRequested;
1345         for (Listener l : mListeners) {
1346             l.onRingbackRequested(this, mRingbackRequested);
1347         }
1348     }
1349 
isRingbackRequested()1350     public boolean isRingbackRequested() {
1351         return mRingbackRequested;
1352     }
1353 
setSilentRingingRequested(boolean silentRingingRequested)1354     public void setSilentRingingRequested(boolean silentRingingRequested) {
1355         mSilentRingingRequested = silentRingingRequested;
1356         Bundle bundle = new Bundle();
1357         bundle.putBoolean(android.telecom.Call.EXTRA_SILENT_RINGING_REQUESTED,
1358                 silentRingingRequested);
1359         putConnectionServiceExtras(bundle);
1360     }
1361 
isSilentRingingRequested()1362     public boolean isSilentRingingRequested() {
1363         return mSilentRingingRequested;
1364     }
1365 
setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed)1366     public void setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed) {
1367         Bundle bundle = new Bundle();
1368         bundle.putBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB,
1369                 isCallSuppressed);
1370         putConnectionServiceExtras(bundle);
1371     }
1372 
isCallSuppressedByDoNotDisturb()1373     public boolean isCallSuppressedByDoNotDisturb() {
1374         if (getExtras() == null) {
1375             return false;
1376         }
1377         return getExtras().getBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB);
1378     }
1379 
wasDndCheckComputedForCall()1380     public boolean wasDndCheckComputedForCall() {
1381         if (getExtras() == null) {
1382             return false;
1383         }
1384         return getExtras().containsKey(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB);
1385     }
1386 
1387     @VisibleForTesting
isConference()1388     public boolean isConference() {
1389         return mIsConference;
1390     }
1391 
1392     /**
1393      * @return {@code true} if this call had children at some point, {@code false} otherwise.
1394      */
hadChildren()1395     public boolean hadChildren() {
1396         return mHadChildren;
1397     }
1398 
getHandle()1399     public Uri getHandle() {
1400         return mHandle;
1401     }
1402 
getParticipants()1403     public List<Uri> getParticipants() {
1404         return mParticipants;
1405     }
1406 
isAdhocConferenceCall()1407     public boolean isAdhocConferenceCall() {
1408         return mIsConference &&
1409                 (mCallDirection == CALL_DIRECTION_OUTGOING ||
1410                 mCallDirection == CALL_DIRECTION_INCOMING);
1411     }
1412 
getPostDialDigits()1413     public String getPostDialDigits() {
1414         return mPostDialDigits;
1415     }
1416 
clearPostDialDigits()1417     public void clearPostDialDigits() {
1418         mPostDialDigits = null;
1419     }
1420 
getViaNumber()1421     public String getViaNumber() {
1422         return mViaNumber;
1423     }
1424 
setViaNumber(String viaNumber)1425     public void setViaNumber(String viaNumber) {
1426         // If at any point the via number is not empty throughout the call, save that via number.
1427         if (!TextUtils.isEmpty(viaNumber)) {
1428             mViaNumber = viaNumber;
1429         }
1430     }
1431 
getHandlePresentation()1432     public int getHandlePresentation() {
1433         return mHandlePresentation;
1434     }
1435 
setCallerNumberVerificationStatus( @onnection.VerificationStatus int callerNumberVerificationStatus)1436     public void setCallerNumberVerificationStatus(
1437             @Connection.VerificationStatus int callerNumberVerificationStatus) {
1438         mCallerNumberVerificationStatus = callerNumberVerificationStatus;
1439         mListeners.forEach(l -> l.onCallerNumberVerificationStatusChanged(this,
1440                 callerNumberVerificationStatus));
1441     }
1442 
getCallerNumberVerificationStatus()1443     public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
1444         return mCallerNumberVerificationStatus;
1445     }
1446 
setHandle(Uri handle)1447     void setHandle(Uri handle) {
1448         setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
1449     }
1450 
setHandle(Uri handle, int presentation)1451     public void setHandle(Uri handle, int presentation) {
1452         if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
1453             mHandlePresentation = presentation;
1454             if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
1455                     mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
1456                 mHandle = null;
1457             } else {
1458                 mHandle = handle;
1459                 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
1460                         && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
1461                     // If the number is actually empty, set it to null, unless this is a
1462                     // SCHEME_VOICEMAIL uri which always has an empty number.
1463                     mHandle = null;
1464                 }
1465             }
1466 
1467             // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
1468             // call, it will remain so for the rest of it's lifetime.
1469             if (!mIsEmergencyCall) {
1470                 try {
1471                     mIsEmergencyCall = mHandle != null &&
1472                             getTelephonyManager().isEmergencyNumber(
1473                                     mHandle.getSchemeSpecificPart());
1474                 } catch (IllegalStateException ise) {
1475                     Log.e(this, ise, "setHandle: can't determine if number is emergency");
1476                     mIsEmergencyCall = false;
1477                 } catch (RuntimeException r) {
1478                     Log.e(this, r, "setHandle: can't determine if number is emergency");
1479                     mIsEmergencyCall = false;
1480                 }
1481                 mAnalytics.setCallIsEmergency(mIsEmergencyCall);
1482             }
1483             if (!mIsTestEmergencyCall) {
1484                 mIsTestEmergencyCall = mHandle != null &&
1485                         isTestEmergencyCall(mHandle.getSchemeSpecificPart());
1486             }
1487             startCallerInfoLookup();
1488             for (Listener l : mListeners) {
1489                 l.onHandleChanged(this);
1490             }
1491         }
1492     }
1493 
isTestEmergencyCall(String number)1494     private boolean isTestEmergencyCall(String number) {
1495         try {
1496             Map<Integer, List<EmergencyNumber>> eMap =
1497                     getTelephonyManager().getEmergencyNumberList();
1498             return eMap.values().stream().flatMap(Collection::stream)
1499                     .anyMatch(eNumber ->
1500                             eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) &&
1501                                     number.equals(eNumber.getNumber()));
1502         } catch (IllegalStateException ise) {
1503             return false;
1504         } catch (RuntimeException r) {
1505             return false;
1506         }
1507     }
1508 
getContactPhotoUri()1509     public Uri getContactPhotoUri() {
1510         return mCallerInfo != null ? mCallerInfo.getContactDisplayPhotoUri() : null;
1511     }
1512 
getCallerDisplayName()1513     public String getCallerDisplayName() {
1514         return mCallerDisplayName;
1515     }
1516 
getCallerDisplayNamePresentation()1517     public int getCallerDisplayNamePresentation() {
1518         return mCallerDisplayNamePresentation;
1519     }
1520 
setCallerDisplayName(String callerDisplayName, int presentation)1521     void setCallerDisplayName(String callerDisplayName, int presentation) {
1522         if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
1523                 presentation != mCallerDisplayNamePresentation) {
1524             mCallerDisplayName = callerDisplayName;
1525             mCallerDisplayNamePresentation = presentation;
1526             for (Listener l : mListeners) {
1527                 l.onCallerDisplayNameChanged(this);
1528             }
1529         }
1530     }
1531 
setContactPhotoUri(Uri contactPhotoUri)1532     void setContactPhotoUri(Uri contactPhotoUri) {
1533         if (mCallerInfo != null) {
1534             mCallerInfo.SetContactDisplayPhotoUri(contactPhotoUri);
1535         }
1536     }
1537 
getName()1538     public String getName() {
1539         return mCallerInfo == null ? null : mCallerInfo.getName();
1540     }
1541 
getPhoneNumber()1542     public String getPhoneNumber() {
1543         return mCallerInfo == null ? null : mCallerInfo.getPhoneNumber();
1544     }
1545 
getPhotoIcon()1546     public Bitmap getPhotoIcon() {
1547         return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
1548     }
1549 
getPhoto()1550     public Drawable getPhoto() {
1551         return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
1552     }
1553 
1554     /**
1555      * @param cause The reason for the disconnection, represented by
1556      * {@link android.telecom.DisconnectCause}.
1557      */
setDisconnectCause(DisconnectCause cause)1558     public void setDisconnectCause(DisconnectCause cause) {
1559         // TODO: Consider combining this method with a setDisconnected() method that is totally
1560         // separate from setState.
1561 
1562         if (mOverrideDisconnectCause.getCode() != DisconnectCause.UNKNOWN) {
1563             cause = new DisconnectCause(mOverrideDisconnectCause.getCode(),
1564                     TextUtils.isEmpty(mOverrideDisconnectCause.getLabel()) ?
1565                             cause.getLabel() : mOverrideDisconnectCause.getLabel(),
1566                     (mOverrideDisconnectCause.getDescription() == null) ?
1567                             cause.getDescription() :mOverrideDisconnectCause.getDescription(),
1568                     TextUtils.isEmpty(mOverrideDisconnectCause.getReason()) ?
1569                             cause.getReason() : mOverrideDisconnectCause.getReason(),
1570                     (mOverrideDisconnectCause.getTone() == 0) ?
1571                             cause.getTone() : mOverrideDisconnectCause.getTone());
1572         }
1573         mAnalytics.setCallDisconnectCause(cause);
1574         mDisconnectCause = cause;
1575     }
1576 
setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause)1577     public void setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause) {
1578         mOverrideDisconnectCause = overrideDisconnectCause;
1579     }
1580 
1581 
getDisconnectCause()1582     public DisconnectCause getDisconnectCause() {
1583         return mDisconnectCause;
1584     }
1585 
1586     /**
1587      * @return {@code true} if this is an outgoing call to emergency services. An outgoing call is
1588      * identified as an emergency call by the dialer phone number.
1589      */
isEmergencyCall()1590     public boolean isEmergencyCall() {
1591         return mIsEmergencyCall;
1592     }
1593 
1594     /**
1595      * For testing purposes, set if this call is an emergency call or not.
1596      * @param isEmergencyCall {@code true} if emergency, {@code false} otherwise.
1597      */
1598     @VisibleForTesting
setIsEmergencyCall(boolean isEmergencyCall)1599     public void setIsEmergencyCall(boolean isEmergencyCall) {
1600         mIsEmergencyCall = isEmergencyCall;
1601     }
1602 
1603     /**
1604      * @return {@code true} if this an outgoing call to a test emergency number (and NOT to
1605      * emergency services). Used for testing purposes to differentiate between a real and fake
1606      * emergency call for safety reasons during testing.
1607      */
isTestEmergencyCall()1608     public boolean isTestEmergencyCall() {
1609         return mIsTestEmergencyCall;
1610     }
1611 
1612     /**
1613      * @return {@code true} if the target phone account is in ECBM.
1614      */
isInECBM()1615     public boolean isInECBM() {
1616         return mIsInECBM;
1617     }
1618 
1619     /**
1620      * Set if the target phone account is in ECBM.
1621      * @param isInEcbm {@code true} if target phone account is in ECBM, {@code false} otherwise.
1622      */
setIsInECBM(boolean isInECBM)1623     public void setIsInECBM(boolean isInECBM) {
1624         mIsInECBM = isInECBM;
1625     }
1626 
1627     /**
1628      * @return {@code true} if the network has identified this call as an emergency call.
1629      */
isNetworkIdentifiedEmergencyCall()1630     public boolean isNetworkIdentifiedEmergencyCall() {
1631         return hasProperty(Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL);
1632     }
1633 
1634     /**
1635      * @return The original handle this call is associated with. In-call services should use this
1636      * handle when indicating in their UI the handle that is being called.
1637      */
getOriginalHandle()1638     public Uri getOriginalHandle() {
1639         if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
1640             return mGatewayInfo.getOriginalAddress();
1641         }
1642         return getHandle();
1643     }
1644 
1645     @VisibleForTesting
getGatewayInfo()1646     public GatewayInfo getGatewayInfo() {
1647         return mGatewayInfo;
1648     }
1649 
setGatewayInfo(GatewayInfo gatewayInfo)1650     void setGatewayInfo(GatewayInfo gatewayInfo) {
1651         mGatewayInfo = gatewayInfo;
1652     }
1653 
1654     @VisibleForTesting
getConnectionManagerPhoneAccount()1655     public PhoneAccountHandle getConnectionManagerPhoneAccount() {
1656         return mConnectionManagerPhoneAccountHandle;
1657     }
1658 
1659     @VisibleForTesting
setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle)1660     public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
1661         if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
1662             mConnectionManagerPhoneAccountHandle = accountHandle;
1663             for (Listener l : mListeners) {
1664                 l.onConnectionManagerPhoneAccountChanged(this);
1665             }
1666         }
1667         checkIfRttCapable();
1668     }
1669 
1670     /**
1671      * @return the {@link PhoneAccountHandle} of the remote connection service which placing this
1672      * call was delegated to, or {@code null} if a remote connection service was not used.
1673      */
getRemotePhoneAccountHandle()1674     public @Nullable PhoneAccountHandle getRemotePhoneAccountHandle() {
1675         return mRemotePhoneAccountHandle;
1676     }
1677 
1678     /**
1679      * Sets the {@link PhoneAccountHandle} of the remote connection service which placing this
1680      * call was delegated to.
1681      * @param accountHandle The phone account handle.
1682      */
setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle)1683     public void setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle) {
1684         mRemotePhoneAccountHandle = accountHandle;
1685     }
1686 
1687     /**
1688      * Determines which {@link PhoneAccountHandle} is actually placing a call.
1689      * Where {@link #getRemotePhoneAccountHandle()} is non-null, the connection manager is placing
1690      * the call via a remote connection service, so the remote connection service's phone account
1691      * is the source.
1692      * Where {@link #getConnectionManagerPhoneAccount()} is non-null and
1693      * {@link #getRemotePhoneAccountHandle()} is null, the connection manager is placing the call
1694      * itself (even if the target specifies something else).
1695      * Finally, if neither of the above cases apply, the target phone account is the one actually
1696      * placing the call.
1697      * @return The {@link PhoneAccountHandle} which is actually placing a call.
1698      */
getDelegatePhoneAccountHandle()1699     public @NonNull PhoneAccountHandle getDelegatePhoneAccountHandle() {
1700         if (mRemotePhoneAccountHandle != null) {
1701             return mRemotePhoneAccountHandle;
1702         }
1703         if (mConnectionManagerPhoneAccountHandle != null) {
1704             return mConnectionManagerPhoneAccountHandle;
1705         }
1706         return mTargetPhoneAccountHandle;
1707     }
1708 
1709     @VisibleForTesting
getTargetPhoneAccount()1710     public PhoneAccountHandle getTargetPhoneAccount() {
1711         return mTargetPhoneAccountHandle;
1712     }
1713 
1714     @VisibleForTesting
setTargetPhoneAccount(PhoneAccountHandle accountHandle)1715     public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
1716         if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
1717             mTargetPhoneAccountHandle = accountHandle;
1718             // Update the last MO emergency call in the helper, if applicable.
1719             if (isEmergencyCall() && !isIncoming()) {
1720                 mCallsManager.getEmergencyCallHelper().setLastOutgoingEmergencyCallPAH(
1721                         accountHandle);
1722             }
1723             for (Listener l : mListeners) {
1724                 l.onTargetPhoneAccountChanged(this);
1725             }
1726             configureCallAttributes();
1727         }
1728         checkIfVideoCapable();
1729         checkIfRttCapable();
1730 
1731         if (accountHandle != null) {
1732             mCallStateChangedAtomWriter.setUid(
1733                     accountHandle.getComponentName().getPackageName(),
1734                     mContext.getPackageManager());
1735             // Set the associated user for the call for MT calls based on the target phone account.
1736             if (isIncoming() && !accountHandle.getUserHandle().equals(mAssociatedUser)) {
1737                 setAssociatedUser(accountHandle.getUserHandle());
1738             }
1739         }
1740     }
1741 
getPhoneAccountFromHandle()1742     public PhoneAccount getPhoneAccountFromHandle() {
1743         if (getTargetPhoneAccount() == null) {
1744             return null;
1745         }
1746         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1747                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1748 
1749         if (phoneAccount == null) {
1750             return null;
1751         }
1752 
1753         return phoneAccount;
1754     }
1755 
getTargetPhoneAccountLabel()1756     public CharSequence getTargetPhoneAccountLabel() {
1757         if (getTargetPhoneAccount() == null) {
1758             return null;
1759         }
1760         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1761                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1762 
1763         if (phoneAccount == null) {
1764             return null;
1765         }
1766 
1767         return phoneAccount.getLabel();
1768     }
1769 
1770     /**
1771      * Determines if this Call should be written to the call log.
1772      * @return {@code true} for managed calls or for self-managed calls which have the
1773      * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set.
1774      */
isLoggedSelfManaged()1775     public boolean isLoggedSelfManaged() {
1776         if (!isSelfManaged()) {
1777             // Managed calls are always logged.
1778             return true;
1779         }
1780         if (getTargetPhoneAccount() == null) {
1781             return false;
1782         }
1783         PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1784                 .getPhoneAccountUnchecked(getTargetPhoneAccount());
1785 
1786         if (phoneAccount == null) {
1787             return false;
1788         }
1789 
1790         if (getHandle() == null) {
1791             // No point in logging a null-handle call. Some self-managed calls will have this.
1792             return false;
1793         }
1794 
1795         if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) &&
1796                 !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) {
1797             // Can't log schemes other than SIP or TEL for now.
1798             return false;
1799         }
1800 
1801         return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean(
1802                 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false);
1803     }
1804 
1805     @VisibleForTesting
isIncoming()1806     public boolean isIncoming() {
1807         return mCallDirection == CALL_DIRECTION_INCOMING;
1808     }
1809 
isExternalCall()1810     public boolean isExternalCall() {
1811         return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
1812                 Connection.PROPERTY_IS_EXTERNAL_CALL;
1813     }
1814 
isWorkCall()1815     public boolean isWorkCall() {
1816         return mIsWorkCall;
1817     }
1818 
isUsingCallRecordingTone()1819     public boolean isUsingCallRecordingTone() {
1820         return mUseCallRecordingTone;
1821     }
1822 
1823     /**
1824      * @return {@code true} if the {@link Call}'s {@link #getTargetPhoneAccount()} supports video.
1825      */
isVideoCallingSupportedByPhoneAccount()1826     public boolean isVideoCallingSupportedByPhoneAccount() {
1827         return mIsVideoCallingSupportedByPhoneAccount;
1828     }
1829 
1830     /**
1831      * Sets whether video calling is supported by the current phone account. Since video support
1832      * can change during a call, this method facilitates updating call video state.
1833      * @param isVideoCallingSupported Sets whether video calling is supported.
1834      */
setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported)1835     public void setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported) {
1836         if (mIsVideoCallingSupportedByPhoneAccount == isVideoCallingSupported) {
1837             return;
1838         }
1839         Log.i(this, "setVideoCallingSupportedByPhoneAccount: isSupp=%b", isVideoCallingSupported);
1840         mIsVideoCallingSupportedByPhoneAccount = isVideoCallingSupported;
1841 
1842         // Force an update of the connection capabilities so that the dialer is informed of the new
1843         // video capabilities based on the phone account's support for video.
1844         setConnectionCapabilities(getConnectionCapabilities(), true /* force */);
1845     }
1846 
1847     /**
1848      * Determines if pulling this external call is supported. If it is supported, we will allow the
1849      * {@link Connection#CAPABILITY_CAN_PULL_CALL} capability to be added to this call's
1850      * capabilities. If it is not supported, we will strip this capability before sending this
1851      * call's capabilities to the InCallService.
1852      * @param isPullExternalCallSupported true, if pulling this external call is supported, false
1853      *                                    otherwise.
1854      */
setIsPullExternalCallSupported(boolean isPullExternalCallSupported)1855     public void setIsPullExternalCallSupported(boolean isPullExternalCallSupported) {
1856         if (!isExternalCall()) return;
1857         if (isPullExternalCallSupported == mIsPullExternalCallSupported) return;
1858 
1859         Log.i(this, "setCanPullExternalCall: canPull=%b", isPullExternalCallSupported);
1860 
1861         mIsPullExternalCallSupported = isPullExternalCallSupported;
1862 
1863         // Use mConnectionCapabilities here to get the unstripped capabilities.
1864         setConnectionCapabilities(mConnectionCapabilities, true /* force */);
1865     }
1866 
1867     /**
1868      * @return {@code true} if the {@link Call} locally supports video.
1869      */
isLocallyVideoCapable()1870     public boolean isLocallyVideoCapable() {
1871         return (getConnectionCapabilities() & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
1872                 == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL;
1873     }
1874 
isSelfManaged()1875     public boolean isSelfManaged() {
1876         return mIsSelfManaged;
1877     }
1878 
setIsSelfManaged(boolean isSelfManaged)1879     public void setIsSelfManaged(boolean isSelfManaged) {
1880         mIsSelfManaged = isSelfManaged;
1881 
1882         // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1883         setConnectionProperties(getConnectionProperties());
1884     }
1885 
isTransactionalCall()1886     public boolean isTransactionalCall() {
1887         return mIsTransactionalCall;
1888     }
1889 
setIsTransactionalCall(boolean isTransactionalCall)1890     public void setIsTransactionalCall(boolean isTransactionalCall) {
1891         mIsTransactionalCall = isTransactionalCall;
1892 
1893         // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1894         setConnectionProperties(getConnectionProperties());
1895     }
1896 
setCallingPackageIdentity(Bundle extras)1897     public void setCallingPackageIdentity(Bundle extras) {
1898         mCallingPackageIdentity = new CallingPackageIdentity(extras);
1899         // These extras should NOT be propagated to Dialer and should be removed.
1900         extras.remove(CallAttributes.CALLER_PID_KEY);
1901         extras.remove(CallAttributes.CALLER_UID_KEY);
1902     }
1903 
getCallingPackageIdentity()1904     public CallingPackageIdentity getCallingPackageIdentity() {
1905         return mCallingPackageIdentity;
1906     }
1907 
setTransactionServiceWrapper(TransactionalServiceWrapper service)1908     public void setTransactionServiceWrapper(TransactionalServiceWrapper service) {
1909         mTransactionalService = service;
1910     }
1911 
getTransactionServiceWrapper()1912     public TransactionalServiceWrapper getTransactionServiceWrapper() {
1913         return mTransactionalService;
1914     }
1915 
visibleToInCallService()1916     public boolean visibleToInCallService() {
1917         return mVisibleToInCallService;
1918     }
1919 
setVisibleToInCallService(boolean visibleToInCallService)1920     public void setVisibleToInCallService(boolean visibleToInCallService) {
1921         mVisibleToInCallService = visibleToInCallService;
1922     }
1923 
markFinishedHandoverStateAndCleanup(int handoverState)1924     public void markFinishedHandoverStateAndCleanup(int handoverState) {
1925         if (mHandoverSourceCall != null) {
1926             mHandoverSourceCall.setHandoverState(handoverState);
1927         } else if (mHandoverDestinationCall != null) {
1928             mHandoverDestinationCall.setHandoverState(handoverState);
1929         }
1930         setHandoverState(handoverState);
1931         maybeCleanupHandover();
1932     }
1933 
maybeCleanupHandover()1934     public void maybeCleanupHandover() {
1935         if (mHandoverSourceCall != null) {
1936             mHandoverSourceCall.setHandoverSourceCall(null);
1937             mHandoverSourceCall.setHandoverDestinationCall(null);
1938             mHandoverSourceCall = null;
1939         } else if (mHandoverDestinationCall != null) {
1940             mHandoverDestinationCall.setHandoverSourceCall(null);
1941             mHandoverDestinationCall.setHandoverDestinationCall(null);
1942             mHandoverDestinationCall = null;
1943         }
1944     }
1945 
isHandoverInProgress()1946     public boolean isHandoverInProgress() {
1947         return mHandoverSourceCall != null || mHandoverDestinationCall != null;
1948     }
1949 
getHandoverDestinationCall()1950     public Call getHandoverDestinationCall() {
1951         return mHandoverDestinationCall;
1952     }
1953 
setHandoverDestinationCall(Call call)1954     public void setHandoverDestinationCall(Call call) {
1955         mHandoverDestinationCall = call;
1956     }
1957 
getHandoverSourceCall()1958     public Call getHandoverSourceCall() {
1959         return mHandoverSourceCall;
1960     }
1961 
setHandoverSourceCall(Call call)1962     public void setHandoverSourceCall(Call call) {
1963         mHandoverSourceCall = call;
1964     }
1965 
setHandoverState(int handoverState)1966     public void setHandoverState(int handoverState) {
1967         Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(),
1968                 HandoverState.stateToString(handoverState));
1969         mHandoverState = handoverState;
1970     }
1971 
getHandoverState()1972     public int getHandoverState() {
1973         return mHandoverState;
1974     }
1975 
configureCallAttributes()1976     private void configureCallAttributes() {
1977         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1978         boolean isWorkCall = false;
1979         boolean isCallRecordingToneSupported = false;
1980         boolean isSimCall = false;
1981         PhoneAccount phoneAccount =
1982                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1983         if (phoneAccount != null) {
1984             final UserHandle userHandle;
1985             if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
1986                 userHandle = mAssociatedUser;
1987             } else {
1988                 userHandle = mTargetPhoneAccountHandle.getUserHandle();
1989             }
1990             if (userHandle != null) {
1991                 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
1992             }
1993 
1994             isCallRecordingToneSupported = (phoneAccount.hasCapabilities(
1995                     PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null
1996                     && phoneAccount.getExtras().getBoolean(
1997                     PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false));
1998             isSimCall = phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION);
1999         }
2000         mIsWorkCall = isWorkCall;
2001         mUseCallRecordingTone = isCallRecordingToneSupported;
2002         mIsSimCall = isSimCall;
2003     }
2004 
2005     /**
2006      * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
2007      * capability and ensures that the video state is updated if the phone account does not support
2008      * video calling.
2009      */
checkIfVideoCapable()2010     private void checkIfVideoCapable() {
2011         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
2012         if (mTargetPhoneAccountHandle == null) {
2013             // If no target phone account handle is specified, assume we can potentially perform a
2014             // video call; once the phone account is set, we can confirm that it is video capable.
2015             mIsVideoCallingSupportedByPhoneAccount = true;
2016             Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
2017             return;
2018         }
2019         PhoneAccount phoneAccount =
2020                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
2021         mIsVideoCallingSupportedByPhoneAccount = phoneAccount != null &&
2022                 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING);
2023 
2024         if (!mIsVideoCallingSupportedByPhoneAccount && VideoProfile.isVideo(getVideoState())) {
2025             // The PhoneAccount for the Call was set to one which does not support video calling,
2026             // and the current call is configured to be a video call; downgrade to audio-only.
2027             setVideoState(VideoProfile.STATE_AUDIO_ONLY);
2028             Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
2029         }
2030     }
2031 
checkIfRttCapable()2032     private void checkIfRttCapable() {
2033         PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
2034         if (mTargetPhoneAccountHandle == null) {
2035             return;
2036         }
2037 
2038         // Check both the target phone account and the connection manager phone account -- if
2039         // either support RTT, just set the streams and have them set/unset the RTT property as
2040         // needed.
2041         PhoneAccount phoneAccount =
2042                 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
2043         PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked(
2044                         mConnectionManagerPhoneAccountHandle);
2045         boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities(
2046                 PhoneAccount.CAPABILITY_RTT);
2047         boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null
2048                 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT);
2049 
2050         if ((isConnectionManagerRttSupported || isRttSupported)
2051                 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) {
2052             // If the phone account got set to an RTT capable one and we haven't set the streams
2053             // yet, do so now.
2054             createRttStreams();
2055             Log.i(this, "Setting RTT streams after target phone account selected");
2056         }
2057     }
2058 
shouldAttachToExistingConnection()2059     boolean shouldAttachToExistingConnection() {
2060         return mShouldAttachToExistingConnection;
2061     }
2062 
2063     /**
2064      * Note: This method relies on {@link #mConnectElapsedTimeMillis} and
2065      * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could
2066      * change due to clock changes).
2067      * @return The "age" of this call object in milliseconds, which typically also represents the
2068      *     period since this call was added to the set pending outgoing calls.
2069      */
2070     @VisibleForTesting
getAgeMillis()2071     public long getAgeMillis() {
2072         if (mState == CallState.DISCONNECTED &&
2073                 (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
2074                  mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
2075             // Rejected and missed calls have no age. They're immortal!!
2076             return 0;
2077         } else if (mConnectElapsedTimeMillis == 0) {
2078             // Age is measured in the amount of time the call was active. A zero connect time
2079             // indicates that we never went active, so return 0 for the age.
2080             return 0;
2081         } else if (mDisconnectElapsedTimeMillis == 0) {
2082             // We connected, but have not yet disconnected
2083             return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis;
2084         }
2085 
2086         return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis;
2087     }
2088 
2089     /**
2090      * @return The time when this call object was created and added to the set of pending outgoing
2091      *     calls.
2092      */
getCreationTimeMillis()2093     public long getCreationTimeMillis() {
2094         return mCreationTimeMillis;
2095     }
2096 
2097     /**
2098      * @return The elapsed realtime millis when the call was created; ONLY useful for determining
2099      * how long has elapsed since the call was first created.
2100      */
getCreationElapsedRealtimeMillis()2101     public long getCreationElapsedRealtimeMillis() {
2102         return mCreationElapsedRealtimeMillis;
2103     }
2104 
getConnectTimeMillis()2105     public long getConnectTimeMillis() {
2106         return mConnectTimeMillis;
2107     }
2108 
setConnectTimeMillis(long connectTimeMillis)2109     public void setConnectTimeMillis(long connectTimeMillis) {
2110         mConnectTimeMillis = connectTimeMillis;
2111     }
2112 
setConnectElapsedTimeMillis(long connectElapsedTimeMillis)2113     public void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) {
2114         mConnectElapsedTimeMillis = connectElapsedTimeMillis;
2115     }
2116 
getConnectionCapabilities()2117     public int getConnectionCapabilities() {
2118         return stripUnsupportedCapabilities(mConnectionCapabilities);
2119     }
2120 
getConnectionProperties()2121     int getConnectionProperties() {
2122         return mConnectionProperties;
2123     }
2124 
setConnectionCapabilities(int connectionCapabilities)2125     public void setConnectionCapabilities(int connectionCapabilities) {
2126         setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
2127     }
2128 
setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate)2129     void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
2130         Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
2131                 connectionCapabilities));
2132         if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
2133             int previousCapabilities = mConnectionCapabilities;
2134             mConnectionCapabilities = connectionCapabilities;
2135             for (Listener l : mListeners) {
2136                 l.onConnectionCapabilitiesChanged(this);
2137             }
2138 
2139             int strippedCaps = getConnectionCapabilities();
2140             int xorCaps = previousCapabilities ^ strippedCaps;
2141             Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
2142                     "Current: [%s], Removed [%s], Added [%s]",
2143                     Connection.capabilitiesToStringShort(strippedCaps),
2144                     Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
2145                     Connection.capabilitiesToStringShort(strippedCaps & xorCaps));
2146         }
2147     }
2148 
2149     /**
2150      * For some states of Telecom, we need to modify this connection's capabilities:
2151      * - A user should not be able to pull an external call during an emergency call, so
2152      *   CAPABILITY_CAN_PULL_CALL should be removed until the emergency call ends.
2153      * @param capabilities The original capabilities.
2154      * @return The stripped capabilities.
2155      */
stripUnsupportedCapabilities(int capabilities)2156     private int stripUnsupportedCapabilities(int capabilities) {
2157         if (!mIsPullExternalCallSupported) {
2158             if ((capabilities |= Connection.CAPABILITY_CAN_PULL_CALL) > 0) {
2159                 capabilities &= ~Connection.CAPABILITY_CAN_PULL_CALL;
2160                 Log.i(this, "stripCapabilitiesBasedOnState: CAPABILITY_CAN_PULL_CALL removed.");
2161             }
2162         }
2163         return capabilities;
2164     }
2165 
setConnectionProperties(int connectionProperties)2166     public void setConnectionProperties(int connectionProperties) {
2167         Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
2168                 connectionProperties));
2169 
2170         // Ensure the ConnectionService can't change the state of the self-managed property.
2171         if (isSelfManaged()) {
2172             connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
2173         } else {
2174             connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
2175         }
2176 
2177         int changedProperties = mConnectionProperties ^ connectionProperties;
2178 
2179         if (changedProperties != 0) {
2180             int previousProperties = mConnectionProperties;
2181             mConnectionProperties = connectionProperties;
2182             boolean didRttChange =
2183                     (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2184             if (didRttChange) {
2185                 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
2186                         Connection.PROPERTY_IS_RTT) {
2187                     // If we already had RTT streams up, that means that either the call started
2188                     // with RTT or the user previously requested to start RTT. Either way, don't
2189                     // play the alert tone.
2190                     if (!areRttStreamsInitialized()) {
2191                         mCallsManager.playRttUpgradeToneForCall(this);
2192                     }
2193 
2194                     createRttStreams();
2195                     // Call startRtt to pass the RTT pipes down to the connection service.
2196                     // They already turned on the RTT property so no request should be sent.
2197                     if (mConnectionService != null) {
2198                         mConnectionService.startRtt(this,
2199                                 getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2200                     }
2201                     mWasEverRtt = true;
2202                     if (isEmergencyCall()) {
2203                         mCallsManager.mute(false);
2204                     }
2205                 } else {
2206                     closeRttStreams();
2207                     mInCallToConnectionServiceStreams = null;
2208                     mConnectionServiceToInCallStreams = null;
2209                 }
2210             }
2211             mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) ==
2212                     Connection.PROPERTY_HIGH_DEF_AUDIO;
2213             mWasWifi = (connectionProperties & Connection.PROPERTY_WIFI) > 0;
2214             for (Listener l : mListeners) {
2215                 l.onConnectionPropertiesChanged(this, didRttChange);
2216             }
2217 
2218             boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
2219                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
2220             boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
2221                     == Connection.PROPERTY_IS_EXTERNAL_CALL;
2222             if (wasExternal != isExternal) {
2223                 Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
2224                         isExternal);
2225                 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
2226                 if (isExternal) {
2227                     // If there is an ongoing emergency call, remove the ability for this call to
2228                     // be pulled.
2229                     boolean isInEmergencyCall = mCallsManager.isInEmergencyCall();
2230                     setIsPullExternalCallSupported(!isInEmergencyCall);
2231                 }
2232                 for (Listener l : mListeners) {
2233                     l.onExternalCallChanged(this, isExternal);
2234                 }
2235             }
2236 
2237             boolean wasDowngradedConference =
2238                     (previousProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
2239             boolean isDowngradedConference =
2240                     (connectionProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0;
2241             if (wasDowngradedConference && !isDowngradedConference) {
2242                 Log.i(this, "DOWNGRADED_CONFERENCE property removed; setting"
2243                         + " conference state to false");
2244                 setConferenceState(false);
2245             }
2246 
2247             mAnalytics.addCallProperties(mConnectionProperties);
2248 
2249             int xorProps = previousProperties ^ mConnectionProperties;
2250             Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
2251                     "Current: [%s], Removed [%s], Added [%s]",
2252                     Connection.propertiesToStringShort(mConnectionProperties),
2253                     Connection.propertiesToStringShort(previousProperties & xorProps),
2254                     Connection.propertiesToStringShort(mConnectionProperties & xorProps));
2255         }
2256     }
2257 
getSupportedAudioRoutes()2258     public int getSupportedAudioRoutes() {
2259         return mSupportedAudioRoutes;
2260     }
2261 
setSupportedAudioRoutes(int audioRoutes)2262     void setSupportedAudioRoutes(int audioRoutes) {
2263         if (mSupportedAudioRoutes != audioRoutes) {
2264             mSupportedAudioRoutes = audioRoutes;
2265         }
2266     }
2267 
2268     @VisibleForTesting
getParentCall()2269     public Call getParentCall() {
2270         return mParentCall;
2271     }
2272 
2273     @VisibleForTesting
getChildCalls()2274     public List<Call> getChildCalls() {
2275         return mChildCalls;
2276     }
2277 
2278     @VisibleForTesting
wasConferencePreviouslyMerged()2279     public boolean wasConferencePreviouslyMerged() {
2280         return mWasConferencePreviouslyMerged;
2281     }
2282 
isDisconnectingChildCall()2283     public boolean isDisconnectingChildCall() {
2284         return mIsDisconnectingChildCall;
2285     }
2286 
2287     /**
2288      * Sets whether this call is a child call.
2289      */
maybeSetCallAsDisconnectingChild()2290     private void maybeSetCallAsDisconnectingChild() {
2291         if (mParentCall != null) {
2292             mIsDisconnectingChildCall = true;
2293         }
2294     }
2295 
2296     @VisibleForTesting
getConferenceLevelActiveCall()2297     public Call getConferenceLevelActiveCall() {
2298         return mConferenceLevelActiveCall;
2299     }
2300 
getConnectionService()2301     public ConnectionServiceWrapper getConnectionService() {
2302         return mConnectionService;
2303     }
2304 
2305     /**
2306      * Retrieves the {@link Context} for the call.
2307      *
2308      * @return The {@link Context}.
2309      */
getContext()2310     public Context getContext() {
2311         return mContext;
2312     }
2313 
2314     @VisibleForTesting
setConnectionService(ConnectionServiceWrapper service)2315     public void setConnectionService(ConnectionServiceWrapper service) {
2316         Preconditions.checkNotNull(service);
2317 
2318         clearConnectionService();
2319 
2320         service.incrementAssociatedCallCount();
2321         mConnectionService = service;
2322         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
2323         mConnectionService.addCall(this);
2324     }
2325 
2326     /**
2327      * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
2328      * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
2329      * ConnectionService is NOT unbound if the call count hits zero.
2330      * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
2331      * {@link Conference} additions via a ConnectionManager.
2332      * The original {@link android.telecom.ConnectionService} will directly add external calls and
2333      * conferences to Telecom as well as the ConnectionManager, which will add to Telecom.  In these
2334      * cases since its first added to via the original CS, we want to change the CS responsible for
2335      * the call to the ConnectionManager rather than adding it again as another call/conference.
2336      *
2337      * @param service The new {@link ConnectionServiceWrapper}.
2338      */
replaceConnectionService(ConnectionServiceWrapper service)2339     public void replaceConnectionService(ConnectionServiceWrapper service) {
2340         Preconditions.checkNotNull(service);
2341 
2342         if (mConnectionService != null) {
2343             ConnectionServiceWrapper serviceTemp = mConnectionService;
2344             mConnectionService = null;
2345             serviceTemp.removeCall(this);
2346             serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
2347         }
2348 
2349         service.incrementAssociatedCallCount();
2350         mConnectionService = service;
2351         mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
2352     }
2353 
2354     /**
2355      * Clears the associated connection service.
2356      */
clearConnectionService()2357     void clearConnectionService() {
2358         if (mConnectionService != null) {
2359             ConnectionServiceWrapper serviceTemp = mConnectionService;
2360             mConnectionService = null;
2361             serviceTemp.removeCall(this);
2362 
2363             // Decrementing the count can cause the service to unbind, which itself can trigger the
2364             // service-death code.  Since the service death code tries to clean up any associated
2365             // calls, we need to make sure to remove that information (e.g., removeCall()) before
2366             // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
2367             // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
2368             // to do.
2369             decrementAssociatedCallCount(serviceTemp);
2370         }
2371     }
2372 
2373     /**
2374      * Starts the create connection sequence. Upon completion, there should exist an active
2375      * connection through a connection service (or the call will have failed).
2376      *
2377      * @param phoneAccountRegistrar The phone account registrar.
2378      */
startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)2379     void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
2380         if (mCreateConnectionProcessor != null) {
2381             Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
2382                     " due to a race between NewOutgoingCallIntentBroadcaster and " +
2383                     "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
2384                     "invocation.");
2385             return;
2386         }
2387         mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
2388                 phoneAccountRegistrar, mContext);
2389         mCreateConnectionProcessor.process();
2390     }
2391 
2392     @Override
handleCreateConferenceSuccess( CallIdMapper idMapper, ParcelableConference conference)2393     public void handleCreateConferenceSuccess(
2394             CallIdMapper idMapper,
2395             ParcelableConference conference) {
2396         Log.v(this, "handleCreateConferenceSuccessful %s", conference);
2397         mIsCreateConnectionComplete = true;
2398         setTargetPhoneAccount(conference.getPhoneAccount());
2399         setHandle(conference.getHandle(), conference.getHandlePresentation());
2400 
2401         setConnectionCapabilities(conference.getConnectionCapabilities());
2402         setConnectionProperties(conference.getConnectionProperties());
2403         setVideoProvider(conference.getVideoProvider());
2404         setVideoState(conference.getVideoState());
2405         setRingbackRequested(conference.isRingbackRequested());
2406         setStatusHints(conference.getStatusHints());
2407         putConnectionServiceExtras(conference.getExtras());
2408 
2409         switch (mCallDirection) {
2410             case CALL_DIRECTION_INCOMING:
2411                 // Listeners (just CallsManager for now) will be responsible for checking whether
2412                 // the call should be blocked.
2413                 for (Listener l : mListeners) {
2414                     l.onSuccessfulIncomingCall(this);
2415                 }
2416                 break;
2417             case CALL_DIRECTION_OUTGOING:
2418                 for (Listener l : mListeners) {
2419                     l.onSuccessfulOutgoingCall(this,
2420                             getStateFromConnectionState(conference.getState()));
2421                 }
2422                 break;
2423         }
2424     }
2425 
2426     @Override
handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection)2427     public void handleCreateConnectionSuccess(
2428             CallIdMapper idMapper,
2429             ParcelableConnection connection) {
2430         Log.v(this, "handleCreateConnectionSuccessful %s", connection);
2431         mIsCreateConnectionComplete = true;
2432         setTargetPhoneAccount(connection.getPhoneAccount());
2433         setHandle(connection.getHandle(), connection.getHandlePresentation());
2434 
2435         setCallerDisplayName(
2436                 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
2437         setConnectionCapabilities(connection.getConnectionCapabilities());
2438         setConnectionProperties(connection.getConnectionProperties());
2439         setIsVoipAudioMode(connection.getIsVoipAudioMode());
2440         setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
2441         setVideoProvider(connection.getVideoProvider());
2442         setVideoState(connection.getVideoState());
2443         setRingbackRequested(connection.isRingbackRequested());
2444         setStatusHints(connection.getStatusHints());
2445         putConnectionServiceExtras(connection.getExtras());
2446 
2447         mConferenceableCalls.clear();
2448         for (String id : connection.getConferenceableConnectionIds()) {
2449             mConferenceableCalls.add(idMapper.getCall(id));
2450         }
2451 
2452         switch (mCallDirection) {
2453             case CALL_DIRECTION_INCOMING:
2454                 setCallerNumberVerificationStatus(connection.getCallerNumberVerificationStatus());
2455 
2456                 // Listeners (just CallsManager for now) will be responsible for checking whether
2457                 // the call should be blocked.
2458                 for (Listener l : mListeners) {
2459                     l.onSuccessfulIncomingCall(this);
2460                 }
2461                 break;
2462             case CALL_DIRECTION_OUTGOING:
2463                 for (Listener l : mListeners) {
2464                     l.onSuccessfulOutgoingCall(this,
2465                             getStateFromConnectionState(connection.getState()));
2466                 }
2467                 break;
2468             case CALL_DIRECTION_UNKNOWN:
2469                 for (Listener l : mListeners) {
2470                     l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
2471                             .getState()));
2472                 }
2473                 break;
2474         }
2475     }
2476 
2477     @Override
handleCreateConferenceFailure(DisconnectCause disconnectCause)2478     public void handleCreateConferenceFailure(DisconnectCause disconnectCause) {
2479         Log.i(this, "handleCreateConferenceFailure; callid=%s, disconnectCause=%s",
2480                 getId(), disconnectCause);
2481         clearConnectionService();
2482         setDisconnectCause(disconnectCause);
2483         mCallsManager.markCallAsDisconnected(this, disconnectCause);
2484 
2485         switch (mCallDirection) {
2486             case CALL_DIRECTION_INCOMING:
2487                 for (Listener listener : mListeners) {
2488                     listener.onFailedIncomingCall(this);
2489                 }
2490                 break;
2491             case CALL_DIRECTION_OUTGOING:
2492                 for (Listener listener : mListeners) {
2493                     listener.onFailedOutgoingCall(this, disconnectCause);
2494                 }
2495                 break;
2496         }
2497     }
2498 
2499     @Override
handleCreateConnectionFailure(DisconnectCause disconnectCause)2500     public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
2501         Log.i(this, "handleCreateConnectionFailure; callid=%s, disconnectCause=%s",
2502                 getId(), disconnectCause);
2503         clearConnectionService();
2504         setDisconnectCause(disconnectCause);
2505         mCallsManager.markCallAsDisconnected(this, disconnectCause);
2506 
2507         switch (mCallDirection) {
2508             case CALL_DIRECTION_INCOMING:
2509                 for (Listener listener : mListeners) {
2510                     listener.onFailedIncomingCall(this);
2511                 }
2512                 break;
2513             case CALL_DIRECTION_OUTGOING:
2514                 for (Listener listener : mListeners) {
2515                     listener.onFailedOutgoingCall(this, disconnectCause);
2516                 }
2517                 break;
2518             case CALL_DIRECTION_UNKNOWN:
2519                 for (Listener listener : mListeners) {
2520                     listener.onFailedUnknownCall(this);
2521                 }
2522                 break;
2523         }
2524     }
2525 
2526     /**
2527      * Plays the specified DTMF tone.
2528      */
2529     @VisibleForTesting
playDtmfTone(char digit)2530     public void playDtmfTone(char digit) {
2531         if (mConnectionService == null) {
2532             Log.w(this, "playDtmfTone() request on a call without a connection service.");
2533         } else {
2534             Log.i(this, "Send playDtmfTone to connection service for call %s", this);
2535             mConnectionService.playDtmfTone(this, digit);
2536             Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
2537         }
2538         mPlayingDtmfTone = digit;
2539     }
2540 
2541     /**
2542      * Stops playing any currently playing DTMF tone.
2543      */
2544     @VisibleForTesting
stopDtmfTone()2545     public void stopDtmfTone() {
2546         if (mConnectionService == null) {
2547             Log.w(this, "stopDtmfTone() request on a call without a connection service.");
2548         } else {
2549             Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
2550             Log.addEvent(this, LogUtils.Events.STOP_DTMF);
2551             mConnectionService.stopDtmfTone(this);
2552         }
2553         mPlayingDtmfTone = NO_DTMF_TONE;
2554     }
2555 
2556     /**
2557      * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has
2558      * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise.
2559      */
isDtmfTonePlaying()2560     boolean isDtmfTonePlaying() {
2561         return mPlayingDtmfTone != NO_DTMF_TONE;
2562     }
2563 
2564     /**
2565      * Silences the ringer.
2566      */
silence()2567     void silence() {
2568         if (mConnectionService == null) {
2569             Log.w(this, "silence() request on a call without a connection service.");
2570         } else {
2571             Log.i(this, "Send silence to connection service for call %s", this);
2572             Log.addEvent(this, LogUtils.Events.SILENCE);
2573             mConnectionService.silence(this);
2574         }
2575     }
2576 
2577     @VisibleForTesting
disconnect()2578     public void disconnect() {
2579         disconnect(0);
2580     }
2581 
disconnect(String reason)2582     public void disconnect(String reason) {
2583         disconnect(0, reason);
2584     }
2585 
2586     /**
2587      * Attempts to disconnect the call through the connection service.
2588      */
2589     @VisibleForTesting
disconnect(long disconnectionTimeout)2590     public void disconnect(long disconnectionTimeout) {
2591         disconnect(disconnectionTimeout, "internal" /** reason */);
2592     }
2593 
2594     /**
2595      * Attempts to disconnect the call through the connection service.
2596      * @param reason the reason for the disconnect; used for logging purposes only.  In some cases
2597      *               this can be a package name if the disconnect was initiated through an API such
2598      *               as TelecomManager.
2599      */
2600     @VisibleForTesting
disconnect(long disconnectionTimeout, String reason)2601     public void disconnect(long disconnectionTimeout, String reason) {
2602         Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason);
2603 
2604         // Track that the call is now locally disconnecting.
2605         setLocallyDisconnecting(true);
2606         maybeSetCallAsDisconnectingChild();
2607 
2608         if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
2609                 mState == CallState.CONNECTING) {
2610             Log.i(this, "disconnect: Aborting call %s", getId());
2611             abort(disconnectionTimeout);
2612         } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
2613             if (mState == CallState.AUDIO_PROCESSING && !hasGoneActiveBefore()) {
2614                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
2615             } else if (mState == CallState.SIMULATED_RINGING) {
2616                 // This is the case where the dialer calls disconnect() because the call timed out
2617                 // or an emergency call was dialed while in this state.
2618                 // Override the disconnect cause to MISSED
2619                 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.MISSED));
2620             }
2621             if (mTransactionalService != null) {
2622                 mTransactionalService.onDisconnect(this, getDisconnectCause());
2623                 Log.i(this, "Send Disconnect to transactional service for call");
2624             } else if (mConnectionService == null) {
2625                 Log.e(this, new Exception(), "disconnect() request on a call without a"
2626                         + " connection service.");
2627             } else {
2628                 Log.i(this, "Send disconnect to connection service for call: %s", this);
2629                 // The call isn't officially disconnected until the connection service
2630                 // confirms that the call was actually disconnected. Only then is the
2631                 // association between call and connection service severed, see
2632                 // {@link CallsManager#markCallAsDisconnected}.
2633                 mConnectionService.disconnect(this);
2634             }
2635         }
2636     }
2637 
abort(long disconnectionTimeout)2638     void abort(long disconnectionTimeout) {
2639         if (mCreateConnectionProcessor != null &&
2640                 !mCreateConnectionProcessor.isProcessingComplete()) {
2641             mCreateConnectionProcessor.abort();
2642         } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
2643                 || mState == CallState.CONNECTING) {
2644             if (disconnectionTimeout > 0) {
2645                 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
2646                 // milliseconds, do not destroy the call.
2647                 // Instead, we announce the cancellation and CallsManager handles
2648                 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
2649                 // then re-dial them quickly using a gateway, allowing the first call to end
2650                 // causes jank. This timeout allows CallsManager to transition the first call into
2651                 // the second call so that in-call only ever sees a single call...eliminating the
2652                 // jank altogether. The app will also be able to set the timeout via an extra on
2653                 // the ordered broadcast.
2654                 for (Listener listener : mListeners) {
2655                     if (listener.onCanceledViaNewOutgoingCallBroadcast(
2656                             this, disconnectionTimeout)) {
2657                         // The first listener to handle this wins. A return value of true means that
2658                         // the listener will handle the disconnection process later and so we
2659                         // should not continue it here.
2660                         setLocallyDisconnecting(false);
2661                         return;
2662                     }
2663                 }
2664             }
2665 
2666             handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
2667         } else {
2668             Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
2669         }
2670     }
2671 
2672     /**
2673      * Answers the call if it is ringing.
2674      *
2675      * @param videoState The video state in which to answer the call.
2676      */
2677     @VisibleForTesting
answer(int videoState)2678     public void answer(int videoState) {
2679         // Check to verify that the call is still in the ringing state. A call can change states
2680         // between the time the user hits 'answer' and Telecom receives the command.
2681         if (isRinging("answer")) {
2682             if (!isVideoCallingSupportedByPhoneAccount() && VideoProfile.isVideo(videoState)) {
2683                 // Video calling is not supported, yet the InCallService is attempting to answer as
2684                 // video.  We will simply answer as audio-only.
2685                 videoState = VideoProfile.STATE_AUDIO_ONLY;
2686             }
2687             // At this point, we are asking the connection service to answer but we don't assume
2688             // that it will work. Instead, we wait until confirmation from the connectino service
2689             // that the call is in a non-STATE_RINGING state before changing the UI. See
2690             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
2691             if (mConnectionService != null) {
2692                 mConnectionService.answer(this, videoState);
2693             } else if (mTransactionalService != null) {
2694                 mTransactionalService.onAnswer(this, videoState);
2695             } else {
2696                 Log.e(this, new NullPointerException(),
2697                         "answer call failed due to null CS callId=%s", getId());
2698             }
2699             Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
2700         }
2701     }
2702 
2703     /**
2704      * Answers the call on the connectionservice side in order to start audio processing.
2705      *
2706      * This pathway keeps the call in the ANSWERED state until the connection service confirms the
2707      * answer, at which point we'll set it to AUDIO_PROCESSING. However, to prevent any other
2708      * components from seeing the churn between RINGING -> ANSWERED -> AUDIO_PROCESSING, we'll
2709      * refrain from tracking this call in CallsManager until we've stabilized in AUDIO_PROCESSING
2710      */
answerForAudioProcessing()2711     public void answerForAudioProcessing() {
2712         if (mState != CallState.RINGING) {
2713             Log.w(this, "Trying to audio-process a non-ringing call: id=%s", mId);
2714             return;
2715         }
2716 
2717         if (mConnectionService != null) {
2718             mConnectionService.answer(this, VideoProfile.STATE_AUDIO_ONLY);
2719         } else {
2720             Log.e(this, new NullPointerException(),
2721                     "answer call (audio processing) failed due to null CS callId=%s", getId());
2722         }
2723 
2724         Log.addEvent(this, LogUtils.Events.REQUEST_PICKUP_FOR_AUDIO_PROCESSING);
2725     }
2726 
setAudioProcessingRequestingApp(CharSequence appName)2727     public void setAudioProcessingRequestingApp(CharSequence appName) {
2728         mAudioProcessingRequestingApp = appName;
2729     }
2730 
getAudioProcessingRequestingApp()2731     public CharSequence getAudioProcessingRequestingApp() {
2732         return mAudioProcessingRequestingApp;
2733     }
2734 
2735     /**
2736      * Deflects the call if it is ringing.
2737      *
2738      * @param address address to be deflected to.
2739      */
2740     @VisibleForTesting
deflect(Uri address)2741     public void deflect(Uri address) {
2742         // Check to verify that the call is still in the ringing state. A call can change states
2743         // between the time the user hits 'deflect' and Telecomm receives the command.
2744         if (isRinging("deflect")) {
2745             // At this point, we are asking the connection service to deflect but we don't assume
2746             // that it will work. Instead, we wait until confirmation from the connection service
2747             // that the call is in a non-STATE_RINGING state before changing the UI. See
2748             // {@link ConnectionServiceAdapter#setActive} and other set* methods.
2749             mVideoStateHistory |= mVideoState;
2750             if (mConnectionService != null) {
2751                 mConnectionService.deflect(this, address);
2752             } else {
2753                 Log.e(this, new NullPointerException(),
2754                         "deflect call failed due to null CS callId=%s", getId());
2755             }
2756             Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address));
2757         }
2758     }
2759 
2760     /**
2761      * Rejects the call if it is ringing.
2762      *
2763      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
2764      * @param textMessage An optional text message to send as part of the rejection.
2765      */
2766     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage)2767     public void reject(boolean rejectWithMessage, String textMessage) {
2768         reject(rejectWithMessage, textMessage, "internal" /** reason */);
2769     }
2770 
2771     /**
2772      * Rejects the call if it is ringing.
2773      *
2774      * @param rejectWithMessage Whether to send a text message as part of the call rejection.
2775      * @param textMessage An optional text message to send as part of the rejection.
2776      * @param reason The reason for the reject; used for logging purposes.  May be a package name
2777      *               if the reject is initiated from an API such as TelecomManager.
2778      */
2779     @VisibleForTesting
reject(boolean rejectWithMessage, String textMessage, String reason)2780     public void reject(boolean rejectWithMessage, String textMessage, String reason) {
2781         if (mState == CallState.SIMULATED_RINGING) {
2782             // This handles the case where the user manually rejects a call that's in simulated
2783             // ringing. Since the call is already active on the connectionservice side, we want to
2784             // hangup, not reject.
2785             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
2786             if (mTransactionalService != null) {
2787                 mTransactionalService.onDisconnect(this,
2788                         new DisconnectCause(DisconnectCause.REJECTED));
2789             } else if (mConnectionService != null) {
2790                 mConnectionService.disconnect(this);
2791             } else {
2792                 Log.e(this, new NullPointerException(),
2793                         "reject call failed due to null CS callId=%s", getId());
2794             }
2795             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
2796         } else if (isRinging("reject") || isAnswered("reject")) {
2797             // Ensure video state history tracks video state at time of rejection.
2798             mVideoStateHistory |= mVideoState;
2799 
2800             if (mTransactionalService != null) {
2801                 mTransactionalService.onDisconnect(this,
2802                         new DisconnectCause(DisconnectCause.REJECTED));
2803             } else if (mConnectionService != null) {
2804                 mConnectionService.reject(this, rejectWithMessage, textMessage);
2805             } else {
2806                 Log.e(this, new NullPointerException(),
2807                         "reject call failed due to null CS callId=%s", getId());
2808             }
2809             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason);
2810         }
2811     }
2812 
2813     /**
2814      * Reject this Telecom call with the user-indicated reason.
2815      * @param rejectReason The user-indicated reason fore rejecting the call.
2816      */
reject(@ndroid.telecom.Call.RejectReason int rejectReason)2817     public void reject(@android.telecom.Call.RejectReason int rejectReason) {
2818         if (mState == CallState.SIMULATED_RINGING) {
2819             // This handles the case where the user manually rejects a call that's in simulated
2820             // ringing. Since the call is already active on the connectionservice side, we want to
2821             // hangup, not reject.
2822             // Since its simulated reason we can't pass along the reject reason.
2823             setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED));
2824             if (mTransactionalService != null) {
2825                 mTransactionalService.onDisconnect(this,
2826                         new DisconnectCause(DisconnectCause.REJECTED));
2827             } else if (mConnectionService != null) {
2828                 mConnectionService.disconnect(this);
2829             } else {
2830                 Log.e(this, new NullPointerException(),
2831                         "reject call failed due to null CS callId=%s", getId());
2832             }
2833             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT);
2834         } else if (isRinging("reject") || isAnswered("reject")) {
2835             // Ensure video state history tracks video state at time of rejection.
2836             mVideoStateHistory |= mVideoState;
2837             if (mTransactionalService != null) {
2838                 mTransactionalService.onDisconnect(this,
2839                         new DisconnectCause(DisconnectCause.REJECTED));
2840             } else if (mConnectionService != null) {
2841                 mConnectionService.rejectWithReason(this, rejectReason);
2842             } else {
2843                 Log.e(this, new NullPointerException(),
2844                         "reject call failed due to null CS callId=%s", getId());
2845             }
2846             Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, rejectReason);
2847         }
2848     }
2849 
2850     /**
2851      * Transfers the call if it is active or held.
2852      *
2853      * @param number number to be transferred to.
2854      * @param isConfirmationRequired whether for blind or assured transfer.
2855      */
2856     @VisibleForTesting
transfer(Uri number, boolean isConfirmationRequired)2857     public void transfer(Uri number, boolean isConfirmationRequired) {
2858         if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
2859             if (mTransactionalService != null) {
2860                 Log.i(this, "transfer: called on TransactionalService. doing nothing");
2861             } else if (mConnectionService != null) {
2862                 mConnectionService.transfer(this, number, isConfirmationRequired);
2863             } else {
2864                 Log.e(this, new NullPointerException(),
2865                         "transfer call failed due to null CS callId=%s", getId());
2866             }
2867             Log.addEvent(this, LogUtils.Events.REQUEST_TRANSFER, Log.pii(number));
2868         }
2869     }
2870 
2871     /**
2872      * Transfers the call when this call is active and the other call is held.
2873      * This is for Consultative call transfer.
2874      *
2875      * @param otherCall The other {@link Call} to which this call will be transferred.
2876      */
2877     @VisibleForTesting
transfer(Call otherCall)2878     public void transfer(Call otherCall) {
2879         if (mState == CallState.ACTIVE &&
2880                 (otherCall != null && otherCall.getState() == CallState.ON_HOLD)) {
2881             if (mTransactionalService != null) {
2882                 Log.i(this, "transfer: called on TransactionalService. doing nothing");
2883             } else if (mConnectionService != null) {
2884                 mConnectionService.transfer(this, otherCall);
2885             } else {
2886                 Log.e(this, new NullPointerException(),
2887                         "transfer call failed due to null CS callId=%s", getId());
2888             }
2889             Log.addEvent(this, LogUtils.Events.REQUEST_CONSULTATIVE_TRANSFER, otherCall);
2890         }
2891     }
2892 
2893     /**
2894      * Puts the call on hold if it is currently active.
2895      */
2896     @VisibleForTesting
hold()2897     public void hold() {
2898         hold(null /* reason */);
2899     }
2900 
hold(String reason)2901     public void hold(String reason) {
2902         if (mState == CallState.ACTIVE) {
2903             if (mTransactionalService != null) {
2904                 mTransactionalService.onSetInactive(this);
2905             } else if (mConnectionService != null) {
2906                 mConnectionService.hold(this);
2907             } else {
2908                 Log.e(this, new NullPointerException(),
2909                         "hold call failed due to null CS callId=%s", getId());
2910             }
2911             Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason);
2912         }
2913     }
2914 
2915     /**
2916      * Releases the call from hold if it is currently active.
2917      */
2918     @VisibleForTesting
unhold()2919     public void unhold() {
2920         unhold(null /* reason */);
2921     }
2922 
unhold(String reason)2923     public void unhold(String reason) {
2924         if (mState == CallState.ON_HOLD) {
2925             if (mTransactionalService != null){
2926                 mTransactionalService.onSetActive(this);
2927             } else if (mConnectionService != null){
2928                 mConnectionService.unhold(this);
2929             } else {
2930                 Log.e(this, new NullPointerException(),
2931                         "unhold call failed due to null CS callId=%s", getId());
2932             }
2933             Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason);
2934         }
2935     }
2936 
2937     /** Checks if this is a live call or not. */
2938     @VisibleForTesting
isAlive()2939     public boolean isAlive() {
2940         switch (mState) {
2941             case CallState.NEW:
2942             case CallState.RINGING:
2943             case CallState.ANSWERED:
2944             case CallState.DISCONNECTED:
2945             case CallState.ABORTED:
2946                 return false;
2947             default:
2948                 return true;
2949         }
2950     }
2951 
isActive()2952     public boolean isActive() {
2953         return mState == CallState.ACTIVE;
2954     }
2955 
2956     @VisibleForTesting
getExtras()2957     public Bundle getExtras() {
2958         return mExtras;
2959     }
2960 
2961     /**
2962      * Adds extras to the extras bundle associated with this {@link Call}, as made by a
2963      * {@link ConnectionService} or other non {@link android.telecom.InCallService} source.
2964      *
2965      * @param extras The extras.
2966      */
putConnectionServiceExtras(Bundle extras)2967     public void putConnectionServiceExtras(Bundle extras) {
2968         putExtras(SOURCE_CONNECTION_SERVICE, extras, null);
2969     }
2970 
2971     /**
2972      * Adds extras to the extras bundle associated with this {@link Call}, as made by a
2973      * {@link android.telecom.InCallService}.
2974      * @param extras the extras.
2975      * @param requestingPackageName the package name of the {@link android.telecom.InCallService}
2976      *                              which requested the extras changed; required so that when we
2977      *                              have {@link InCallController} notify other
2978      *                              {@link android.telecom.InCallService}s we don't notify the
2979      *                              originator of their own change.
2980      */
putInCallServiceExtras(Bundle extras, String requestingPackageName)2981     public void putInCallServiceExtras(Bundle extras, String requestingPackageName) {
2982         putExtras(SOURCE_INCALL_SERVICE, extras, requestingPackageName);
2983     }
2984 
2985     /**
2986      * Adds extras to the extras bundle associated with this {@link Call}.
2987      *
2988      * Note: this method needs to know the source of the extras change (see
2989      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
2990      * originate from a connection service will only be notified to incall services.  Changes
2991      * originating from the InCallServices will notify the connection service of the
2992      * change, as well as other InCallServices other than the originator.
2993      *
2994      * @param source The source of the extras addition.
2995      * @param extras The extras.
2996      * @param requestingPackageName The package name which requested the extras change.  For
2997      *                              {@link #SOURCE_INCALL_SERVICE} will be populated with the
2998      *                              package name of the ICS that requested the change.
2999      */
putExtras(int source, Bundle extras, String requestingPackageName)3000     private void putExtras(int source, Bundle extras, String requestingPackageName) {
3001         if (extras == null) {
3002             return;
3003         }
3004         if (mExtras == null) {
3005             mExtras = new Bundle();
3006         }
3007         mExtras.putAll(extras);
3008 
3009         for (Listener l : mListeners) {
3010             l.onExtrasChanged(this, source, extras, requestingPackageName);
3011         }
3012 
3013         // If mExtra shows that the call using Volte, record it with mWasVolte
3014         if (mExtras.containsKey(TelecomManager.EXTRA_CALL_NETWORK_TYPE) &&
3015             mExtras.get(TelecomManager.EXTRA_CALL_NETWORK_TYPE)
3016                     .equals(TelephonyManager.NETWORK_TYPE_LTE)) {
3017             mWasVolte = true;
3018         }
3019 
3020         if (extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) {
3021             setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID));
3022         }
3023 
3024         if (extras.containsKey(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS)
3025                 && source == SOURCE_CONNECTION_SERVICE) {
3026             int callerNumberVerificationStatus =
3027                     extras.getInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS);
3028             if (mCallerNumberVerificationStatus != callerNumberVerificationStatus) {
3029                 Log.addEvent(this, LogUtils.Events.VERSTAT_CHANGED, callerNumberVerificationStatus);
3030                 setCallerNumberVerificationStatus(callerNumberVerificationStatus);
3031             }
3032         }
3033 
3034         // The remote connection service API can track the phone account which was originally
3035         // requested to create a connection via the remote connection service API; we store that so
3036         // we have some visibility into how a call was actually placed.
3037         if (mExtras.containsKey(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE)) {
3038             setRemotePhoneAccountHandle(extras.getParcelable(
3039                     Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE));
3040         }
3041 
3042         // If the change originated from an InCallService, notify the connection service.
3043         if (source == SOURCE_INCALL_SERVICE) {
3044             Log.addEvent(this, LogUtils.Events.ICS_EXTRAS_CHANGED);
3045             if (mTransactionalService != null) {
3046                 Log.i(this, "putExtras: called on TransactionalService. doing nothing");
3047             } else if (mConnectionService != null) {
3048                 mConnectionService.onExtrasChanged(this, mExtras);
3049             } else {
3050                 Log.e(this, new NullPointerException(),
3051                         "putExtras failed due to null CS callId=%s", getId());
3052             }
3053         }
3054     }
3055 
3056     /**
3057      * Removes extras from the extras bundle associated with this {@link Call}.
3058      *
3059      * Note: this method needs to know the source of the extras change (see
3060      * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
3061      * originate from a connection service will only be notified to incall services.  Likewise,
3062      * changes originating from the incall services will only notify the connection service of the
3063      * change.
3064      *
3065      * @param source The source of the extras removal.
3066      * @param keys The extra keys to remove.
3067      */
removeExtras(int source, List<String> keys)3068     void removeExtras(int source, List<String> keys) {
3069         if (mExtras == null) {
3070             return;
3071         }
3072         for (String key : keys) {
3073             mExtras.remove(key);
3074         }
3075 
3076         for (Listener l : mListeners) {
3077             l.onExtrasRemoved(this, source, keys);
3078         }
3079 
3080         // If the change originated from an InCallService, notify the connection service.
3081         if (source == SOURCE_INCALL_SERVICE) {
3082             if (mTransactionalService != null) {
3083                 Log.i(this, "removeExtras: called on TransactionalService. doing nothing");
3084             } else if (mConnectionService != null) {
3085                 mConnectionService.onExtrasChanged(this, mExtras);
3086             } else {
3087                 Log.e(this, new NullPointerException(),
3088                         "removeExtras failed due to null CS callId=%s", getId());
3089             }
3090         }
3091     }
3092 
3093     @VisibleForTesting
getIntentExtras()3094     public Bundle getIntentExtras() {
3095         return mIntentExtras;
3096     }
3097 
setIntentExtras(Bundle extras)3098     void setIntentExtras(Bundle extras) {
3099         mIntentExtras = extras;
3100     }
3101 
getOriginalCallIntent()3102     public Intent getOriginalCallIntent() {
3103         return mOriginalCallIntent;
3104     }
3105 
setOriginalCallIntent(Intent intent)3106     public void setOriginalCallIntent(Intent intent) {
3107         mOriginalCallIntent = intent;
3108     }
3109 
3110     /**
3111      * @return the uri of the contact associated with this call.
3112      */
3113     @VisibleForTesting
getContactUri()3114     public Uri getContactUri() {
3115         if (mCallerInfo == null || !mCallerInfo.contactExists) {
3116             return getHandle();
3117         }
3118         return Contacts.getLookupUri(mCallerInfo.getContactId(), mCallerInfo.lookupKey);
3119     }
3120 
getRingtone()3121     Uri getRingtone() {
3122         return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
3123     }
3124 
onPostDialWait(String remaining)3125     void onPostDialWait(String remaining) {
3126         for (Listener l : mListeners) {
3127             l.onPostDialWait(this, remaining);
3128         }
3129     }
3130 
onPostDialChar(char nextChar)3131     void onPostDialChar(char nextChar) {
3132         for (Listener l : mListeners) {
3133             l.onPostDialChar(this, nextChar);
3134         }
3135     }
3136 
postDialContinue(boolean proceed)3137     void postDialContinue(boolean proceed) {
3138         if (mTransactionalService != null) {
3139             Log.i(this, "postDialContinue: called on TransactionalService. doing nothing");
3140         } else if (mConnectionService != null) {
3141             mConnectionService.onPostDialContinue(this, proceed);
3142         } else {
3143             Log.e(this, new NullPointerException(),
3144                     "postDialContinue failed due to null CS callId=%s", getId());
3145         }
3146     }
3147 
conferenceWith(Call otherCall)3148     void conferenceWith(Call otherCall) {
3149         if (mTransactionalService != null) {
3150             Log.i(this, "conferenceWith: called on TransactionalService. doing nothing");
3151         } else if (mConnectionService == null) {
3152             Log.w(this, "conference requested on a call without a connection service.");
3153         } else {
3154             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
3155             mConnectionService.conference(this, otherCall);
3156         }
3157     }
3158 
splitFromConference()3159     void splitFromConference() {
3160         if (mTransactionalService != null) {
3161             Log.i(this, "splitFromConference: called on TransactionalService. doing nothing");
3162         } else if (mConnectionService == null) {
3163             Log.w(this, "splitting from conference call without a connection service");
3164         } else {
3165             Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
3166             mConnectionService.splitFromConference(this);
3167         }
3168     }
3169 
3170     @VisibleForTesting
mergeConference()3171     public void mergeConference() {
3172         if (mTransactionalService != null) {
3173             Log.i(this, "mergeConference: called on TransactionalService. doing nothing");
3174         } else if (mConnectionService == null) {
3175             Log.w(this, "merging conference calls without a connection service.");
3176         } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
3177             Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
3178             mConnectionService.mergeConference(this);
3179             mWasConferencePreviouslyMerged = true;
3180         }
3181     }
3182 
3183     @VisibleForTesting
swapConference()3184     public void swapConference() {
3185         if (mTransactionalService != null) {
3186             Log.i(this, "swapConference: called on TransactionalService. doing nothing");
3187         } else if (mConnectionService == null) {
3188             Log.w(this, "swapping conference calls without a connection service.");
3189         } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
3190             Log.addEvent(this, LogUtils.Events.SWAP);
3191             mConnectionService.swapConference(this);
3192             switch (mChildCalls.size()) {
3193                 case 1:
3194                     mConferenceLevelActiveCall = mChildCalls.get(0);
3195                     break;
3196                 case 2:
3197                     // swap
3198                     mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
3199                             mChildCalls.get(1) : mChildCalls.get(0);
3200                     break;
3201                 default:
3202                     // For anything else 0, or 3+, set it to null since it is impossible to tell.
3203                     mConferenceLevelActiveCall = null;
3204                     break;
3205             }
3206             for (Listener l : mListeners) {
3207                 l.onCdmaConferenceSwap(this);
3208             }
3209         }
3210     }
3211 
addConferenceParticipants(List<Uri> participants)3212     public void addConferenceParticipants(List<Uri> participants) {
3213         if (mTransactionalService != null) {
3214             Log.i(this, "addConferenceParticipants: called on TransactionalService. doing nothing");
3215         } else if (mConnectionService == null) {
3216             Log.w(this, "adding conference participants without a connection service.");
3217         } else if (can(Connection.CAPABILITY_ADD_PARTICIPANT)) {
3218             Log.addEvent(this, LogUtils.Events.ADD_PARTICIPANT);
3219             mConnectionService.addConferenceParticipants(this, participants);
3220         }
3221     }
3222 
3223     /**
3224      * Initiates a request to the connection service to pull this call.
3225      * <p>
3226      * This method can only be used for calls that have the
3227      * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
3228      * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
3229      * <p>
3230      * An external call is a representation of a call which is taking place on another device
3231      * associated with a PhoneAccount on this device.  Issuing a request to pull the external call
3232      * tells the {@link android.telecom.ConnectionService} that it should move the call from the
3233      * other device to this one.  An example of this is the IMS multi-endpoint functionality.  A
3234      * user may have two phones with the same phone number.  If the user is engaged in an active
3235      * call on their first device, the network will inform the second device of that ongoing call in
3236      * the form of an external call.  The user may wish to continue their conversation on the second
3237      * device, so will issue a request to pull the call to the second device.
3238      * <p>
3239      * Requests to pull a call which is not external, or a call which is not pullable are ignored.
3240      * If there is an ongoing emergency call, pull requests are also ignored.
3241      */
pullExternalCall()3242     public void pullExternalCall() {
3243         if (mTransactionalService != null) {
3244             Log.i(this, "transfer: called on TransactionalService. doing nothing");
3245             return;
3246         }
3247 
3248         if (mConnectionService == null) {
3249             Log.w(this, "pulling a call without a connection service.");
3250         }
3251 
3252         if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
3253             Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
3254             return;
3255         }
3256 
3257         if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
3258             Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
3259             return;
3260         }
3261 
3262         if (mCallsManager.isInEmergencyCall()) {
3263             Log.w(this, "pullExternalCall = pullExternalCall - call %s is external but can not be"
3264                     + " pulled while an emergency call is in progress.", mId);
3265             mToastFactory.makeText(mContext, R.string.toast_emergency_can_not_pull_call,
3266                     Toast.LENGTH_LONG).show();
3267             return;
3268         }
3269 
3270         Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
3271         mConnectionService.pullExternalCall(this);
3272     }
3273 
3274     /**
3275      * Sends a call event to the {@link ConnectionService} for this call. This function is
3276      * called for event other than {@link Call#EVENT_REQUEST_HANDOVER}
3277      *
3278      * @param event The call event.
3279      * @param extras Associated extras.
3280      */
sendCallEvent(String event, Bundle extras)3281     public void sendCallEvent(String event, Bundle extras) {
3282         sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras);
3283     }
3284 
3285     /**
3286      * Sends a call event to the {@link ConnectionService} for this call.
3287      *
3288      * See {@link Call#sendCallEvent(String, Bundle)}.
3289      *
3290      * @param event The call event.
3291      * @param targetSdkVer SDK version of the app calling this api
3292      * @param extras Associated extras.
3293      */
sendCallEvent(String event, int targetSdkVer, Bundle extras)3294     public void sendCallEvent(String event, int targetSdkVer, Bundle extras) {
3295         if (mConnectionService != null || mTransactionalService != null) {
3296             if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) {
3297                 if (targetSdkVer > Build.VERSION_CODES.P) {
3298                     Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" +
3299                             " for API > 28(P)");
3300                     // Event-based Handover APIs are deprecated, so inform the user.
3301                     mHandler.post(new Runnable() {
3302                         @Override
3303                         public void run() {
3304                             mToastFactory.makeText(mContext,
3305                                     "WARNING: Event-based handover APIs are deprecated and will no"
3306                                             + " longer function in Android Q.",
3307                                     Toast.LENGTH_LONG).show();
3308                         }
3309                     });
3310 
3311                     // Uncomment and remove toast at feature complete: return;
3312                 }
3313 
3314                 // Handover requests are targeted at Telecom, not the ConnectionService.
3315                 if (extras == null) {
3316                     Log.w(this, "sendCallEvent: %s event received with null extras.",
3317                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
3318                     sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED,
3319                             null);
3320                     return;
3321                 }
3322                 Parcelable parcelable = extras.getParcelable(
3323                         android.telecom.Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE);
3324                 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) {
3325                     Log.w(this, "sendCallEvent: %s event received with invalid handover acct.",
3326                             android.telecom.Call.EVENT_REQUEST_HANDOVER);
3327                     sendEventToService(this, android.telecom.Call.EVENT_HANDOVER_FAILED, null);
3328                     return;
3329                 }
3330                 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable;
3331                 int videoState = extras.getInt(android.telecom.Call.EXTRA_HANDOVER_VIDEO_STATE,
3332                         VideoProfile.STATE_AUDIO_ONLY);
3333                 Parcelable handoverExtras = extras.getParcelable(
3334                         android.telecom.Call.EXTRA_HANDOVER_EXTRAS);
3335                 Bundle handoverExtrasBundle = null;
3336                 if (handoverExtras instanceof Bundle) {
3337                     handoverExtrasBundle = (Bundle) handoverExtras;
3338                 }
3339                 requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true);
3340             } else {
3341                 // Relay bluetooth call quality reports to the call diagnostic service.
3342                 if (BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT.equals(event)
3343                         && extras.containsKey(
3344                         BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT)) {
3345                     notifyBluetoothCallQualityReport(extras.getParcelable(
3346                             BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT
3347                     ));
3348                 }
3349                 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event);
3350                 sendEventToService(this, event, extras);
3351             }
3352         } else {
3353             Log.e(this, new NullPointerException(),
3354                     "sendCallEvent failed due to null CS callId=%s", getId());
3355         }
3356     }
3357 
3358     /**
3359      *  This method should only be called from sendCallEvent(String, int, Bundle).
3360      */
sendEventToService(Call call, String event, Bundle extras)3361     private void sendEventToService(Call call, String event, Bundle extras) {
3362         if (mConnectionService != null) {
3363             mConnectionService.sendCallEvent(call, event, extras);
3364         } else if (mTransactionalService != null) {
3365             mTransactionalService.onEvent(call, event, extras);
3366         }
3367     }
3368 
3369     /**
3370      * Notifies listeners when a bluetooth quality report is received.
3371      * @param report The bluetooth quality report.
3372      */
notifyBluetoothCallQualityReport(@onNull BluetoothCallQualityReport report)3373     void notifyBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport report) {
3374         Log.addEvent(this, LogUtils.Events.BT_QUALITY_REPORT, "choppy=" + report.isChoppyVoice());
3375         for (Listener l : mListeners) {
3376             l.onBluetoothCallQualityReport(this, report);
3377         }
3378     }
3379 
3380     /**
3381      * Initiates a handover of this Call to the {@link ConnectionService} identified
3382      * by destAcct.
3383      * @param destAcct ConnectionService to which the call should be handed over.
3384      * @param videoState The video state desired after the handover.
3385      * @param extras Extra information to be passed to ConnectionService
3386      */
handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras)3387     public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) {
3388         requestHandover(destAcct, videoState, extras, false);
3389     }
3390 
3391     /**
3392      * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
3393      * have this call as a child.
3394      * @param parentCall
3395      */
setParentAndChildCall(Call parentCall)3396     void setParentAndChildCall(Call parentCall) {
3397         boolean isParentChanging = (mParentCall != parentCall);
3398         setParentCall(parentCall);
3399         setChildOf(parentCall);
3400         if (isParentChanging) {
3401             notifyParentChanged(parentCall);
3402         }
3403     }
3404 
3405     /**
3406      * Notifies listeners when the parent call changes.
3407      * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
3408      * @param parentCall The new parent call for this call.
3409      */
notifyParentChanged(Call parentCall)3410     void notifyParentChanged(Call parentCall) {
3411         Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
3412         for (Listener l : mListeners) {
3413             l.onParentChanged(this);
3414         }
3415     }
3416 
3417     /**
3418      * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
3419      * the child.
3420      * TODO: This is only required when adding existing connections as a workaround so that we
3421      * can avoid sending the "onParentChanged" callback until later.
3422      * @param parentCall The new parent call.
3423      */
setParentCall(Call parentCall)3424     void setParentCall(Call parentCall) {
3425         if (parentCall == this) {
3426             Log.e(this, new Exception(), "setting the parent to self");
3427             return;
3428         }
3429         if (parentCall == mParentCall) {
3430             // nothing to do
3431             return;
3432         }
3433         if (mParentCall != null) {
3434             mParentCall.removeChildCall(this);
3435         }
3436         mParentCall = parentCall;
3437     }
3438 
3439     /**
3440      * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
3441      * this call as a child of another call.
3442      * <p>
3443      * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
3444      * ensure the InCall UI is updated with the change in parent.
3445      * @param parentCall The new parent for this call.
3446      */
setChildOf(Call parentCall)3447     public void setChildOf(Call parentCall) {
3448         if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
3449             parentCall.addChildCall(this);
3450         }
3451     }
3452 
setConferenceableCalls(List<Call> conferenceableCalls)3453     void setConferenceableCalls(List<Call> conferenceableCalls) {
3454         mConferenceableCalls.clear();
3455         mConferenceableCalls.addAll(conferenceableCalls);
3456         String confCallIds = "";
3457         if (!conferenceableCalls.isEmpty()) {
3458             confCallIds = conferenceableCalls.stream()
3459                     .map(c -> c.getId())
3460                     .collect(Collectors.joining(","));
3461         }
3462         Log.addEvent(this, LogUtils.Events.CONF_CALLS_CHANGED, confCallIds);
3463 
3464         for (Listener l : mListeners) {
3465             l.onConferenceableCallsChanged(this);
3466         }
3467     }
3468 
3469     @VisibleForTesting
getConferenceableCalls()3470     public List<Call> getConferenceableCalls() {
3471         return mConferenceableCalls;
3472     }
3473 
3474     @VisibleForTesting
can(int capability)3475     public boolean can(int capability) {
3476         return (getConnectionCapabilities() & capability) == capability;
3477     }
3478 
3479     @VisibleForTesting
hasProperty(int property)3480     public boolean hasProperty(int property) {
3481         return (mConnectionProperties & property) == property;
3482     }
3483 
addChildCall(Call call)3484     private void addChildCall(Call call) {
3485         if (!mChildCalls.contains(call)) {
3486             mHadChildren = true;
3487             // Set the pseudo-active call to the latest child added to the conference.
3488             // See definition of mConferenceLevelActiveCall for more detail.
3489             mConferenceLevelActiveCall = call;
3490             mChildCalls.add(call);
3491 
3492             // When adding a child, we will potentially adjust the various times from the calls
3493             // based on the children being added.  This ensures the parent of the conference has a
3494             // connect time reflective of all the children added.
3495             maybeAdjustConnectTime(call);
3496 
3497             Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
3498 
3499             for (Listener l : mListeners) {
3500                 l.onChildrenChanged(this);
3501             }
3502         }
3503     }
3504 
3505     /**
3506      * Potentially adjust the connect and creation time of this call based on another one.
3507      * Ensures that if the other call has an earlier connect time that we adjust the connect time of
3508      * this call to match.
3509      * <p>
3510      * This is important for conference calls; as we add children to the conference we need to
3511      * ensure that earlier connect time is reflected on the conference.  In the past this
3512      * was just done in {@link ParcelableCallUtils} when parceling the calls to the UI, but that
3513      * approach would not reflect the right time on the parent as children disconnect.
3514      *
3515      * @param call the call to potentially use to adjust connect time.
3516      */
maybeAdjustConnectTime(@onNull Call call)3517     private void maybeAdjustConnectTime(@NonNull Call call) {
3518         long childConnectTimeMillis = call.getConnectTimeMillis();
3519         long currentConnectTimeMillis = getConnectTimeMillis();
3520         // Conference calls typically have a 0 connect time, so we will replace the current connect
3521         // time if its zero also.
3522         if (childConnectTimeMillis != 0
3523                 && (currentConnectTimeMillis == 0
3524                 || childConnectTimeMillis < getConnectTimeMillis())) {
3525             setConnectTimeMillis(childConnectTimeMillis);
3526         }
3527     }
3528 
removeChildCall(Call call)3529     private void removeChildCall(Call call) {
3530         if (mChildCalls.remove(call)) {
3531             Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
3532             for (Listener l : mListeners) {
3533                 l.onChildrenChanged(this);
3534             }
3535         }
3536     }
3537 
3538     /**
3539      * Return whether the user can respond to this {@code Call} via an SMS message.
3540      *
3541      * @return true if the "Respond via SMS" feature should be enabled
3542      * for this incoming call.
3543      *
3544      * The general rule is that we *do* allow "Respond via SMS" except for
3545      * the few (relatively rare) cases where we know for sure it won't
3546      * work, namely:
3547      *   - a bogus or blank incoming number
3548      *   - a call from a SIP address
3549      *   - a "call presentation" that doesn't allow the number to be revealed
3550      *
3551      * In all other cases, we allow the user to respond via SMS.
3552      *
3553      * Note that this behavior isn't perfect; for example we have no way
3554      * to detect whether the incoming call is from a landline (with most
3555      * networks at least), so we still enable this feature even though
3556      * SMSes to that number will silently fail.
3557      */
isRespondViaSmsCapable()3558     public boolean isRespondViaSmsCapable() {
3559         if (mState != CallState.RINGING) {
3560             return false;
3561         }
3562 
3563         if (getHandle() == null) {
3564             // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
3565             // other words, the user should not be able to see the incoming phone number.
3566             return false;
3567         }
3568 
3569         if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
3570             // The incoming number is actually a URI (i.e. a SIP address),
3571             // not a regular PSTN phone number, and we can't send SMSes to
3572             // SIP addresses.
3573             // (TODO: That might still be possible eventually, though. Is
3574             // there some SIP-specific equivalent to sending a text message?)
3575             return false;
3576         }
3577 
3578         // Is there a valid SMS application on the phone?
3579         if (mContext.getSystemService(TelephonyManager.class)
3580                 .getAndUpdateDefaultRespondViaMessageApplication() == null) {
3581             return false;
3582         }
3583 
3584         // TODO: with some carriers (in certain countries) you *can* actually
3585         // tell whether a given number is a mobile phone or not. So in that
3586         // case we could potentially return false here if the incoming call is
3587         // from a land line.
3588 
3589         // If none of the above special cases apply, it's OK to enable the
3590         // "Respond via SMS" feature.
3591         return true;
3592     }
3593 
getCannedSmsResponses()3594     List<String> getCannedSmsResponses() {
3595         return mCannedSmsResponses;
3596     }
3597 
3598     /**
3599      * We need to make sure that before we move a call to the disconnected state, it no
3600      * longer has any parent/child relationships.  We want to do this to ensure that the InCall
3601      * Service always has the right data in the right order.  We also want to do it in telecom so
3602      * that the insurance policy lives in the framework side of things.
3603      */
fixParentAfterDisconnect()3604     private void fixParentAfterDisconnect() {
3605         setParentAndChildCall(null);
3606     }
3607 
3608     /**
3609      * @return True if the call is ringing, else logs the action name.
3610      */
isRinging(String actionName)3611     private boolean isRinging(String actionName) {
3612         if (mState == CallState.RINGING || mState == CallState.ANSWERED) {
3613             return true;
3614         }
3615 
3616         Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
3617         return false;
3618     }
3619 
3620     /**
3621      * @return True if the call is answered, else logs the action name.
3622      */
isAnswered(String actionName)3623     private boolean isAnswered(String actionName) {
3624         if (mState == CallState.ANSWERED) {
3625             return true;
3626         }
3627 
3628         Log.i(this, "Request to %s a non-answered call %s", actionName, this);
3629         return false;
3630     }
3631 
3632     @SuppressWarnings("rawtypes")
decrementAssociatedCallCount(ServiceBinder binder)3633     private void decrementAssociatedCallCount(ServiceBinder binder) {
3634         if (binder != null) {
3635             binder.decrementAssociatedCallCount();
3636         }
3637     }
3638 
3639     /**
3640      * Looks up contact information based on the current handle.
3641      */
startCallerInfoLookup()3642     private void startCallerInfoLookup() {
3643         mCallerInfo = null;
3644         mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
3645     }
3646 
3647     /**
3648      * Saves the specified caller info if the specified token matches that of the last query
3649      * that was made.
3650      *
3651      * @param callerInfo The new caller information to set.
3652      */
setCallerInfo(Uri handle, CallerInfo callerInfo)3653     private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
3654         Trace.beginSection("setCallerInfo");
3655         if (callerInfo == null) {
3656             Log.i(this, "CallerInfo lookup returned null, skipping update");
3657             return;
3658         }
3659 
3660         if ((handle != null) && !handle.equals(mHandle)) {
3661             Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
3662             return;
3663         }
3664 
3665         String newName = callerInfo.getName();
3666         boolean contactNameChanged = mCallerInfo == null || !mCallerInfo.getName().equals(newName);
3667 
3668         mCallerInfo = callerInfo;
3669         Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
3670 
3671         if (mCallerInfo.getContactDisplayPhotoUri() == null || mCallerInfo.cachedPhotoIcon != null
3672             || mCallerInfo.cachedPhoto != null || contactNameChanged) {
3673             for (Listener l : mListeners) {
3674                 l.onCallerInfoChanged(this);
3675             }
3676         }
3677 
3678         Trace.endSection();
3679     }
3680 
getCallerInfo()3681     public CallerInfo getCallerInfo() {
3682         return mCallerInfo;
3683     }
3684 
maybeLoadCannedSmsResponses()3685     private void maybeLoadCannedSmsResponses() {
3686         if (mCallDirection == CALL_DIRECTION_INCOMING
3687                 && isRespondViaSmsCapable()
3688                 && !mCannedSmsResponsesLoadingStarted) {
3689             Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
3690             mCannedSmsResponsesLoadingStarted = true;
3691             mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
3692                     new Response<Void, List<String>>() {
3693                         @Override
3694                         public void onResult(Void request, List<String>... result) {
3695                             if (result.length > 0) {
3696                                 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
3697                                 mCannedSmsResponses = result[0];
3698                                 for (Listener l : mListeners) {
3699                                     l.onCannedSmsResponsesLoaded(Call.this);
3700                                 }
3701                             }
3702                         }
3703 
3704                         @Override
3705                         public void onError(Void request, int code, String msg) {
3706                             Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
3707                                     msg);
3708                         }
3709                     },
3710                     mContext
3711             );
3712         } else {
3713             Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
3714         }
3715     }
3716 
3717     /**
3718      * Sets speakerphone option on when call begins.
3719      */
setStartWithSpeakerphoneOn(boolean startWithSpeakerphone)3720     public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
3721         mSpeakerphoneOn = startWithSpeakerphone;
3722     }
3723 
3724     /**
3725      * Returns speakerphone option.
3726      *
3727      * @return Whether or not speakerphone should be set automatically when call begins.
3728      */
getStartWithSpeakerphoneOn()3729     public boolean getStartWithSpeakerphoneOn() {
3730         return mSpeakerphoneOn;
3731     }
3732 
setRequestedToStartWithRtt()3733     public void setRequestedToStartWithRtt() {
3734         mDidRequestToStartWithRtt = true;
3735     }
3736 
stopRtt()3737     public void stopRtt() {
3738         if (mTransactionalService != null) {
3739             Log.i(this, "stopRtt: called on TransactionalService. doing nothing");
3740         } else if (mConnectionService != null) {
3741             Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "stop");
3742             mConnectionService.stopRtt(this);
3743         } else {
3744             // If this gets called by the in-call app before the connection service is set, we'll
3745             // just ignore it since it's really not supposed to happen.
3746             Log.w(this, "stopRtt() called before connection service is set.");
3747         }
3748     }
3749 
sendRttRequest()3750     public void sendRttRequest() {
3751         if (mTransactionalService != null) {
3752             Log.i(this, "sendRttRequest: called on TransactionalService. doing nothing");
3753             return;
3754         }
3755         Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "start");
3756         createRttStreams();
3757         mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
3758     }
3759 
areRttStreamsInitialized()3760     private boolean areRttStreamsInitialized() {
3761         return mInCallToConnectionServiceStreams != null
3762                 && mConnectionServiceToInCallStreams != null;
3763     }
3764 
createRttStreams()3765     public void createRttStreams() {
3766         if (!areRttStreamsInitialized()) {
3767             Log.i(this, "Initializing RTT streams");
3768             try {
3769                 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
3770                 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
3771             } catch (IOException e) {
3772                 Log.e(this, e, "Failed to create pipes for RTT call.");
3773             }
3774         }
3775     }
3776 
onRttConnectionFailure(int reason)3777     public void onRttConnectionFailure(int reason) {
3778         Log.i(this, "Got RTT initiation failure with reason %d", reason);
3779         Log.addEvent(this, LogUtils.Events.ON_RTT_FAILED, "reason="  + reason);
3780         for (Listener l : mListeners) {
3781             l.onRttInitiationFailure(this, reason);
3782         }
3783     }
3784 
onRemoteRttRequest()3785     public void onRemoteRttRequest() {
3786         Log.addEvent(this, LogUtils.Events.ON_RTT_REQUEST);
3787         if (isRttCall()) {
3788             Log.w(this, "Remote RTT request on a call that's already RTT");
3789             return;
3790         }
3791 
3792         mPendingRttRequestId = mCallsManager.getNextRttRequestId();
3793         for (Listener l : mListeners) {
3794             l.onRemoteRttRequest(this, mPendingRttRequestId);
3795         }
3796     }
3797 
handleRttRequestResponse(int id, boolean accept)3798     public void handleRttRequestResponse(int id, boolean accept) {
3799         if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
3800             Log.w(this, "Response received to a nonexistent RTT request: %d", id);
3801             return;
3802         }
3803         if (id != mPendingRttRequestId) {
3804             Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
3805             return;
3806         }
3807         if (mTransactionalService != null) {
3808             Log.i(this, "handleRttRequestResponse: called on TransactionalService. doing nothing");
3809             return;
3810         }
3811         Log.addEvent(this, LogUtils.Events.RESPOND_TO_RTT_REQUEST, "id=" + id + ", accept="
3812                 + accept);
3813         if (accept) {
3814             createRttStreams();
3815             Log.i(this, "RTT request %d accepted.", id);
3816             mConnectionService.respondToRttRequest(
3817                     this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
3818         } else {
3819             Log.i(this, "RTT request %d rejected.", id);
3820             mConnectionService.respondToRttRequest(this, null, null);
3821         }
3822     }
3823 
isRttCall()3824     public boolean isRttCall() {
3825         return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
3826     }
3827 
wasEverRttCall()3828     public boolean wasEverRttCall() {
3829         return mWasEverRtt;
3830     }
3831 
getCsToInCallRttPipeForCs()3832     public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
3833         return mConnectionServiceToInCallStreams == null ? null
3834                 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
3835     }
3836 
getInCallToCsRttPipeForCs()3837     public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
3838         return mInCallToConnectionServiceStreams == null ? null
3839                 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
3840     }
3841 
getCsToInCallRttPipeForInCall()3842     public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
3843         return mConnectionServiceToInCallStreams == null ? null
3844                 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
3845     }
3846 
getInCallToCsRttPipeForInCall()3847     public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
3848         return mInCallToConnectionServiceStreams == null ? null
3849                 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
3850     }
3851 
getRttMode()3852     public int getRttMode() {
3853         return mRttMode;
3854     }
3855 
3856     /**
3857      * Sets a video call provider for the call.
3858      */
setVideoProvider(IVideoProvider videoProvider)3859     public void setVideoProvider(IVideoProvider videoProvider) {
3860         Log.v(this, "setVideoProvider");
3861 
3862         if (mVideoProviderProxy != null) {
3863             mVideoProviderProxy.clearVideoCallback();
3864             mVideoProviderProxy = null;
3865         }
3866 
3867         if (videoProvider != null ) {
3868             try {
3869                 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
3870                         mCallsManager);
3871             } catch (RemoteException ignored) {
3872                 // Ignore RemoteException.
3873             }
3874         }
3875 
3876         for (Listener l : mListeners) {
3877             l.onVideoCallProviderChanged(Call.this);
3878         }
3879     }
3880 
3881     /**
3882      * @return The {@link Connection.VideoProvider} binder.
3883      */
getVideoProvider()3884     public IVideoProvider getVideoProvider() {
3885         if (mVideoProviderProxy == null) {
3886             return null;
3887         }
3888 
3889         return mVideoProviderProxy.getInterface();
3890     }
3891 
3892     /**
3893      * @return The {@link VideoProviderProxy} for this call.
3894      */
getVideoProviderProxy()3895     public VideoProviderProxy getVideoProviderProxy() {
3896         return mVideoProviderProxy;
3897     }
3898 
3899     /**
3900      * The current video state for the call.
3901      * See {@link VideoProfile} for a list of valid video states.
3902      */
getVideoState()3903     public int getVideoState() {
3904         return mVideoState;
3905     }
3906 
3907     /**
3908      * Returns the video states which were applicable over the duration of a call.
3909      * See {@link VideoProfile} for a list of valid video states.
3910      *
3911      * @return The video states applicable over the duration of the call.
3912      */
getVideoStateHistory()3913     public int getVideoStateHistory() {
3914         return mVideoStateHistory;
3915     }
3916 
3917     /**
3918      * Determines the current video state for the call.
3919      * For an outgoing call determines the desired video state for the call.
3920      * Valid values: see {@link VideoProfile}
3921      *
3922      * @param videoState The video state for the call.
3923      */
setVideoState(int videoState)3924     public void setVideoState(int videoState) {
3925         // If the phone account associated with this call does not support video calling, then we
3926         // will automatically set the video state to audio-only.
3927         if (!isVideoCallingSupportedByPhoneAccount()) {
3928             Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
3929                     VideoProfile.videoStateToString(videoState));
3930             videoState = VideoProfile.STATE_AUDIO_ONLY;
3931         }
3932 
3933         // Track Video State history during the duration of the call.
3934         // Only update the history when the call is active or disconnected. This ensures we do
3935         // not include the video state history when:
3936         // - Call is incoming (but not answered).
3937         // - Call it outgoing (but not answered).
3938         // We include the video state when disconnected to ensure that rejected calls reflect the
3939         // appropriate video state.
3940         // For all other times we add to the video state history, see #setState.
3941         if (isActive() || getState() == CallState.DISCONNECTED) {
3942             mVideoStateHistory = mVideoStateHistory | videoState;
3943         }
3944 
3945         int previousVideoState = mVideoState;
3946         mVideoState = videoState;
3947         if (mVideoState != previousVideoState) {
3948             Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
3949                     VideoProfile.videoStateToString(videoState));
3950             for (Listener l : mListeners) {
3951                 l.onVideoStateChanged(this, previousVideoState, mVideoState);
3952             }
3953         }
3954 
3955         if (VideoProfile.isVideo(videoState)) {
3956             mAnalytics.setCallIsVideo(true);
3957         }
3958     }
3959 
getIsVoipAudioMode()3960     public boolean getIsVoipAudioMode() {
3961         return mIsVoipAudioMode;
3962     }
3963 
setIsVoipAudioMode(boolean audioModeIsVoip)3964     public void setIsVoipAudioMode(boolean audioModeIsVoip) {
3965         if (isSelfManaged() && !audioModeIsVoip) {
3966             Log.i(this,
3967                     "setIsVoipAudioMode: ignoring request to set self-managed audio to "
3968                             + "non-voip mode");
3969             return;
3970         }
3971         if (mIsVoipAudioMode != audioModeIsVoip) {
3972             Log.addEvent(this, LogUtils.Events.SET_VOIP_MODE, audioModeIsVoip ? "Y" : "N");
3973         }
3974         mIsVoipAudioMode = audioModeIsVoip;
3975         for (Listener l : mListeners) {
3976             l.onIsVoipAudioModeChanged(this);
3977         }
3978     }
3979 
getStatusHints()3980     public StatusHints getStatusHints() {
3981         return mStatusHints;
3982     }
3983 
setStatusHints(StatusHints statusHints)3984     public void setStatusHints(StatusHints statusHints) {
3985         mStatusHints = statusHints;
3986         for (Listener l : mListeners) {
3987             l.onStatusHintsChanged(this);
3988         }
3989     }
3990 
isUnknown()3991     public boolean isUnknown() {
3992         return mCallDirection == CALL_DIRECTION_UNKNOWN;
3993     }
3994 
isOutgoing()3995     public boolean isOutgoing() {
3996         return mCallDirection == CALL_DIRECTION_OUTGOING;
3997     }
3998 
3999     /**
4000      * Determines if this call is in a disconnecting state.
4001      *
4002      * @return {@code true} if this call is locally disconnecting.
4003      */
isLocallyDisconnecting()4004     public boolean isLocallyDisconnecting() {
4005         return mIsLocallyDisconnecting;
4006     }
4007 
4008     /**
4009      * Sets whether this call is in a disconnecting state.
4010      *
4011      * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
4012      */
setLocallyDisconnecting(boolean isLocallyDisconnecting)4013     private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
4014         mIsLocallyDisconnecting = isLocallyDisconnecting;
4015     }
4016 
4017     /**
4018      * It's possible that the target phone account isn't set when a user hasn't selected a
4019      * default sim to place a call. Instead of using the user from the target phone account to
4020      * associate the user with a call, we'll use mAssociatedUser instead. For MT calls, we will
4021      * continue to use the target phone account user (as it's always set) and for MO calls, we will
4022      * use the initiating user instead.
4023      *
4024      * @return user handle of user associated with the call.
4025      */
getAssociatedUser()4026     public UserHandle getAssociatedUser() {
4027         return mAssociatedUser;
4028     }
4029 
4030     /**
4031      * Set the user handle of user associated with the call.
4032      * @param associatedUser
4033      */
setAssociatedUser(UserHandle associatedUser)4034     public void setAssociatedUser(UserHandle associatedUser) {
4035         Log.i(this, "Setting associated user for call");
4036         Preconditions.checkNotNull(associatedUser);
4037         mAssociatedUser = associatedUser;
4038     }
4039 
getStateFromConnectionState(int state)4040     static int getStateFromConnectionState(int state) {
4041         switch (state) {
4042             case Connection.STATE_INITIALIZING:
4043                 return CallState.CONNECTING;
4044             case Connection.STATE_ACTIVE:
4045                 return CallState.ACTIVE;
4046             case Connection.STATE_DIALING:
4047                 return CallState.DIALING;
4048             case Connection.STATE_PULLING_CALL:
4049                 return CallState.PULLING;
4050             case Connection.STATE_DISCONNECTED:
4051                 return CallState.DISCONNECTED;
4052             case Connection.STATE_HOLDING:
4053                 return CallState.ON_HOLD;
4054             case Connection.STATE_NEW:
4055                 return CallState.NEW;
4056             case Connection.STATE_RINGING:
4057                 return CallState.RINGING;
4058         }
4059         return CallState.DISCONNECTED;
4060     }
4061 
4062     /**
4063      * Determines if this call is in disconnected state and waiting to be destroyed.
4064      *
4065      * @return {@code true} if this call is disconected.
4066      */
isDisconnected()4067     public boolean isDisconnected() {
4068         return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
4069     }
4070 
4071     /**
4072      * Determines if this call has just been created and has not been configured properly yet.
4073      *
4074      * @return {@code true} if this call is new.
4075      */
isNew()4076     public boolean isNew() {
4077         return getState() == CallState.NEW;
4078     }
4079 
4080     /**
4081      * Sets the call data usage for the call.
4082      *
4083      * @param callDataUsage The new call data usage (in bytes).
4084      */
setCallDataUsage(long callDataUsage)4085     public void setCallDataUsage(long callDataUsage) {
4086         mCallDataUsage = callDataUsage;
4087     }
4088 
4089     /**
4090      * Returns the call data usage for the call.
4091      *
4092      * @return The call data usage (in bytes).
4093      */
getCallDataUsage()4094     public long getCallDataUsage() {
4095         return mCallDataUsage;
4096     }
4097 
setRttMode(int mode)4098     public void setRttMode(int mode) {
4099         mRttMode = mode;
4100         Log.addEvent(this, LogUtils.Events.SET_RRT_MODE, "mode=" + mode);
4101         // TODO: hook this up to CallAudioManager.
4102     }
4103 
4104     /**
4105      * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
4106      * has come back to telecom and was processed.
4107      */
isNewOutgoingCallIntentBroadcastDone()4108     public boolean isNewOutgoingCallIntentBroadcastDone() {
4109         return mIsNewOutgoingCallIntentBroadcastDone;
4110     }
4111 
setNewOutgoingCallIntentBroadcastIsDone()4112     public void setNewOutgoingCallIntentBroadcastIsDone() {
4113         mIsNewOutgoingCallIntentBroadcastDone = true;
4114     }
4115 
4116     /**
4117      * Determines if the call has been held by the remote party.
4118      *
4119      * @return {@code true} if the call is remotely held, {@code false} otherwise.
4120      */
isRemotelyHeld()4121     public boolean isRemotelyHeld() {
4122         return mIsRemotelyHeld;
4123     }
4124 
4125     /**
4126      * Handles Connection events received from a {@link ConnectionService}.
4127      *
4128      * @param event The event.
4129      * @param extras The extras.
4130      */
onConnectionEvent(String event, Bundle extras)4131     public void onConnectionEvent(String event, Bundle extras) {
4132         if (mIsTransactionalCall) {
4133             // send the Event directly to the ICS via the InCallController listener
4134             for (Listener l : mListeners) {
4135                 l.onConnectionEvent(this, event, extras);
4136             }
4137             // Don't run the below block since it applies to Calls that are attached to a
4138             // ConnectionService
4139             return;
4140         }
4141         // Don't log call quality reports; they're quite frequent and will clog the log.
4142         if (!Connection.EVENT_CALL_QUALITY_REPORT.equals(event)) {
4143             Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
4144         }
4145         if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
4146             mIsRemotelyHeld = true;
4147             Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
4148             // Inform listeners of the fact that a call hold tone was received.  This will trigger
4149             // the CallAudioManager to play a tone via the InCallTonePlayer.
4150             for (Listener l : mListeners) {
4151                 l.onHoldToneRequested(this);
4152             }
4153         } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
4154             mIsRemotelyHeld = false;
4155             Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
4156             for (Listener l : mListeners) {
4157                 l.onHoldToneRequested(this);
4158             }
4159         } else if (Connection.EVENT_CALL_HOLD_FAILED.equals(event)) {
4160             for (Listener l : mListeners) {
4161                 l.onCallHoldFailed(this);
4162             }
4163         } else if (Connection.EVENT_CALL_SWITCH_FAILED.equals(event)) {
4164             for (Listener l : mListeners) {
4165                 l.onCallSwitchFailed(this);
4166             }
4167         } else if (Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE.equals(event)
4168                 && extras != null && extras.containsKey(
4169                 Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE)
4170                 && extras.containsKey(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE)) {
4171             // Relay an incoming D2D message to interested listeners; most notably the
4172             // CallDiagnosticService.
4173             int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE);
4174             int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE);
4175             for (Listener l : mListeners) {
4176                 l.onReceivedDeviceToDeviceMessage(this, messageType, messageValue);
4177             }
4178         } else if (Connection.EVENT_CALL_QUALITY_REPORT.equals(event)
4179                 && extras != null && extras.containsKey(Connection.EXTRA_CALL_QUALITY_REPORT)) {
4180             CallQuality callQuality = extras.getParcelable(Connection.EXTRA_CALL_QUALITY_REPORT);
4181             for (Listener l : mListeners) {
4182                 l.onReceivedCallQualityReport(this, callQuality);
4183             }
4184         } else {
4185             if (event.equals(EVENT_DISPLAY_SOS_MESSAGE) && !isEmergencyCall()) {
4186                 Log.w(this, "onConnectionEvent: EVENT_DISPLAY_SOS_MESSAGE is sent "
4187                         + "without an emergency call");
4188                 return;
4189             }
4190 
4191             for (Listener l : mListeners) {
4192                 l.onConnectionEvent(this, event, extras);
4193             }
4194         }
4195     }
4196 
4197     /**
4198      * Notifies interested parties that the handover has completed.
4199      * Notifies:
4200      * 1. {@link InCallController} which communicates this to the
4201      * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}.
4202      * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of
4203      * the successful handover.
4204      */
onHandoverComplete()4205     public void onHandoverComplete() {
4206         Log.i(this, "onHandoverComplete; callId=%s", getId());
4207         if (mConnectionService != null) {
4208             mConnectionService.handoverComplete(this);
4209         }
4210         for (Listener l : mListeners) {
4211             l.onHandoverComplete(this);
4212         }
4213     }
4214 
onHandoverFailed(int handoverError)4215     public void onHandoverFailed(int handoverError) {
4216         Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError);
4217         for (Listener l : mListeners) {
4218             l.onHandoverFailed(this, handoverError);
4219         }
4220     }
4221 
setOriginalConnectionId(String originalConnectionId)4222     public void setOriginalConnectionId(String originalConnectionId) {
4223         mOriginalConnectionId = originalConnectionId;
4224     }
4225 
4226     /**
4227      * For calls added via a ConnectionManager using the
4228      * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
4229      * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
4230      * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
4231      * originally created it.
4232      *
4233      * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
4234      * @return The original connection ID.
4235      */
getOriginalConnectionId()4236     public String getOriginalConnectionId() {
4237         return mOriginalConnectionId;
4238     }
4239 
getConnectionServiceFocusManager()4240     public ConnectionServiceFocusManager getConnectionServiceFocusManager() {
4241         return mCallsManager.getConnectionServiceFocusManager();
4242     }
4243 
4244     /**
4245      * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
4246      * remotely or locally.
4247      *
4248      * @param capabilities The {@link Connection} capabilities for the call.
4249      * @return {@code true} if video is supported, {@code false} otherwise.
4250      */
doesCallSupportVideo(int capabilities)4251     private boolean doesCallSupportVideo(int capabilities) {
4252         return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
4253                 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
4254     }
4255 
4256     /**
4257      * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
4258      *
4259      * @param capabilities The capabilities.
4260      * @return The bitmask with video capabilities removed.
4261      */
removeVideoCapabilities(int capabilities)4262     private int removeVideoCapabilities(int capabilities) {
4263         return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
4264                 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
4265     }
4266 
4267     /**
4268      * Initiates a handover of this {@link Call} to another {@link PhoneAccount}.
4269      * @param handoverToHandle The {@link PhoneAccountHandle} to handover to.
4270      * @param videoState The video state of the call when handed over.
4271      * @param extras Optional extras {@link Bundle} provided by the initiating
4272      *      {@link android.telecom.InCallService}.
4273      */
requestHandover(PhoneAccountHandle handoverToHandle, int videoState, Bundle extras, boolean isLegacy)4274     private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState,
4275                                  Bundle extras, boolean isLegacy) {
4276         for (Listener l : mListeners) {
4277             l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy);
4278         }
4279     }
4280 
getTelephonyManager()4281     private TelephonyManager getTelephonyManager() {
4282         return mContext.getSystemService(TelephonyManager.class);
4283     }
4284 
4285     /**
4286      * Sets whether this {@link Call} is a conference or not.
4287      * @param isConference
4288      */
setConferenceState(boolean isConference)4289     public void setConferenceState(boolean isConference) {
4290         mIsConference = isConference;
4291         Log.addEvent(this, LogUtils.Events.CONF_STATE_CHANGED, "isConference=" + isConference);
4292         // Ultimately CallsManager needs to know so it can update the "add call" state and inform
4293         // the UI to update itself.
4294         for (Listener l : mListeners) {
4295             l.onConferenceStateChanged(this, isConference);
4296         }
4297     }
4298 
4299     /**
4300      * Change the call direction. This is useful if it was not previously defined (for example in
4301      * single caller emulation mode).
4302      * @param callDirection The new direction of this call.
4303      */
4304     // Make sure the callDirection has been mapped to the Call definition correctly!
setCallDirection(int callDirection)4305     public void setCallDirection(int callDirection) {
4306         if (mCallDirection != callDirection) {
4307             Log.addEvent(this, LogUtils.Events.CALL_DIRECTION_CHANGED, "callDirection="
4308                     + callDirection);
4309             mCallDirection = callDirection;
4310             for (Listener l : mListeners) {
4311                 // Update InCallService directly, do not notify CallsManager.
4312                 l.onCallDirectionChanged(this);
4313             }
4314         }
4315     }
4316 
4317     /**
4318      * Sets the video history based on the state and state transitions of the call. Always add the
4319      * current video state to the video state history during a call transition except for the
4320      * transitions DIALING->ACTIVE and RINGING->ANSWERED. In these cases, clear the history. If a
4321      * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record
4322      * the history as an audio call.
4323      */
updateVideoHistoryViaState(int oldState, int newState)4324     private void updateVideoHistoryViaState(int oldState, int newState) {
4325         if ((oldState == CallState.DIALING && newState == CallState.ACTIVE)
4326                 || (oldState == CallState.RINGING && newState == CallState.ANSWERED)) {
4327             mVideoStateHistory = mVideoState;
4328         }
4329 
4330         mVideoStateHistory |= mVideoState;
4331     }
4332 
4333     /**
4334      * Returns whether or not high definition audio was used.
4335      *
4336      * @return true if high definition audio was used during this call.
4337      */
wasHighDefAudio()4338     boolean wasHighDefAudio() {
4339         return mWasHighDefAudio;
4340     }
4341 
4342     /**
4343      * Returns whether or not Wifi call was used.
4344      *
4345      * @return true if wifi call was used during this call.
4346      */
wasWifi()4347     boolean wasWifi() {
4348         return mWasWifi;
4349     }
4350 
setIsUsingCallFiltering(boolean isUsingCallFiltering)4351     public void setIsUsingCallFiltering(boolean isUsingCallFiltering) {
4352         mIsUsingCallFiltering = isUsingCallFiltering;
4353     }
4354 
isUsingCallFiltering()4355     public boolean isUsingCallFiltering() {
4356         return mIsUsingCallFiltering;
4357     }
4358 
4359     /**
4360      * Returns whether or not Volte call was used.
4361      *
4362      * @return true if Volte call was used during this call.
4363      */
wasVolte()4364     public boolean wasVolte() {
4365         return mWasVolte;
4366     }
4367 
4368     /**
4369      * In some cases, we need to know if this call has ever gone active (for example, the case
4370      * when the call was put into the {@link CallState#AUDIO_PROCESSING} state after being active)
4371      * for call logging purposes.
4372      *
4373      * @return {@code true} if this call has gone active before (even if it isn't now), false if it
4374      * has never gone active.
4375      */
hasGoneActiveBefore()4376     public boolean hasGoneActiveBefore() {
4377         return mHasGoneActiveBefore;
4378     }
4379 
4380     /**
4381      * When upgrading a call to video via
4382      * {@link VideoProviderProxy#onSendSessionModifyRequest(VideoProfile, VideoProfile)}, if the
4383      * upgrade is from audio to video, potentially auto-engage the speakerphone.
4384      * @param newVideoState The proposed new video state for the call.
4385      */
maybeEnableSpeakerForVideoUpgrade(@ideoProfile.VideoState int newVideoState)4386     public void maybeEnableSpeakerForVideoUpgrade(@VideoProfile.VideoState int newVideoState) {
4387         if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) {
4388             Log.i(this, "maybeEnableSpeakerForVideoCall; callId=%s, auto-enable speaker for call"
4389                             + " upgraded to video.");
4390             mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null);
4391         }
4392     }
4393 
4394     /**
4395      * Sends a device to device message to the other part of the call.
4396      * @param message the message type to send.
4397      * @param value the value for the message.
4398      */
sendDeviceToDeviceMessage(@allDiagnostics.MessageType int message, int value)4399     public void sendDeviceToDeviceMessage(@CallDiagnostics.MessageType int message, int value) {
4400         Log.i(this, "sendDeviceToDeviceMessage; callId=%s, msg=%d/%d", getId(), message, value);
4401         Bundle extras = new Bundle();
4402         extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, message);
4403         extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, value);
4404         // Send to the connection service.
4405         sendCallEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras);
4406     }
4407 
4408     /**
4409      * Signals to the Dialer app to start displaying a diagnostic message.
4410      * @param messageId a unique ID for the message to display.
4411      * @param message the message to display.
4412      */
displayDiagnosticMessage(int messageId, @NonNull CharSequence message)4413     public void displayDiagnosticMessage(int messageId, @NonNull CharSequence message) {
4414         Bundle extras = new Bundle();
4415         extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
4416         extras.putCharSequence(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE, message);
4417         // Send to the dialer.
4418         onConnectionEvent(android.telecom.Call.EVENT_DISPLAY_DIAGNOSTIC_MESSAGE, extras);
4419     }
4420 
4421     /**
4422      * Signals to the Dialer app to stop displaying a diagnostic message.
4423      * @param messageId a unique ID for the message to clear.
4424      */
clearDiagnosticMessage(int messageId)4425     public void clearDiagnosticMessage(int messageId) {
4426         Bundle extras = new Bundle();
4427         extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId);
4428         // Send to the dialer.
4429         onConnectionEvent(android.telecom.Call.EVENT_CLEAR_DIAGNOSTIC_MESSAGE, extras);
4430     }
4431 
4432     /**
4433      * Remaps the call direction as indicated by an {@link android.telecom.Call.Details} direction
4434      * constant to the constants (e.g. {@link #CALL_DIRECTION_INCOMING}) used in this call class.
4435      * @param direction The android.telecom.Call direction.
4436      * @return The direction using the constants in this class.
4437      */
getRemappedCallDirection( @ndroid.telecom.Call.Details.CallDirection int direction)4438     public static int getRemappedCallDirection(
4439             @android.telecom.Call.Details.CallDirection int direction) {
4440         switch(direction) {
4441             case android.telecom.Call.Details.DIRECTION_INCOMING:
4442                 return CALL_DIRECTION_INCOMING;
4443             case android.telecom.Call.Details.DIRECTION_OUTGOING:
4444                 return CALL_DIRECTION_OUTGOING;
4445             case android.telecom.Call.Details.DIRECTION_UNKNOWN:
4446                 return CALL_DIRECTION_UNDEFINED;
4447         }
4448         return CALL_DIRECTION_UNDEFINED;
4449     }
4450 
4451     /**
4452      * Set the package name of the {@link android.telecom.CallScreeningService} which should be sent
4453      * the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a call.
4454      * @param packageName post call screen service package name.
4455      */
setPostCallPackageName(String packageName)4456     public void setPostCallPackageName(String packageName) {
4457         mPostCallPackageName = packageName;
4458     }
4459 
4460     /**
4461      * Return the package name of the {@link android.telecom.CallScreeningService} which should be
4462      * sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a
4463      * call.
4464      * @return post call screen service package name.
4465      */
getPostCallPackageName()4466     public String getPostCallPackageName() {
4467         return mPostCallPackageName;
4468     }
4469 
getMissedReason()4470     public long getMissedReason() {
4471         return mMissedReason;
4472     }
4473 
setMissedReason(long missedReason)4474     public void setMissedReason(long missedReason) {
4475         mMissedReason = missedReason;
4476     }
4477 
setUserMissed(long code)4478     public void setUserMissed(long code) {
4479         mMissedReason |= code;
4480     }
4481 
getStartRingTime()4482     public long getStartRingTime() {
4483         return mStartRingTime;
4484     }
4485 
setStartRingTime()4486     public void setStartRingTime() {
4487         mStartRingTime = mClockProxy.elapsedRealtime();
4488     }
4489 
getCallScreeningAppName()4490     public CharSequence getCallScreeningAppName() {
4491         return mCallScreeningAppName;
4492     }
4493 
setCallScreeningAppName(CharSequence callScreeningAppName)4494     public void setCallScreeningAppName(CharSequence callScreeningAppName) {
4495         mCallScreeningAppName = callScreeningAppName;
4496     }
4497 
getCallScreeningComponentName()4498     public String getCallScreeningComponentName() {
4499         return mCallScreeningComponentName;
4500     }
4501 
setCallScreeningComponentName(String callScreeningComponentName)4502     public void setCallScreeningComponentName(String callScreeningComponentName) {
4503         mCallScreeningComponentName = callScreeningComponentName;
4504     }
4505 
setStartFailCause(CallFailureCause cause)4506     public void setStartFailCause(CallFailureCause cause) {
4507         mCallStateChangedAtomWriter.setStartFailCause(cause);
4508     }
4509 
increaseHeldByThisCallCount()4510     public void increaseHeldByThisCallCount() {
4511         mCallStateChangedAtomWriter.increaseHeldCallCount();
4512     }
4513 
maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi)4514     public void maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi) {
4515         if (mTransactionalService != null) {
4516             Log.i(this,
4517                     "maybeOnInCallServiceTrackingChanged: called on TransactionalService");
4518             return;
4519         }
4520         if (mConnectionService == null) {
4521             Log.w(this, "maybeOnInCallServiceTrackingChanged() request on a call"
4522                     + " without a connection service.");
4523         } else {
4524             if (hasUi) {
4525                 mConnectionService.onUsingAlternativeUi(this, isTracking);
4526             } else if (isTracking) {
4527                 mConnectionService.onTrackedByNonUiService(this, isTracking);
4528             }
4529         }
4530     }
4531 
4532     /**
4533      * @return {@code true} when this call originated from a SIM-based {@link PhoneAccount}.
4534      * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION}
4535      * set.
4536      */
isSimCall()4537     public boolean isSimCall() {
4538         return mIsSimCall;
4539     }
4540 
4541     /**
4542      * Sets whether this is a sim call or not.
4543      * @param isSimCall {@code true} if this is a SIM call, {@code false} otherwise.
4544      */
setIsSimCall(boolean isSimCall)4545     public void setIsSimCall(boolean isSimCall) {
4546         mIsSimCall = isSimCall;
4547     }
4548 
4549     /**
4550      * Initializes a disconnect future which is used to chain up pending operations which take
4551      * place when the {@link CallDiagnosticService} returns the result of the
4552      * {@link CallDiagnostics#onCallDisconnected(int, int)} or
4553      * {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} invocation via
4554      * {@link CallDiagnosticServiceAdapter}.  If no {@link CallDiagnosticService} is in use, we
4555      * would not try to make a disconnect future.
4556      * @param timeoutMillis Timeout we use for waiting for the response.
4557      * @return the {@link CompletableFuture}.
4558      */
initializeDisconnectFuture(long timeoutMillis)4559     public CompletableFuture<Boolean> initializeDisconnectFuture(long timeoutMillis) {
4560         if (mDisconnectFuture == null) {
4561             mDisconnectFuture = new CompletableFuture<Boolean>()
4562                     .completeOnTimeout(false, timeoutMillis, TimeUnit.MILLISECONDS);
4563             // After all the chained stuff we will report where the CDS timed out.
4564             mDisconnectFuture.thenRunAsync(() -> {
4565                 if (!mReceivedCallDiagnosticPostCallResponse) {
4566                     Log.addEvent(this, LogUtils.Events.CALL_DIAGNOSTIC_SERVICE_TIMEOUT);
4567                 }
4568                 // Clear the future as a final step.
4569                 mDisconnectFuture = null;
4570                 },
4571                 new LoggedHandlerExecutor(mHandler, "C.iDF", mLock))
4572                     .exceptionally((throwable) -> {
4573                         Log.e(this, throwable, "Error while executing disconnect future");
4574                         return null;
4575                     });
4576         }
4577         return mDisconnectFuture;
4578     }
4579 
4580     /**
4581      * @return the disconnect future, if initialized.  Used for chaining operations after creation.
4582      */
getDisconnectFuture()4583     public CompletableFuture<Boolean> getDisconnectFuture() {
4584         return mDisconnectFuture;
4585     }
4586 
4587     /**
4588      * @return {@code true} if disconnection and removal is handled via a future, or {@code false}
4589      * if this is handled immediately.
4590      */
isDisconnectHandledViaFuture()4591     public boolean isDisconnectHandledViaFuture() {
4592         return mDisconnectFuture != null;
4593     }
4594 
4595     /**
4596      * Perform any cleanup on this call as a result of a {@link TelecomServiceImpl}
4597      * {@code cleanupStuckCalls} request.
4598      */
cleanup()4599     public void cleanup() {
4600         if (mDisconnectFuture != null) {
4601             mDisconnectFuture.complete(false);
4602             mDisconnectFuture = null;
4603         }
4604     }
4605 
4606     /**
4607      * @return {@code true} if the connection has been created by the underlying
4608      * {@link ConnectionService}, {@code false} otherwise.
4609      */
isCreateConnectionComplete()4610     public boolean isCreateConnectionComplete() {
4611         return mIsCreateConnectionComplete;
4612     }
4613 
4614     @VisibleForTesting
setIsCreateConnectionComplete(boolean isCreateConnectionComplete)4615     public void setIsCreateConnectionComplete(boolean isCreateConnectionComplete) {
4616         mIsCreateConnectionComplete = isCreateConnectionComplete;
4617     }
4618 
isStreaming()4619     public boolean isStreaming() {
4620         synchronized (mLock) {
4621             return mIsStreaming;
4622         }
4623     }
4624 
startStreaming()4625     public void startStreaming() {
4626         if (!mIsTransactionalCall) {
4627             throw new UnsupportedOperationException(
4628                     "Can't streaming call created by non voip apps");
4629         }
4630         Log.addEvent(this, LogUtils.Events.START_STREAMING);
4631         synchronized (mLock) {
4632             if (mIsStreaming) {
4633                 // ignore
4634                 return;
4635             }
4636 
4637             mIsStreaming = true;
4638             for (Listener listener : mListeners) {
4639                 listener.onCallStreamingStateChanged(this, true /** isStreaming */);
4640             }
4641         }
4642     }
4643 
stopStreaming()4644     public void stopStreaming() {
4645         synchronized (mLock) {
4646             if (!mIsStreaming) {
4647                 // ignore
4648                 return;
4649             }
4650             Log.addEvent(this, LogUtils.Events.STOP_STREAMING);
4651             mIsStreaming = false;
4652             for (Listener listener : mListeners) {
4653                 listener.onCallStreamingStateChanged(this, false /** isStreaming */);
4654             }
4655         }
4656     }
4657 }
4658