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