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