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