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