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