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