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        // We should not keep these bitmaps around because the Call objects may be held for logging
582        // purposes.
583        // TODO: Make a container object that only stores the information we care about for Logging.
584        if (mCallerInfo != null) {
585            mCallerInfo.cachedPhotoIcon = null;
586            mCallerInfo.cachedPhoto = null;
587        }
588        Log.addEvent(this, LogUtils.Events.DESTROYED);
589    }
590
591    /** {@inheritDoc} */
592    @Override
593    public String toString() {
594        String component = null;
595        if (mConnectionService != null && mConnectionService.getComponentName() != null) {
596            component = mConnectionService.getComponentName().flattenToShortString();
597        }
598
599        return String.format(Locale.US, "[%s, %s, %s, %s, %s, childs(%d), has_parent(%b), %s, %s]",
600                mId,
601                CallState.toString(mState),
602                component,
603                Log.piiHandle(mHandle),
604                getVideoStateDescription(getVideoState()),
605                getChildCalls().size(),
606                getParentCall() != null,
607                Connection.capabilitiesToString(getConnectionCapabilities()),
608                Connection.propertiesToString(getConnectionProperties()));
609    }
610
611    @Override
612    public String getDescription() {
613        StringBuilder s = new StringBuilder();
614        if (isSelfManaged()) {
615            s.append("SelfMgd Call");
616        } else if (isExternalCall()) {
617            s.append("External Call");
618        } else {
619            s.append("Call");
620        }
621        s.append(getId());
622        s.append(" [");
623        s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis())));
624        s.append("]");
625        s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)");
626        s.append("\n\tVia PhoneAccount: ");
627        PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount();
628        if (targetPhoneAccountHandle != null) {
629            s.append(targetPhoneAccountHandle);
630            s.append(" (");
631            s.append(getTargetPhoneAccountLabel());
632            s.append(")");
633        } else {
634            s.append("not set");
635        }
636
637        s.append("\n\tTo address: ");
638        s.append(Log.piiHandle(getHandle()));
639        s.append("\n");
640        return s.toString();
641    }
642
643    /**
644     * Builds a debug-friendly description string for a video state.
645     * <p>
646     * A = audio active, T = video transmission active, R = video reception active, P = video
647     * paused.
648     *
649     * @param videoState The video state.
650     * @return A string indicating which bits are set in the video state.
651     */
652    private String getVideoStateDescription(int videoState) {
653        StringBuilder sb = new StringBuilder();
654        sb.append("A");
655
656        if (VideoProfile.isTransmissionEnabled(videoState)) {
657            sb.append("T");
658        }
659
660        if (VideoProfile.isReceptionEnabled(videoState)) {
661            sb.append("R");
662        }
663
664        if (VideoProfile.isPaused(videoState)) {
665            sb.append("P");
666        }
667
668        return sb.toString();
669    }
670
671    @VisibleForTesting
672    public int getState() {
673        return mState;
674    }
675
676    private boolean shouldContinueProcessingAfterDisconnect() {
677        // Stop processing once the call is active.
678        if (!CreateConnectionTimeout.isCallBeingPlaced(this)) {
679            return false;
680        }
681
682        // Only Redial a Call in the case of it being an Emergency Call.
683        if(!isEmergencyCall()) {
684            return false;
685        }
686
687        // Make sure that there are additional connection services to process.
688        if (mCreateConnectionProcessor == null
689            || !mCreateConnectionProcessor.isProcessingComplete()
690            || !mCreateConnectionProcessor.hasMorePhoneAccounts()) {
691            return false;
692        }
693
694        if (mDisconnectCause == null) {
695            return false;
696        }
697
698        // Continue processing if the current attempt failed or timed out.
699        return mDisconnectCause.getCode() == DisconnectCause.ERROR ||
700            mCreateConnectionProcessor.isCallTimedOut();
701    }
702
703    /**
704     * Returns the unique ID for this call as it exists in Telecom.
705     * @return The call ID.
706     */
707    public String getId() {
708        return mId;
709    }
710
711    /**
712     * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that
713     * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor.
714     * @return The call ID with an appended attempt id.
715     */
716    public String getConnectionId() {
717        if(mCreateConnectionProcessor != null) {
718            mConnectionId = mId + "_" +
719                    String.valueOf(mCreateConnectionProcessor.getConnectionAttempt());
720            return mConnectionId;
721        } else {
722            return mConnectionId;
723        }
724    }
725
726    /**
727     * Sets the call state. Although there exists the notion of appropriate state transitions
728     * (see {@link CallState}), in practice those expectations break down when cellular systems
729     * misbehave and they do this very often. The result is that we do not enforce state transitions
730     * and instead keep the code resilient to unexpected state changes.
731     */
732    public void setState(int newState, String tag) {
733        if (mState != newState) {
734            Log.v(this, "setState %s -> %s", mState, newState);
735
736            if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) {
737                Log.w(this, "continuing processing disconnected call with another service");
738                mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause);
739                return;
740            }
741
742            mState = newState;
743            maybeLoadCannedSmsResponses();
744
745            if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) {
746                if (mConnectTimeMillis == 0) {
747                    // We check to see if mConnectTime is already set to prevent the
748                    // call from resetting active time when it goes in and out of
749                    // ACTIVE/ON_HOLD
750                    mConnectTimeMillis = System.currentTimeMillis();
751                    mAnalytics.setCallStartTime(mConnectTimeMillis);
752                }
753
754                // Video state changes are normally tracked against history when a call is active.
755                // When the call goes active we need to be sure we track the history in case the
756                // state never changes during the duration of the call -- we want to ensure we
757                // always know the state at the start of the call.
758                mVideoStateHistory = mVideoStateHistory | mVideoState;
759
760                // We're clearly not disconnected, so reset the disconnected time.
761                mDisconnectTimeMillis = 0;
762            } else if (mState == CallState.DISCONNECTED) {
763                mDisconnectTimeMillis = System.currentTimeMillis();
764                mAnalytics.setCallEndTime(mDisconnectTimeMillis);
765                setLocallyDisconnecting(false);
766                fixParentAfterDisconnect();
767            }
768            if (mState == CallState.DISCONNECTED &&
769                    mDisconnectCause.getCode() == DisconnectCause.MISSED) {
770                // Ensure when an incoming call is missed that the video state history is updated.
771                mVideoStateHistory |= mVideoState;
772            }
773
774            // Log the state transition event
775            String event = null;
776            Object data = null;
777            switch (newState) {
778                case CallState.ACTIVE:
779                    event = LogUtils.Events.SET_ACTIVE;
780                    break;
781                case CallState.CONNECTING:
782                    event = LogUtils.Events.SET_CONNECTING;
783                    break;
784                case CallState.DIALING:
785                    event = LogUtils.Events.SET_DIALING;
786                    break;
787                case CallState.PULLING:
788                    event = LogUtils.Events.SET_PULLING;
789                    break;
790                case CallState.DISCONNECTED:
791                    event = LogUtils.Events.SET_DISCONNECTED;
792                    data = getDisconnectCause();
793                    break;
794                case CallState.DISCONNECTING:
795                    event = LogUtils.Events.SET_DISCONNECTING;
796                    break;
797                case CallState.ON_HOLD:
798                    event = LogUtils.Events.SET_HOLD;
799                    break;
800                case CallState.SELECT_PHONE_ACCOUNT:
801                    event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT;
802                    break;
803                case CallState.RINGING:
804                    event = LogUtils.Events.SET_RINGING;
805                    break;
806            }
807            if (event != null) {
808                // The string data should be just the tag.
809                String stringData = tag;
810                if (data != null) {
811                    // If data exists, add it to tag.  If no tag, just use data.toString().
812                    stringData = stringData == null ? data.toString() : stringData + "> " + data;
813                }
814                Log.addEvent(this, event, stringData);
815            }
816        }
817    }
818
819    void setRingbackRequested(boolean ringbackRequested) {
820        mRingbackRequested = ringbackRequested;
821        for (Listener l : mListeners) {
822            l.onRingbackRequested(this, mRingbackRequested);
823        }
824    }
825
826    boolean isRingbackRequested() {
827        return mRingbackRequested;
828    }
829
830    @VisibleForTesting
831    public boolean isConference() {
832        return mIsConference;
833    }
834
835    public Uri getHandle() {
836        return mHandle;
837    }
838
839    public String getPostDialDigits() {
840        return mPostDialDigits;
841    }
842
843    public String getViaNumber() {
844        return mViaNumber;
845    }
846
847    public void setViaNumber(String viaNumber) {
848        // If at any point the via number is not empty throughout the call, save that via number.
849        if (!TextUtils.isEmpty(viaNumber)) {
850            mViaNumber = viaNumber;
851        }
852    }
853
854    int getHandlePresentation() {
855        return mHandlePresentation;
856    }
857
858
859    void setHandle(Uri handle) {
860        setHandle(handle, TelecomManager.PRESENTATION_ALLOWED);
861    }
862
863    public void setHandle(Uri handle, int presentation) {
864        if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) {
865            mHandlePresentation = presentation;
866            if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED ||
867                    mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) {
868                mHandle = null;
869            } else {
870                mHandle = handle;
871                if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme())
872                        && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) {
873                    // If the number is actually empty, set it to null, unless this is a
874                    // SCHEME_VOICEMAIL uri which always has an empty number.
875                    mHandle = null;
876                }
877            }
878
879            // Let's not allow resetting of the emergency flag. Once a call becomes an emergency
880            // call, it will remain so for the rest of it's lifetime.
881            if (!mIsEmergencyCall) {
882                mIsEmergencyCall = mHandle != null &&
883                        mPhoneNumberUtilsAdapter.isLocalEmergencyNumber(mContext,
884                                mHandle.getSchemeSpecificPart());
885            }
886            startCallerInfoLookup();
887            for (Listener l : mListeners) {
888                l.onHandleChanged(this);
889            }
890        }
891    }
892
893    public String getCallerDisplayName() {
894        return mCallerDisplayName;
895    }
896
897    public int getCallerDisplayNamePresentation() {
898        return mCallerDisplayNamePresentation;
899    }
900
901    void setCallerDisplayName(String callerDisplayName, int presentation) {
902        if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) ||
903                presentation != mCallerDisplayNamePresentation) {
904            mCallerDisplayName = callerDisplayName;
905            mCallerDisplayNamePresentation = presentation;
906            for (Listener l : mListeners) {
907                l.onCallerDisplayNameChanged(this);
908            }
909        }
910    }
911
912    public String getName() {
913        return mCallerInfo == null ? null : mCallerInfo.name;
914    }
915
916    public String getPhoneNumber() {
917        return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
918    }
919
920    public Bitmap getPhotoIcon() {
921        return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon;
922    }
923
924    public Drawable getPhoto() {
925        return mCallerInfo == null ? null : mCallerInfo.cachedPhoto;
926    }
927
928    /**
929     * @param disconnectCause The reason for the disconnection, represented by
930     *         {@link android.telecom.DisconnectCause}.
931     */
932    public void setDisconnectCause(DisconnectCause disconnectCause) {
933        // TODO: Consider combining this method with a setDisconnected() method that is totally
934        // separate from setState.
935        mAnalytics.setCallDisconnectCause(disconnectCause);
936        mDisconnectCause = disconnectCause;
937    }
938
939    public DisconnectCause getDisconnectCause() {
940        return mDisconnectCause;
941    }
942
943    @VisibleForTesting
944    public boolean isEmergencyCall() {
945        return mIsEmergencyCall;
946    }
947
948    /**
949     * @return The original handle this call is associated with. In-call services should use this
950     * handle when indicating in their UI the handle that is being called.
951     */
952    public Uri getOriginalHandle() {
953        if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) {
954            return mGatewayInfo.getOriginalAddress();
955        }
956        return getHandle();
957    }
958
959    @VisibleForTesting
960    public GatewayInfo getGatewayInfo() {
961        return mGatewayInfo;
962    }
963
964    void setGatewayInfo(GatewayInfo gatewayInfo) {
965        mGatewayInfo = gatewayInfo;
966    }
967
968    @VisibleForTesting
969    public PhoneAccountHandle getConnectionManagerPhoneAccount() {
970        return mConnectionManagerPhoneAccountHandle;
971    }
972
973    @VisibleForTesting
974    public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) {
975        if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) {
976            mConnectionManagerPhoneAccountHandle = accountHandle;
977            for (Listener l : mListeners) {
978                l.onConnectionManagerPhoneAccountChanged(this);
979            }
980        }
981
982    }
983
984    @VisibleForTesting
985    public PhoneAccountHandle getTargetPhoneAccount() {
986        return mTargetPhoneAccountHandle;
987    }
988
989    @VisibleForTesting
990    public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) {
991        if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) {
992            mTargetPhoneAccountHandle = accountHandle;
993            for (Listener l : mListeners) {
994                l.onTargetPhoneAccountChanged(this);
995            }
996            configureIsWorkCall();
997        }
998        checkIfVideoCapable();
999    }
1000
1001    public CharSequence getTargetPhoneAccountLabel() {
1002        if (getTargetPhoneAccount() == null) {
1003            return null;
1004        }
1005        PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar()
1006                .getPhoneAccountUnchecked(getTargetPhoneAccount());
1007
1008        if (phoneAccount == null) {
1009            return null;
1010        }
1011
1012        return phoneAccount.getLabel();
1013    }
1014
1015    @VisibleForTesting
1016    public boolean isIncoming() {
1017        return mCallDirection == CALL_DIRECTION_INCOMING;
1018    }
1019
1020    public boolean isExternalCall() {
1021        return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) ==
1022                Connection.PROPERTY_IS_EXTERNAL_CALL;
1023    }
1024
1025    public boolean isWorkCall() {
1026        return mIsWorkCall;
1027    }
1028
1029    public boolean isVideoCallingSupported() {
1030        return mIsVideoCallingSupported;
1031    }
1032
1033    public boolean isSelfManaged() {
1034        return mIsSelfManaged;
1035    }
1036
1037    public void setIsSelfManaged(boolean isSelfManaged) {
1038        mIsSelfManaged = isSelfManaged;
1039
1040        // Connection properties will add/remove the PROPERTY_SELF_MANAGED.
1041        setConnectionProperties(getConnectionProperties());
1042    }
1043
1044    private void configureIsWorkCall() {
1045        PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1046        boolean isWorkCall = false;
1047        PhoneAccount phoneAccount =
1048                phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1049        if (phoneAccount != null) {
1050            final UserHandle userHandle;
1051            if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
1052                userHandle = mInitiatingUser;
1053            } else {
1054                userHandle = mTargetPhoneAccountHandle.getUserHandle();
1055            }
1056            if (userHandle != null) {
1057                isWorkCall = UserUtil.isManagedProfile(mContext, userHandle);
1058            }
1059        }
1060        mIsWorkCall = isWorkCall;
1061    }
1062
1063    /**
1064     * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount}
1065     * capability and ensures that the video state is updated if the phone account does not support
1066     * video calling.
1067     */
1068    private void checkIfVideoCapable() {
1069        PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar();
1070        if (mTargetPhoneAccountHandle == null) {
1071            // If no target phone account handle is specified, assume we can potentially perform a
1072            // video call; once the phone account is set, we can confirm that it is video capable.
1073            mIsVideoCallingSupported = true;
1074            Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable.");
1075            return;
1076        }
1077        PhoneAccount phoneAccount =
1078                phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle);
1079        mIsVideoCallingSupported = phoneAccount != null && phoneAccount.hasCapabilities(
1080                    PhoneAccount.CAPABILITY_VIDEO_CALLING);
1081
1082        if (!mIsVideoCallingSupported && VideoProfile.isVideo(getVideoState())) {
1083            // The PhoneAccount for the Call was set to one which does not support video calling,
1084            // and the current call is configured to be a video call; downgrade to audio-only.
1085            setVideoState(VideoProfile.STATE_AUDIO_ONLY);
1086            Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video.");
1087        }
1088    }
1089
1090    boolean shouldAttachToExistingConnection() {
1091        return mShouldAttachToExistingConnection;
1092    }
1093
1094    /**
1095     * @return The "age" of this call object in milliseconds, which typically also represents the
1096     *     period since this call was added to the set pending outgoing calls, see
1097     *     mCreationTimeMillis.
1098     */
1099    @VisibleForTesting
1100    public long getAgeMillis() {
1101        if (mState == CallState.DISCONNECTED &&
1102                (mDisconnectCause.getCode() == DisconnectCause.REJECTED ||
1103                 mDisconnectCause.getCode() == DisconnectCause.MISSED)) {
1104            // Rejected and missed calls have no age. They're immortal!!
1105            return 0;
1106        } else if (mConnectTimeMillis == 0) {
1107            // Age is measured in the amount of time the call was active. A zero connect time
1108            // indicates that we never went active, so return 0 for the age.
1109            return 0;
1110        } else if (mDisconnectTimeMillis == 0) {
1111            // We connected, but have not yet disconnected
1112            return System.currentTimeMillis() - mConnectTimeMillis;
1113        }
1114
1115        return mDisconnectTimeMillis - mConnectTimeMillis;
1116    }
1117
1118    /**
1119     * @return The time when this call object was created and added to the set of pending outgoing
1120     *     calls.
1121     */
1122    public long getCreationTimeMillis() {
1123        return mCreationTimeMillis;
1124    }
1125
1126    public void setCreationTimeMillis(long time) {
1127        mCreationTimeMillis = time;
1128    }
1129
1130    long getConnectTimeMillis() {
1131        return mConnectTimeMillis;
1132    }
1133
1134    int getConnectionCapabilities() {
1135        return mConnectionCapabilities;
1136    }
1137
1138    int getConnectionProperties() {
1139        return mConnectionProperties;
1140    }
1141
1142    void setConnectionCapabilities(int connectionCapabilities) {
1143        setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */);
1144    }
1145
1146    void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) {
1147        Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString(
1148                connectionCapabilities));
1149        if (forceUpdate || mConnectionCapabilities != connectionCapabilities) {
1150            // If the phone account does not support video calling, and the connection capabilities
1151            // passed in indicate that the call supports video, remove those video capabilities.
1152            if (!isVideoCallingSupported() && doesCallSupportVideo(connectionCapabilities)) {
1153                Log.w(this, "setConnectionCapabilities: attempt to set connection as video " +
1154                        "capable when not supported by the phone account.");
1155                connectionCapabilities = removeVideoCapabilities(connectionCapabilities);
1156            }
1157
1158            int previousCapabilities = mConnectionCapabilities;
1159            mConnectionCapabilities = connectionCapabilities;
1160            for (Listener l : mListeners) {
1161                l.onConnectionCapabilitiesChanged(this);
1162            }
1163
1164            int xorCaps = previousCapabilities ^ mConnectionCapabilities;
1165            Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE,
1166                    "Current: [%s], Removed [%s], Added [%s]",
1167                    Connection.capabilitiesToStringShort(mConnectionCapabilities),
1168                    Connection.capabilitiesToStringShort(previousCapabilities & xorCaps),
1169                    Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps));
1170        }
1171    }
1172
1173    void setConnectionProperties(int connectionProperties) {
1174        Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString(
1175                connectionProperties));
1176
1177        // Ensure the ConnectionService can't change the state of the self-managed property.
1178        if (isSelfManaged()) {
1179            connectionProperties |= Connection.PROPERTY_SELF_MANAGED;
1180        } else {
1181            connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED;
1182        }
1183
1184        int changedProperties = mConnectionProperties ^ connectionProperties;
1185
1186        if (changedProperties != 0) {
1187            int previousProperties = mConnectionProperties;
1188            mConnectionProperties = connectionProperties;
1189            setRttStreams((mConnectionProperties & Connection.PROPERTY_IS_RTT) ==
1190                    Connection.PROPERTY_IS_RTT);
1191            boolean didRttChange =
1192                    (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
1193            for (Listener l : mListeners) {
1194                l.onConnectionPropertiesChanged(this, didRttChange);
1195            }
1196
1197            boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1198                    == Connection.PROPERTY_IS_EXTERNAL_CALL;
1199            boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL)
1200                    == Connection.PROPERTY_IS_EXTERNAL_CALL;
1201            if (wasExternal != isExternal) {
1202                Log.v(this, "setConnectionProperties: external call changed isExternal = %b",
1203                        isExternal);
1204                Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal);
1205                for (Listener l : mListeners) {
1206                    l.onExternalCallChanged(this, isExternal);
1207                }
1208            }
1209
1210            mAnalytics.addCallProperties(mConnectionProperties);
1211
1212            int xorProps = previousProperties ^ mConnectionProperties;
1213            Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE,
1214                    "Current: [%s], Removed [%s], Added [%s]",
1215                    Connection.propertiesToStringShort(mConnectionProperties),
1216                    Connection.propertiesToStringShort(previousProperties & xorProps),
1217                    Connection.propertiesToStringShort(mConnectionProperties & xorProps));
1218        }
1219    }
1220
1221    public int getSupportedAudioRoutes() {
1222        return mSupportedAudioRoutes;
1223    }
1224
1225    void setSupportedAudioRoutes(int audioRoutes) {
1226        if (mSupportedAudioRoutes != audioRoutes) {
1227            mSupportedAudioRoutes = audioRoutes;
1228        }
1229    }
1230
1231    @VisibleForTesting
1232    public Call getParentCall() {
1233        return mParentCall;
1234    }
1235
1236    @VisibleForTesting
1237    public List<Call> getChildCalls() {
1238        return mChildCalls;
1239    }
1240
1241    @VisibleForTesting
1242    public boolean wasConferencePreviouslyMerged() {
1243        return mWasConferencePreviouslyMerged;
1244    }
1245
1246    public boolean isDisconnectingChildCall() {
1247        return mIsDisconnectingChildCall;
1248    }
1249
1250    /**
1251     * Sets whether this call is a child call.
1252     */
1253    private void maybeSetCallAsDisconnectingChild() {
1254        if (mParentCall != null) {
1255            mIsDisconnectingChildCall = true;
1256        }
1257    }
1258
1259    @VisibleForTesting
1260    public Call getConferenceLevelActiveCall() {
1261        return mConferenceLevelActiveCall;
1262    }
1263
1264    @VisibleForTesting
1265    public ConnectionServiceWrapper getConnectionService() {
1266        return mConnectionService;
1267    }
1268
1269    /**
1270     * Retrieves the {@link Context} for the call.
1271     *
1272     * @return The {@link Context}.
1273     */
1274    public Context getContext() {
1275        return mContext;
1276    }
1277
1278    @VisibleForTesting
1279    public void setConnectionService(ConnectionServiceWrapper service) {
1280        Preconditions.checkNotNull(service);
1281
1282        clearConnectionService();
1283
1284        service.incrementAssociatedCallCount();
1285        mConnectionService = service;
1286        mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1287        mConnectionService.addCall(this);
1288    }
1289
1290    /**
1291     * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call.
1292     * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the
1293     * ConnectionService is NOT unbound if the call count hits zero.
1294     * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and
1295     * {@link Conference} additions via a ConnectionManager.
1296     * The original {@link android.telecom.ConnectionService} will directly add external calls and
1297     * conferences to Telecom as well as the ConnectionManager, which will add to Telecom.  In these
1298     * cases since its first added to via the original CS, we want to change the CS responsible for
1299     * the call to the ConnectionManager rather than adding it again as another call/conference.
1300     *
1301     * @param service The new {@link ConnectionServiceWrapper}.
1302     */
1303    public void replaceConnectionService(ConnectionServiceWrapper service) {
1304        Preconditions.checkNotNull(service);
1305
1306        if (mConnectionService != null) {
1307            ConnectionServiceWrapper serviceTemp = mConnectionService;
1308            mConnectionService = null;
1309            serviceTemp.removeCall(this);
1310            serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/);
1311        }
1312
1313        service.incrementAssociatedCallCount();
1314        mConnectionService = service;
1315        mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString());
1316    }
1317
1318    /**
1319     * Clears the associated connection service.
1320     */
1321    void clearConnectionService() {
1322        if (mConnectionService != null) {
1323            ConnectionServiceWrapper serviceTemp = mConnectionService;
1324            mConnectionService = null;
1325            serviceTemp.removeCall(this);
1326
1327            // Decrementing the count can cause the service to unbind, which itself can trigger the
1328            // service-death code.  Since the service death code tries to clean up any associated
1329            // calls, we need to make sure to remove that information (e.g., removeCall()) before
1330            // we decrement. Technically, invoking removeCall() prior to decrementing is all that is
1331            // necessary, but cleaning up mConnectionService prior to triggering an unbind is good
1332            // to do.
1333            decrementAssociatedCallCount(serviceTemp);
1334        }
1335    }
1336
1337    /**
1338     * Starts the create connection sequence. Upon completion, there should exist an active
1339     * connection through a connection service (or the call will have failed).
1340     *
1341     * @param phoneAccountRegistrar The phone account registrar.
1342     */
1343    void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) {
1344        if (mCreateConnectionProcessor != null) {
1345            Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" +
1346                    " due to a race between NewOutgoingCallIntentBroadcaster and " +
1347                    "phoneAccountSelected, but is harmlessly resolved by ignoring the second " +
1348                    "invocation.");
1349            return;
1350        }
1351        mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
1352                phoneAccountRegistrar, mContext);
1353        mCreateConnectionProcessor.process();
1354    }
1355
1356    @Override
1357    public void handleCreateConnectionSuccess(
1358            CallIdMapper idMapper,
1359            ParcelableConnection connection) {
1360        Log.v(this, "handleCreateConnectionSuccessful %s", connection);
1361        setTargetPhoneAccount(connection.getPhoneAccount());
1362        setHandle(connection.getHandle(), connection.getHandlePresentation());
1363        setCallerDisplayName(
1364                connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
1365
1366        setConnectionCapabilities(connection.getConnectionCapabilities());
1367        setConnectionProperties(connection.getConnectionProperties());
1368        setSupportedAudioRoutes(connection.getSupportedAudioRoutes());
1369        setVideoProvider(connection.getVideoProvider());
1370        setVideoState(connection.getVideoState());
1371        setRingbackRequested(connection.isRingbackRequested());
1372        setIsVoipAudioMode(connection.getIsVoipAudioMode());
1373        setStatusHints(connection.getStatusHints());
1374        putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras());
1375
1376        mConferenceableCalls.clear();
1377        for (String id : connection.getConferenceableConnectionIds()) {
1378            mConferenceableCalls.add(idMapper.getCall(id));
1379        }
1380
1381        switch (mCallDirection) {
1382            case CALL_DIRECTION_INCOMING:
1383                // Listeners (just CallsManager for now) will be responsible for checking whether
1384                // the call should be blocked.
1385                for (Listener l : mListeners) {
1386                    l.onSuccessfulIncomingCall(this);
1387                }
1388                break;
1389            case CALL_DIRECTION_OUTGOING:
1390                for (Listener l : mListeners) {
1391                    l.onSuccessfulOutgoingCall(this,
1392                            getStateFromConnectionState(connection.getState()));
1393                }
1394                break;
1395            case CALL_DIRECTION_UNKNOWN:
1396                for (Listener l : mListeners) {
1397                    l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection
1398                            .getState()));
1399                }
1400                break;
1401        }
1402    }
1403
1404    @Override
1405    public void handleCreateConnectionFailure(DisconnectCause disconnectCause) {
1406        clearConnectionService();
1407        setDisconnectCause(disconnectCause);
1408        mCallsManager.markCallAsDisconnected(this, disconnectCause);
1409
1410        switch (mCallDirection) {
1411            case CALL_DIRECTION_INCOMING:
1412                for (Listener listener : mListeners) {
1413                    listener.onFailedIncomingCall(this);
1414                }
1415                break;
1416            case CALL_DIRECTION_OUTGOING:
1417                for (Listener listener : mListeners) {
1418                    listener.onFailedOutgoingCall(this, disconnectCause);
1419                }
1420                break;
1421            case CALL_DIRECTION_UNKNOWN:
1422                for (Listener listener : mListeners) {
1423                    listener.onFailedUnknownCall(this);
1424                }
1425                break;
1426        }
1427    }
1428
1429    /**
1430     * Plays the specified DTMF tone.
1431     */
1432    void playDtmfTone(char digit) {
1433        if (mConnectionService == null) {
1434            Log.w(this, "playDtmfTone() request on a call without a connection service.");
1435        } else {
1436            Log.i(this, "Send playDtmfTone to connection service for call %s", this);
1437            mConnectionService.playDtmfTone(this, digit);
1438            Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit));
1439        }
1440    }
1441
1442    /**
1443     * Stops playing any currently playing DTMF tone.
1444     */
1445    void stopDtmfTone() {
1446        if (mConnectionService == null) {
1447            Log.w(this, "stopDtmfTone() request on a call without a connection service.");
1448        } else {
1449            Log.i(this, "Send stopDtmfTone to connection service for call %s", this);
1450            Log.addEvent(this, LogUtils.Events.STOP_DTMF);
1451            mConnectionService.stopDtmfTone(this);
1452        }
1453    }
1454
1455    /**
1456     * Silences the ringer.
1457     */
1458    void silence() {
1459        if (mConnectionService == null) {
1460            Log.w(this, "silence() request on a call without a connection service.");
1461        } else {
1462            Log.i(this, "Send silence to connection service for call %s", this);
1463            Log.addEvent(this, LogUtils.Events.SILENCE);
1464            mConnectionService.silence(this);
1465        }
1466    }
1467
1468    @VisibleForTesting
1469    public void disconnect() {
1470        disconnect(0);
1471    }
1472
1473    /**
1474     * Attempts to disconnect the call through the connection service.
1475     */
1476    @VisibleForTesting
1477    public void disconnect(long disconnectionTimeout) {
1478        Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT);
1479
1480        // Track that the call is now locally disconnecting.
1481        setLocallyDisconnecting(true);
1482        maybeSetCallAsDisconnectingChild();
1483
1484        if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT ||
1485                mState == CallState.CONNECTING) {
1486            Log.v(this, "Aborting call %s", this);
1487            abort(disconnectionTimeout);
1488        } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) {
1489            if (mConnectionService == null) {
1490                Log.e(this, new Exception(), "disconnect() request on a call without a"
1491                        + " connection service.");
1492            } else {
1493                Log.i(this, "Send disconnect to connection service for call: %s", this);
1494                // The call isn't officially disconnected until the connection service
1495                // confirms that the call was actually disconnected. Only then is the
1496                // association between call and connection service severed, see
1497                // {@link CallsManager#markCallAsDisconnected}.
1498                mConnectionService.disconnect(this);
1499            }
1500        }
1501    }
1502
1503    void abort(long disconnectionTimeout) {
1504        if (mCreateConnectionProcessor != null &&
1505                !mCreateConnectionProcessor.isProcessingComplete()) {
1506            mCreateConnectionProcessor.abort();
1507        } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT
1508                || mState == CallState.CONNECTING) {
1509            if (disconnectionTimeout > 0) {
1510                // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0
1511                // milliseconds, do not destroy the call.
1512                // Instead, we announce the cancellation and CallsManager handles
1513                // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and
1514                // then re-dial them quickly using a gateway, allowing the first call to end
1515                // causes jank. This timeout allows CallsManager to transition the first call into
1516                // the second call so that in-call only ever sees a single call...eliminating the
1517                // jank altogether. The app will also be able to set the timeout via an extra on
1518                // the ordered broadcast.
1519                for (Listener listener : mListeners) {
1520                    if (listener.onCanceledViaNewOutgoingCallBroadcast(
1521                            this, disconnectionTimeout)) {
1522                        // The first listener to handle this wins. A return value of true means that
1523                        // the listener will handle the disconnection process later and so we
1524                        // should not continue it here.
1525                        setLocallyDisconnecting(false);
1526                        return;
1527                    }
1528                }
1529            }
1530
1531            handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED));
1532        } else {
1533            Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING");
1534        }
1535    }
1536
1537    /**
1538     * Answers the call if it is ringing.
1539     *
1540     * @param videoState The video state in which to answer the call.
1541     */
1542    @VisibleForTesting
1543    public void answer(int videoState) {
1544        // Check to verify that the call is still in the ringing state. A call can change states
1545        // between the time the user hits 'answer' and Telecom receives the command.
1546        if (isRinging("answer")) {
1547            if (!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) {
1548                // Video calling is not supported, yet the InCallService is attempting to answer as
1549                // video.  We will simply answer as audio-only.
1550                videoState = VideoProfile.STATE_AUDIO_ONLY;
1551            }
1552            // At this point, we are asking the connection service to answer but we don't assume
1553            // that it will work. Instead, we wait until confirmation from the connectino service
1554            // that the call is in a non-STATE_RINGING state before changing the UI. See
1555            // {@link ConnectionServiceAdapter#setActive} and other set* methods.
1556            if (mConnectionService != null) {
1557                mConnectionService.answer(this, videoState);
1558            } else {
1559                Log.e(this, new NullPointerException(),
1560                        "answer call failed due to null CS callId=%s", getId());
1561            }
1562            Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT);
1563        }
1564    }
1565
1566    /**
1567     * Rejects the call if it is ringing.
1568     *
1569     * @param rejectWithMessage Whether to send a text message as part of the call rejection.
1570     * @param textMessage An optional text message to send as part of the rejection.
1571     */
1572    @VisibleForTesting
1573    public void reject(boolean rejectWithMessage, String textMessage) {
1574        // Check to verify that the call is still in the ringing state. A call can change states
1575        // between the time the user hits 'reject' and Telecomm receives the command.
1576        if (isRinging("reject")) {
1577            // Ensure video state history tracks video state at time of rejection.
1578            mVideoStateHistory |= mVideoState;
1579
1580            if (mConnectionService != null) {
1581                mConnectionService.reject(this, rejectWithMessage, textMessage);
1582            } else {
1583                Log.e(this, new NullPointerException(),
1584                        "reject call failed due to null CS callId=%s", getId());
1585            }
1586            Log.addEvent(this, LogUtils.Events.REQUEST_REJECT);
1587
1588        }
1589    }
1590
1591    /**
1592     * Puts the call on hold if it is currently active.
1593     */
1594    void hold() {
1595        if (mState == CallState.ACTIVE) {
1596            if (mConnectionService != null) {
1597                mConnectionService.hold(this);
1598            } else {
1599                Log.e(this, new NullPointerException(),
1600                        "hold call failed due to null CS callId=%s", getId());
1601            }
1602            Log.addEvent(this, LogUtils.Events.REQUEST_HOLD);
1603        }
1604    }
1605
1606    /**
1607     * Releases the call from hold if it is currently active.
1608     */
1609    void unhold() {
1610        if (mState == CallState.ON_HOLD) {
1611            if (mConnectionService != null) {
1612                mConnectionService.unhold(this);
1613            } else {
1614                Log.e(this, new NullPointerException(),
1615                        "unhold call failed due to null CS callId=%s", getId());
1616            }
1617            Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD);
1618        }
1619    }
1620
1621    /** Checks if this is a live call or not. */
1622    @VisibleForTesting
1623    public boolean isAlive() {
1624        switch (mState) {
1625            case CallState.NEW:
1626            case CallState.RINGING:
1627            case CallState.DISCONNECTED:
1628            case CallState.ABORTED:
1629                return false;
1630            default:
1631                return true;
1632        }
1633    }
1634
1635    boolean isActive() {
1636        return mState == CallState.ACTIVE;
1637    }
1638
1639    Bundle getExtras() {
1640        return mExtras;
1641    }
1642
1643    /**
1644     * Adds extras to the extras bundle associated with this {@link Call}.
1645     *
1646     * Note: this method needs to know the source of the extras change (see
1647     * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
1648     * originate from a connection service will only be notified to incall services.  Likewise,
1649     * changes originating from the incall services will only notify the connection service of the
1650     * change.
1651     *
1652     * @param source The source of the extras addition.
1653     * @param extras The extras.
1654     */
1655    void putExtras(int source, Bundle extras) {
1656        if (extras == null) {
1657            return;
1658        }
1659        if (mExtras == null) {
1660            mExtras = new Bundle();
1661        }
1662        mExtras.putAll(extras);
1663
1664        for (Listener l : mListeners) {
1665            l.onExtrasChanged(this, source, extras);
1666        }
1667
1668        // If the change originated from an InCallService, notify the connection service.
1669        if (source == SOURCE_INCALL_SERVICE) {
1670            if (mConnectionService != null) {
1671                mConnectionService.onExtrasChanged(this, mExtras);
1672            } else {
1673                Log.e(this, new NullPointerException(),
1674                        "putExtras failed due to null CS callId=%s", getId());
1675            }
1676        }
1677    }
1678
1679    /**
1680     * Removes extras from the extras bundle associated with this {@link Call}.
1681     *
1682     * Note: this method needs to know the source of the extras change (see
1683     * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}).  Extras changes which
1684     * originate from a connection service will only be notified to incall services.  Likewise,
1685     * changes originating from the incall services will only notify the connection service of the
1686     * change.
1687     *
1688     * @param source The source of the extras removal.
1689     * @param keys The extra keys to remove.
1690     */
1691    void removeExtras(int source, List<String> keys) {
1692        if (mExtras == null) {
1693            return;
1694        }
1695        for (String key : keys) {
1696            mExtras.remove(key);
1697        }
1698
1699        for (Listener l : mListeners) {
1700            l.onExtrasRemoved(this, source, keys);
1701        }
1702
1703        // If the change originated from an InCallService, notify the connection service.
1704        if (source == SOURCE_INCALL_SERVICE) {
1705            if (mConnectionService != null) {
1706                mConnectionService.onExtrasChanged(this, mExtras);
1707            } else {
1708                Log.e(this, new NullPointerException(),
1709                        "removeExtras failed due to null CS callId=%s", getId());
1710            }
1711        }
1712    }
1713
1714    @VisibleForTesting
1715    public Bundle getIntentExtras() {
1716        return mIntentExtras;
1717    }
1718
1719    void setIntentExtras(Bundle extras) {
1720        mIntentExtras = extras;
1721    }
1722
1723    public Intent getOriginalCallIntent() {
1724        return mOriginalCallIntent;
1725    }
1726
1727    public void setOriginalCallIntent(Intent intent) {
1728        mOriginalCallIntent = intent;
1729    }
1730
1731    /**
1732     * @return the uri of the contact associated with this call.
1733     */
1734    @VisibleForTesting
1735    public Uri getContactUri() {
1736        if (mCallerInfo == null || !mCallerInfo.contactExists) {
1737            return getHandle();
1738        }
1739        return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey);
1740    }
1741
1742    Uri getRingtone() {
1743        return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
1744    }
1745
1746    void onPostDialWait(String remaining) {
1747        for (Listener l : mListeners) {
1748            l.onPostDialWait(this, remaining);
1749        }
1750    }
1751
1752    void onPostDialChar(char nextChar) {
1753        for (Listener l : mListeners) {
1754            l.onPostDialChar(this, nextChar);
1755        }
1756    }
1757
1758    void postDialContinue(boolean proceed) {
1759        if (mConnectionService != null) {
1760            mConnectionService.onPostDialContinue(this, proceed);
1761        } else {
1762            Log.e(this, new NullPointerException(),
1763                    "postDialContinue failed due to null CS callId=%s", getId());
1764        }
1765    }
1766
1767    void conferenceWith(Call otherCall) {
1768        if (mConnectionService == null) {
1769            Log.w(this, "conference requested on a call without a connection service.");
1770        } else {
1771            Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
1772            mConnectionService.conference(this, otherCall);
1773        }
1774    }
1775
1776    void splitFromConference() {
1777        if (mConnectionService == null) {
1778            Log.w(this, "splitting from conference call without a connection service");
1779        } else {
1780            Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE);
1781            mConnectionService.splitFromConference(this);
1782        }
1783    }
1784
1785    @VisibleForTesting
1786    public void mergeConference() {
1787        if (mConnectionService == null) {
1788            Log.w(this, "merging conference calls without a connection service.");
1789        } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) {
1790            Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH);
1791            mConnectionService.mergeConference(this);
1792            mWasConferencePreviouslyMerged = true;
1793        }
1794    }
1795
1796    @VisibleForTesting
1797    public void swapConference() {
1798        if (mConnectionService == null) {
1799            Log.w(this, "swapping conference calls without a connection service.");
1800        } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) {
1801            Log.addEvent(this, LogUtils.Events.SWAP);
1802            mConnectionService.swapConference(this);
1803            switch (mChildCalls.size()) {
1804                case 1:
1805                    mConferenceLevelActiveCall = mChildCalls.get(0);
1806                    break;
1807                case 2:
1808                    // swap
1809                    mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ?
1810                            mChildCalls.get(1) : mChildCalls.get(0);
1811                    break;
1812                default:
1813                    // For anything else 0, or 3+, set it to null since it is impossible to tell.
1814                    mConferenceLevelActiveCall = null;
1815                    break;
1816            }
1817        }
1818    }
1819
1820    /**
1821     * Initiates a request to the connection service to pull this call.
1822     * <p>
1823     * This method can only be used for calls that have the
1824     * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and
1825     * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
1826     * <p>
1827     * An external call is a representation of a call which is taking place on another device
1828     * associated with a PhoneAccount on this device.  Issuing a request to pull the external call
1829     * tells the {@link android.telecom.ConnectionService} that it should move the call from the
1830     * other device to this one.  An example of this is the IMS multi-endpoint functionality.  A
1831     * user may have two phones with the same phone number.  If the user is engaged in an active
1832     * call on their first device, the network will inform the second device of that ongoing call in
1833     * the form of an external call.  The user may wish to continue their conversation on the second
1834     * device, so will issue a request to pull the call to the second device.
1835     * <p>
1836     * Requests to pull a call which is not external, or a call which is not pullable are ignored.
1837     */
1838    public void pullExternalCall() {
1839        if (mConnectionService == null) {
1840            Log.w(this, "pulling a call without a connection service.");
1841        }
1842
1843        if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) {
1844            Log.w(this, "pullExternalCall - call %s is not an external call.", mId);
1845            return;
1846        }
1847
1848        if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) {
1849            Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId);
1850            return;
1851        }
1852        Log.addEvent(this, LogUtils.Events.REQUEST_PULL);
1853        mConnectionService.pullExternalCall(this);
1854    }
1855
1856    /**
1857     * Sends a call event to the {@link ConnectionService} for this call.
1858     *
1859     * See {@link Call#sendCallEvent(String, Bundle)}.
1860     *
1861     * @param event The call event.
1862     * @param extras Associated extras.
1863     */
1864    public void sendCallEvent(String event, Bundle extras) {
1865        if (mConnectionService != null) {
1866            mConnectionService.sendCallEvent(this, event, extras);
1867        } else {
1868            Log.e(this, new NullPointerException(),
1869                    "sendCallEvent failed due to null CS callId=%s", getId());
1870        }
1871    }
1872
1873    /**
1874     * Sets this {@link Call} to has the specified {@code parentCall}.  Also sets the parent to
1875     * have this call as a child.
1876     * @param parentCall
1877     */
1878    void setParentAndChildCall(Call parentCall) {
1879        boolean isParentChanging = (mParentCall != parentCall);
1880        setParentCall(parentCall);
1881        setChildOf(parentCall);
1882        if (isParentChanging) {
1883            notifyParentChanged(parentCall);
1884        }
1885    }
1886
1887    /**
1888     * Notifies listeners when the parent call changes.
1889     * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}.
1890     * @param parentCall The new parent call for this call.
1891     */
1892    void notifyParentChanged(Call parentCall) {
1893        Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall);
1894        for (Listener l : mListeners) {
1895            l.onParentChanged(this);
1896        }
1897    }
1898
1899    /**
1900     * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set
1901     * the child.
1902     * TODO: This is only required when adding existing connections as a workaround so that we
1903     * can avoid sending the "onParentChanged" callback until later.
1904     * @param parentCall The new parent call.
1905     */
1906    void setParentCall(Call parentCall) {
1907        if (parentCall == this) {
1908            Log.e(this, new Exception(), "setting the parent to self");
1909            return;
1910        }
1911        if (parentCall == mParentCall) {
1912            // nothing to do
1913            return;
1914        }
1915        if (mParentCall != null) {
1916            mParentCall.removeChildCall(this);
1917        }
1918        mParentCall = parentCall;
1919    }
1920
1921    /**
1922     * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding
1923     * this call as a child of another call.
1924     * <p>
1925     * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to
1926     * ensure the InCall UI is updated with the change in parent.
1927     * @param parentCall The new parent for this call.
1928     */
1929    void setChildOf(Call parentCall) {
1930        if (parentCall != null && !parentCall.getChildCalls().contains(this)) {
1931            parentCall.addChildCall(this);
1932        }
1933    }
1934
1935    void setConferenceableCalls(List<Call> conferenceableCalls) {
1936        mConferenceableCalls.clear();
1937        mConferenceableCalls.addAll(conferenceableCalls);
1938
1939        for (Listener l : mListeners) {
1940            l.onConferenceableCallsChanged(this);
1941        }
1942    }
1943
1944    @VisibleForTesting
1945    public List<Call> getConferenceableCalls() {
1946        return mConferenceableCalls;
1947    }
1948
1949    @VisibleForTesting
1950    public boolean can(int capability) {
1951        return (mConnectionCapabilities & capability) == capability;
1952    }
1953
1954    @VisibleForTesting
1955    public boolean hasProperty(int property) {
1956        return (mConnectionProperties & property) == property;
1957    }
1958
1959    private void addChildCall(Call call) {
1960        if (!mChildCalls.contains(call)) {
1961            // Set the pseudo-active call to the latest child added to the conference.
1962            // See definition of mConferenceLevelActiveCall for more detail.
1963            mConferenceLevelActiveCall = call;
1964            mChildCalls.add(call);
1965
1966            Log.addEvent(this, LogUtils.Events.ADD_CHILD, call);
1967
1968            for (Listener l : mListeners) {
1969                l.onChildrenChanged(this);
1970            }
1971        }
1972    }
1973
1974    private void removeChildCall(Call call) {
1975        if (mChildCalls.remove(call)) {
1976            Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call);
1977            for (Listener l : mListeners) {
1978                l.onChildrenChanged(this);
1979            }
1980        }
1981    }
1982
1983    /**
1984     * Return whether the user can respond to this {@code Call} via an SMS message.
1985     *
1986     * @return true if the "Respond via SMS" feature should be enabled
1987     * for this incoming call.
1988     *
1989     * The general rule is that we *do* allow "Respond via SMS" except for
1990     * the few (relatively rare) cases where we know for sure it won't
1991     * work, namely:
1992     *   - a bogus or blank incoming number
1993     *   - a call from a SIP address
1994     *   - a "call presentation" that doesn't allow the number to be revealed
1995     *
1996     * In all other cases, we allow the user to respond via SMS.
1997     *
1998     * Note that this behavior isn't perfect; for example we have no way
1999     * to detect whether the incoming call is from a landline (with most
2000     * networks at least), so we still enable this feature even though
2001     * SMSes to that number will silently fail.
2002     */
2003    boolean isRespondViaSmsCapable() {
2004        if (mState != CallState.RINGING) {
2005            return false;
2006        }
2007
2008        if (getHandle() == null) {
2009            // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in
2010            // other words, the user should not be able to see the incoming phone number.
2011            return false;
2012        }
2013
2014        if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) {
2015            // The incoming number is actually a URI (i.e. a SIP address),
2016            // not a regular PSTN phone number, and we can't send SMSes to
2017            // SIP addresses.
2018            // (TODO: That might still be possible eventually, though. Is
2019            // there some SIP-specific equivalent to sending a text message?)
2020            return false;
2021        }
2022
2023        // Is there a valid SMS application on the phone?
2024        if (SmsApplication.getDefaultRespondViaMessageApplication(mContext,
2025                true /*updateIfNeeded*/) == null) {
2026            return false;
2027        }
2028
2029        // TODO: with some carriers (in certain countries) you *can* actually
2030        // tell whether a given number is a mobile phone or not. So in that
2031        // case we could potentially return false here if the incoming call is
2032        // from a land line.
2033
2034        // If none of the above special cases apply, it's OK to enable the
2035        // "Respond via SMS" feature.
2036        return true;
2037    }
2038
2039    List<String> getCannedSmsResponses() {
2040        return mCannedSmsResponses;
2041    }
2042
2043    /**
2044     * We need to make sure that before we move a call to the disconnected state, it no
2045     * longer has any parent/child relationships.  We want to do this to ensure that the InCall
2046     * Service always has the right data in the right order.  We also want to do it in telecom so
2047     * that the insurance policy lives in the framework side of things.
2048     */
2049    private void fixParentAfterDisconnect() {
2050        setParentAndChildCall(null);
2051    }
2052
2053    /**
2054     * @return True if the call is ringing, else logs the action name.
2055     */
2056    private boolean isRinging(String actionName) {
2057        if (mState == CallState.RINGING) {
2058            return true;
2059        }
2060
2061        Log.i(this, "Request to %s a non-ringing call %s", actionName, this);
2062        return false;
2063    }
2064
2065    @SuppressWarnings("rawtypes")
2066    private void decrementAssociatedCallCount(ServiceBinder binder) {
2067        if (binder != null) {
2068            binder.decrementAssociatedCallCount();
2069        }
2070    }
2071
2072    /**
2073     * Looks up contact information based on the current handle.
2074     */
2075    private void startCallerInfoLookup() {
2076        mCallerInfo = null;
2077        mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener);
2078    }
2079
2080    /**
2081     * Saves the specified caller info if the specified token matches that of the last query
2082     * that was made.
2083     *
2084     * @param callerInfo The new caller information to set.
2085     */
2086    private void setCallerInfo(Uri handle, CallerInfo callerInfo) {
2087        Trace.beginSection("setCallerInfo");
2088        if (callerInfo == null) {
2089            Log.i(this, "CallerInfo lookup returned null, skipping update");
2090            return;
2091        }
2092
2093        if (!handle.equals(mHandle)) {
2094            Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring.");
2095            return;
2096        }
2097
2098        mCallerInfo = callerInfo;
2099        Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo);
2100
2101        if (mCallerInfo.contactDisplayPhotoUri == null ||
2102                mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) {
2103            for (Listener l : mListeners) {
2104                l.onCallerInfoChanged(this);
2105            }
2106        }
2107
2108        Trace.endSection();
2109    }
2110
2111    public CallerInfo getCallerInfo() {
2112        return mCallerInfo;
2113    }
2114
2115    private void maybeLoadCannedSmsResponses() {
2116        if (mCallDirection == CALL_DIRECTION_INCOMING
2117                && isRespondViaSmsCapable()
2118                && !mCannedSmsResponsesLoadingStarted) {
2119            Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages");
2120            mCannedSmsResponsesLoadingStarted = true;
2121            mCallsManager.getRespondViaSmsManager().loadCannedTextMessages(
2122                    new Response<Void, List<String>>() {
2123                        @Override
2124                        public void onResult(Void request, List<String>... result) {
2125                            if (result.length > 0) {
2126                                Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]);
2127                                mCannedSmsResponses = result[0];
2128                                for (Listener l : mListeners) {
2129                                    l.onCannedSmsResponsesLoaded(Call.this);
2130                                }
2131                            }
2132                        }
2133
2134                        @Override
2135                        public void onError(Void request, int code, String msg) {
2136                            Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code,
2137                                    msg);
2138                        }
2139                    },
2140                    mContext
2141            );
2142        } else {
2143            Log.d(this, "maybeLoadCannedSmsResponses: doing nothing");
2144        }
2145    }
2146
2147    /**
2148     * Sets speakerphone option on when call begins.
2149     */
2150    public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) {
2151        mSpeakerphoneOn = startWithSpeakerphone;
2152    }
2153
2154    /**
2155     * Returns speakerphone option.
2156     *
2157     * @return Whether or not speakerphone should be set automatically when call begins.
2158     */
2159    public boolean getStartWithSpeakerphoneOn() {
2160        return mSpeakerphoneOn;
2161    }
2162
2163    public void stopRtt() {
2164        if (mConnectionService != null) {
2165            mConnectionService.stopRtt(this);
2166        } else {
2167            // If this gets called by the in-call app before the connection service is set, we'll
2168            // just ignore it since it's really not supposed to happen.
2169            Log.w(this, "stopRtt() called before connection service is set.");
2170        }
2171    }
2172
2173    public void sendRttRequest() {
2174        setRttStreams(true);
2175        mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2176    }
2177
2178    public void setRttStreams(boolean shouldBeRtt) {
2179        boolean areStreamsInitialized = mInCallToConnectionServiceStreams != null
2180                && mConnectionServiceToInCallStreams != null;
2181        if (shouldBeRtt && !areStreamsInitialized) {
2182            try {
2183                mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe();
2184                mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe();
2185            } catch (IOException e) {
2186                Log.e(this, e, "Failed to create pipes for RTT call.");
2187            }
2188        } else if (!shouldBeRtt && areStreamsInitialized) {
2189            closeRttPipes();
2190            mInCallToConnectionServiceStreams = null;
2191            mConnectionServiceToInCallStreams = null;
2192        }
2193    }
2194
2195    public void onRttConnectionFailure(int reason) {
2196        setRttStreams(false);
2197        for (Listener l : mListeners) {
2198            l.onRttInitiationFailure(this, reason);
2199        }
2200    }
2201
2202    public void onRemoteRttRequest() {
2203        if (isRttCall()) {
2204            Log.w(this, "Remote RTT request on a call that's already RTT");
2205            return;
2206        }
2207
2208        mPendingRttRequestId = mCallsManager.getNextRttRequestId();
2209        for (Listener l : mListeners) {
2210            l.onRemoteRttRequest(this, mPendingRttRequestId);
2211        }
2212    }
2213
2214    public void handleRttRequestResponse(int id, boolean accept) {
2215        if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) {
2216            Log.w(this, "Response received to a nonexistent RTT request: %d", id);
2217            return;
2218        }
2219        if (id != mPendingRttRequestId) {
2220            Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId);
2221            return;
2222        }
2223        setRttStreams(accept);
2224        if (accept) {
2225            Log.i(this, "RTT request %d accepted.", id);
2226            mConnectionService.respondToRttRequest(
2227                    this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs());
2228        } else {
2229            Log.i(this, "RTT request %d rejected.", id);
2230            mConnectionService.respondToRttRequest(this, null, null);
2231        }
2232    }
2233
2234    public void closeRttPipes() {
2235        // TODO: may defer this until call is removed?
2236    }
2237
2238    public boolean isRttCall() {
2239        return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT;
2240    }
2241
2242    public ParcelFileDescriptor getCsToInCallRttPipeForCs() {
2243        return mConnectionServiceToInCallStreams == null ? null
2244                : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2245    }
2246
2247    public ParcelFileDescriptor getInCallToCsRttPipeForCs() {
2248        return mInCallToConnectionServiceStreams == null ? null
2249                : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX];
2250    }
2251
2252    public ParcelFileDescriptor getCsToInCallRttPipeForInCall() {
2253        return mConnectionServiceToInCallStreams == null ? null
2254                : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX];
2255    }
2256
2257    public ParcelFileDescriptor getInCallToCsRttPipeForInCall() {
2258        return mInCallToConnectionServiceStreams == null ? null
2259                : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX];
2260    }
2261
2262    public int getRttMode() {
2263        return mRttMode;
2264    }
2265
2266    /**
2267     * Sets a video call provider for the call.
2268     */
2269    public void setVideoProvider(IVideoProvider videoProvider) {
2270        Log.v(this, "setVideoProvider");
2271
2272        if (videoProvider != null ) {
2273            try {
2274                mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this,
2275                        mCallsManager);
2276            } catch (RemoteException ignored) {
2277                // Ignore RemoteException.
2278            }
2279        } else {
2280            mVideoProviderProxy = null;
2281        }
2282
2283        mVideoProvider = videoProvider;
2284
2285        for (Listener l : mListeners) {
2286            l.onVideoCallProviderChanged(Call.this);
2287        }
2288    }
2289
2290    /**
2291     * @return The {@link Connection.VideoProvider} binder.
2292     */
2293    public IVideoProvider getVideoProvider() {
2294        if (mVideoProviderProxy == null) {
2295            return null;
2296        }
2297
2298        return mVideoProviderProxy.getInterface();
2299    }
2300
2301    /**
2302     * @return The {@link VideoProviderProxy} for this call.
2303     */
2304    public VideoProviderProxy getVideoProviderProxy() {
2305        return mVideoProviderProxy;
2306    }
2307
2308    /**
2309     * The current video state for the call.
2310     * See {@link VideoProfile} for a list of valid video states.
2311     */
2312    public int getVideoState() {
2313        return mVideoState;
2314    }
2315
2316    /**
2317     * Returns the video states which were applicable over the duration of a call.
2318     * See {@link VideoProfile} for a list of valid video states.
2319     *
2320     * @return The video states applicable over the duration of the call.
2321     */
2322    public int getVideoStateHistory() {
2323        return mVideoStateHistory;
2324    }
2325
2326    /**
2327     * Determines the current video state for the call.
2328     * For an outgoing call determines the desired video state for the call.
2329     * Valid values: see {@link VideoProfile}
2330     *
2331     * @param videoState The video state for the call.
2332     */
2333    public void setVideoState(int videoState) {
2334        // If the phone account associated with this call does not support video calling, then we
2335        // will automatically set the video state to audio-only.
2336        if (!isVideoCallingSupported()) {
2337            Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)",
2338                    VideoProfile.videoStateToString(videoState));
2339            videoState = VideoProfile.STATE_AUDIO_ONLY;
2340        }
2341
2342        // Track which video states were applicable over the duration of the call.
2343        // Only track the call state when the call is active or disconnected.  This ensures we do
2344        // not include the video state when:
2345        // - Call is incoming (but not answered).
2346        // - Call it outgoing (but not answered).
2347        // We include the video state when disconnected to ensure that rejected calls reflect the
2348        // appropriate video state.
2349        if (isActive() || getState() == CallState.DISCONNECTED) {
2350            mVideoStateHistory = mVideoStateHistory | videoState;
2351        }
2352
2353        int previousVideoState = mVideoState;
2354        mVideoState = videoState;
2355        if (mVideoState != previousVideoState) {
2356            Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED,
2357                    VideoProfile.videoStateToString(videoState));
2358            for (Listener l : mListeners) {
2359                l.onVideoStateChanged(this, previousVideoState, mVideoState);
2360            }
2361        }
2362
2363        if (VideoProfile.isVideo(videoState)) {
2364            mAnalytics.setCallIsVideo(true);
2365        }
2366    }
2367
2368    public boolean getIsVoipAudioMode() {
2369        return mIsVoipAudioMode;
2370    }
2371
2372    public void setIsVoipAudioMode(boolean audioModeIsVoip) {
2373        mIsVoipAudioMode = audioModeIsVoip;
2374        for (Listener l : mListeners) {
2375            l.onIsVoipAudioModeChanged(this);
2376        }
2377    }
2378
2379    public StatusHints getStatusHints() {
2380        return mStatusHints;
2381    }
2382
2383    public void setStatusHints(StatusHints statusHints) {
2384        mStatusHints = statusHints;
2385        for (Listener l : mListeners) {
2386            l.onStatusHintsChanged(this);
2387        }
2388    }
2389
2390    public boolean isUnknown() {
2391        return mCallDirection == CALL_DIRECTION_UNKNOWN;
2392    }
2393
2394    /**
2395     * Determines if this call is in a disconnecting state.
2396     *
2397     * @return {@code true} if this call is locally disconnecting.
2398     */
2399    public boolean isLocallyDisconnecting() {
2400        return mIsLocallyDisconnecting;
2401    }
2402
2403    /**
2404     * Sets whether this call is in a disconnecting state.
2405     *
2406     * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting.
2407     */
2408    private void setLocallyDisconnecting(boolean isLocallyDisconnecting) {
2409        mIsLocallyDisconnecting = isLocallyDisconnecting;
2410    }
2411
2412    /**
2413     * @return user handle of user initiating the outgoing call.
2414     */
2415    public UserHandle getInitiatingUser() {
2416        return mInitiatingUser;
2417    }
2418
2419    /**
2420     * Set the user handle of user initiating the outgoing call.
2421     * @param initiatingUser
2422     */
2423    public void setInitiatingUser(UserHandle initiatingUser) {
2424        Preconditions.checkNotNull(initiatingUser);
2425        mInitiatingUser = initiatingUser;
2426    }
2427
2428    static int getStateFromConnectionState(int state) {
2429        switch (state) {
2430            case Connection.STATE_INITIALIZING:
2431                return CallState.CONNECTING;
2432            case Connection.STATE_ACTIVE:
2433                return CallState.ACTIVE;
2434            case Connection.STATE_DIALING:
2435                return CallState.DIALING;
2436            case Connection.STATE_PULLING_CALL:
2437                return CallState.PULLING;
2438            case Connection.STATE_DISCONNECTED:
2439                return CallState.DISCONNECTED;
2440            case Connection.STATE_HOLDING:
2441                return CallState.ON_HOLD;
2442            case Connection.STATE_NEW:
2443                return CallState.NEW;
2444            case Connection.STATE_RINGING:
2445                return CallState.RINGING;
2446        }
2447        return CallState.DISCONNECTED;
2448    }
2449
2450    /**
2451     * Determines if this call is in disconnected state and waiting to be destroyed.
2452     *
2453     * @return {@code true} if this call is disconected.
2454     */
2455    public boolean isDisconnected() {
2456        return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED);
2457    }
2458
2459    /**
2460     * Determines if this call has just been created and has not been configured properly yet.
2461     *
2462     * @return {@code true} if this call is new.
2463     */
2464    public boolean isNew() {
2465        return getState() == CallState.NEW;
2466    }
2467
2468    /**
2469     * Sets the call data usage for the call.
2470     *
2471     * @param callDataUsage The new call data usage (in bytes).
2472     */
2473    public void setCallDataUsage(long callDataUsage) {
2474        mCallDataUsage = callDataUsage;
2475    }
2476
2477    /**
2478     * Returns the call data usage for the call.
2479     *
2480     * @return The call data usage (in bytes).
2481     */
2482    public long getCallDataUsage() {
2483        return mCallDataUsage;
2484    }
2485
2486    public void setRttMode(int mode) {
2487        mRttMode = mode;
2488        // TODO: hook this up to CallAudioManager
2489    }
2490
2491    /**
2492     * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent
2493     * has come back to telecom and was processed.
2494     */
2495    public boolean isNewOutgoingCallIntentBroadcastDone() {
2496        return mIsNewOutgoingCallIntentBroadcastDone;
2497    }
2498
2499    public void setNewOutgoingCallIntentBroadcastIsDone() {
2500        mIsNewOutgoingCallIntentBroadcastDone = true;
2501    }
2502
2503    /**
2504     * Determines if the call has been held by the remote party.
2505     *
2506     * @return {@code true} if the call is remotely held, {@code false} otherwise.
2507     */
2508    public boolean isRemotelyHeld() {
2509        return mIsRemotelyHeld;
2510    }
2511
2512    /**
2513     * Handles Connection events received from a {@link ConnectionService}.
2514     *
2515     * @param event The event.
2516     * @param extras The extras.
2517     */
2518    public void onConnectionEvent(String event, Bundle extras) {
2519        Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event);
2520        if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) {
2521            mIsRemotelyHeld = true;
2522            Log.addEvent(this, LogUtils.Events.REMOTELY_HELD);
2523            // Inform listeners of the fact that a call hold tone was received.  This will trigger
2524            // the CallAudioManager to play a tone via the InCallTonePlayer.
2525            for (Listener l : mListeners) {
2526                l.onHoldToneRequested(this);
2527            }
2528        } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) {
2529            mIsRemotelyHeld = false;
2530            Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD);
2531            for (Listener l : mListeners) {
2532                l.onHoldToneRequested(this);
2533            }
2534        } else {
2535            for (Listener l : mListeners) {
2536                l.onConnectionEvent(this, event, extras);
2537            }
2538        }
2539    }
2540
2541    public void setOriginalConnectionId(String originalConnectionId) {
2542        mOriginalConnectionId = originalConnectionId;
2543    }
2544
2545    /**
2546     * For calls added via a ConnectionManager using the
2547     * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle,
2548     * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS,
2549     * indicates the ID of this call as it was referred to by the {@code ConnectionService} which
2550     * originally created it.
2551     *
2552     * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}.
2553     * @return The original connection ID.
2554     */
2555    public String getOriginalConnectionId() {
2556        return mOriginalConnectionId;
2557    }
2558
2559    /**
2560     * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either
2561     * remotely or locally.
2562     *
2563     * @param capabilities The {@link Connection} capabilities for the call.
2564     * @return {@code true} if video is supported, {@code false} otherwise.
2565     */
2566    private boolean doesCallSupportVideo(int capabilities) {
2567        return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 ||
2568                (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0;
2569    }
2570
2571    /**
2572     * Remove any video capabilities set on a {@link Connection} capabilities bitmask.
2573     *
2574     * @param capabilities The capabilities.
2575     * @return The bitmask with video capabilities removed.
2576     */
2577    private int removeVideoCapabilities(int capabilities) {
2578        return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL |
2579                Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
2580    }
2581}
2582