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