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