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