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