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