Call.java revision f97a009f7645690a92bdef3a3ef39298deaff4b7
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 android.telecom;
18
19import android.annotation.SystemApi;
20import android.net.Uri;
21import android.os.Bundle;
22import android.os.Handler;
23
24import java.lang.String;
25import java.util.ArrayList;
26import java.util.Collections;
27import java.util.List;
28import java.util.Map;
29import java.util.Objects;
30import java.util.concurrent.CopyOnWriteArrayList;
31
32/**
33 * Represents an ongoing phone call that the in-call app should present to the user.
34 */
35public final class Call {
36    /**
37     * The state of a {@code Call} when newly created.
38     */
39    public static final int STATE_NEW = 0;
40
41    /**
42     * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected.
43     */
44    public static final int STATE_DIALING = 1;
45
46    /**
47     * The state of an incoming {@code Call} when ringing locally, but not yet connected.
48     */
49    public static final int STATE_RINGING = 2;
50
51    /**
52     * The state of a {@code Call} when in a holding state.
53     */
54    public static final int STATE_HOLDING = 3;
55
56    /**
57     * The state of a {@code Call} when actively supporting conversation.
58     */
59    public static final int STATE_ACTIVE = 4;
60
61    /**
62     * The state of a {@code Call} when no further voice or other communication is being
63     * transmitted, the remote side has been or will inevitably be informed that the {@code Call}
64     * is no longer active, and the local data transport has or inevitably will release resources
65     * associated with this {@code Call}.
66     */
67    public static final int STATE_DISCONNECTED = 7;
68
69    /**
70     * The state of an outgoing {@code Call} when waiting on user to select a
71     * {@link PhoneAccount} through which to place the call.
72     */
73    public static final int STATE_SELECT_PHONE_ACCOUNT = 8;
74
75    /**
76     * @hide
77     * @deprecated use STATE_SELECT_PHONE_ACCOUNT.
78     */
79    @Deprecated
80    @SystemApi
81    public static final int STATE_PRE_DIAL_WAIT = STATE_SELECT_PHONE_ACCOUNT;
82
83    /**
84     * The initial state of an outgoing {@code Call}.
85     * Common transitions are to {@link #STATE_DIALING} state for a successful call or
86     * {@link #STATE_DISCONNECTED} if it failed.
87     */
88    public static final int STATE_CONNECTING = 9;
89
90    /**
91     * The state of a {@code Call} when the user has initiated a disconnection of the call, but the
92     * call has not yet been disconnected by the underlying {@code ConnectionService}.  The next
93     * state of the call is (potentially) {@link #STATE_DISCONNECTED}.
94     */
95    public static final int STATE_DISCONNECTING = 10;
96
97    /**
98     * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
99     * extras. Used to pass the phone accounts to display on the front end to the user in order to
100     * select phone accounts to (for example) place a call.
101     */
102    public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
103
104    public static class Details {
105
106        /** Call can currently be put on hold or unheld. */
107        public static final int CAPABILITY_HOLD = 0x00000001;
108
109        /** Call supports the hold feature. */
110        public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
111
112        /**
113         * Calls within a conference can be merged. A {@link ConnectionService} has the option to
114         * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
115         * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
116         * capability allows a merge button to be shown while the conference call is in the foreground
117         * of the in-call UI.
118         * <p>
119         * This is only intended for use by a {@link Conference}.
120         */
121        public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
122
123        /**
124         * Calls within a conference can be swapped between foreground and background.
125         * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
126         * <p>
127         * This is only intended for use by a {@link Conference}.
128         */
129        public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
130
131        /**
132         * @hide
133         */
134        public static final int CAPABILITY_UNUSED_1 = 0x00000010;
135
136        /** Call supports responding via text option. */
137        public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
138
139        /** Call can be muted. */
140        public static final int CAPABILITY_MUTE = 0x00000040;
141
142        /**
143         * Call supports conference call management. This capability only applies to {@link Conference}
144         * calls which can have {@link Connection}s as children.
145         */
146        public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
147
148        /**
149         * Local device supports receiving video.
150         */
151        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
152
153        /**
154         * Local device supports transmitting video.
155         */
156        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
157
158        /**
159         * Local device supports bidirectional video calling.
160         */
161        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
162                CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
163
164        /**
165         * Remote device supports receiving video.
166         */
167        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
168
169        /**
170         * Remote device supports transmitting video.
171         */
172        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
173
174        /**
175         * Remote device supports bidirectional video calling.
176         */
177        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
178                CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
179
180        /**
181         * Call is able to be separated from its parent {@code Conference}, if any.
182         */
183        public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
184
185        /**
186         * Call is able to be individually disconnected when in a {@code Conference}.
187         */
188        public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
189
190        /**
191         * Speed up audio setup for MT call.
192         * @hide
193         */
194        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
195
196        /**
197         * Call can be upgraded to a video call.
198         * @hide
199         */
200        public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
201
202        /**
203         * For video calls, indicates whether the outgoing video for the call can be paused using
204         * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
205         */
206        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
207
208        /**
209         * Call sends responses through connection.
210         * @hide
211         */
212        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
213
214        /**
215         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
216         * <p>
217         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
218         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
219         * downgraded from a video call back to a VideoState of
220         * {@link VideoProfile#STATE_AUDIO_ONLY}.
221         * <p>
222         * Intuitively, a call which can be downgraded to audio should also have local and remote
223         * video
224         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
225         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
226         */
227        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
228
229        //******************************************************************************************
230        // Next CAPABILITY value: 0x00800000
231        //******************************************************************************************
232
233        /**
234         * Whether the call is currently a conference.
235         */
236        public static final int PROPERTY_CONFERENCE = 0x00000001;
237
238        /**
239         * Whether the call is a generic conference, where we do not know the precise state of
240         * participants in the conference (eg. on CDMA).
241         */
242        public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
243
244        /**
245         * Whether the call is made while the device is in emergency callback mode.
246         */
247        public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
248
249        /**
250         * Connection is using WIFI.
251         */
252        public static final int PROPERTY_WIFI = 0x00000008;
253
254        /**
255         * Call is using high definition audio.
256         */
257        public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
258
259        /**
260         * Whether the call is associated with the work profile.
261         */
262        public static final int PROPERTY_WORK_CALL = 0x00000020;
263
264        //******************************************************************************************
265        // Next PROPERTY value: 0x00000040
266        //******************************************************************************************
267
268        private final Uri mHandle;
269        private final int mHandlePresentation;
270        private final String mCallerDisplayName;
271        private final int mCallerDisplayNamePresentation;
272        private final PhoneAccountHandle mAccountHandle;
273        private final int mCallCapabilities;
274        private final int mCallProperties;
275        private final DisconnectCause mDisconnectCause;
276        private final long mConnectTimeMillis;
277        private final GatewayInfo mGatewayInfo;
278        private final int mVideoState;
279        private final StatusHints mStatusHints;
280        private final Bundle mExtras;
281        private final Bundle mIntentExtras;
282
283        /**
284         * Whether the supplied capabilities  supports the specified capability.
285         *
286         * @param capabilities A bit field of capabilities.
287         * @param capability The capability to check capabilities for.
288         * @return Whether the specified capability is supported.
289         */
290        public static boolean can(int capabilities, int capability) {
291            return (capabilities & capability) == capability;
292        }
293
294        /**
295         * Whether the capabilities of this {@code Details} supports the specified capability.
296         *
297         * @param capability The capability to check capabilities for.
298         * @return Whether the specified capability is supported.
299         */
300        public boolean can(int capability) {
301            return can(mCallCapabilities, capability);
302        }
303
304        /**
305         * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
306         *
307         * @param capabilities A capability bit field.
308         * @return A human readable string representation.
309         */
310        public static String capabilitiesToString(int capabilities) {
311            StringBuilder builder = new StringBuilder();
312            builder.append("[Capabilities:");
313            if (can(capabilities, CAPABILITY_HOLD)) {
314                builder.append(" CAPABILITY_HOLD");
315            }
316            if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
317                builder.append(" CAPABILITY_SUPPORT_HOLD");
318            }
319            if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
320                builder.append(" CAPABILITY_MERGE_CONFERENCE");
321            }
322            if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
323                builder.append(" CAPABILITY_SWAP_CONFERENCE");
324            }
325            if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
326                builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
327            }
328            if (can(capabilities, CAPABILITY_MUTE)) {
329                builder.append(" CAPABILITY_MUTE");
330            }
331            if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
332                builder.append(" CAPABILITY_MANAGE_CONFERENCE");
333            }
334            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
335                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
336            }
337            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
338                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
339            }
340            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
341                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
342            }
343            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
344                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
345            }
346            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
347                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
348            }
349            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
350                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
351            }
352            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
353                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
354            }
355            if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
356                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
357            }
358            if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
359                builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
360            }
361            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
362                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
363            }
364            builder.append("]");
365            return builder.toString();
366        }
367
368        /**
369         * Whether the supplied properties includes the specified property.
370         *
371         * @param properties A bit field of properties.
372         * @param property The property to check properties for.
373         * @return Whether the specified property is supported.
374         */
375        public static boolean hasProperty(int properties, int property) {
376            return (properties & property) == property;
377        }
378
379        /**
380         * Whether the properties of this {@code Details} includes the specified property.
381         *
382         * @param property The property to check properties for.
383         * @return Whether the specified property is supported.
384         */
385        public boolean hasProperty(int property) {
386            return hasProperty(mCallProperties, property);
387        }
388
389        /**
390         * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
391         *
392         * @param properties A property bit field.
393         * @return A human readable string representation.
394         */
395        public static String propertiesToString(int properties) {
396            StringBuilder builder = new StringBuilder();
397            builder.append("[Properties:");
398            if (hasProperty(properties, PROPERTY_CONFERENCE)) {
399                builder.append(" PROPERTY_CONFERENCE");
400            }
401            if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
402                builder.append(" PROPERTY_GENERIC_CONFERENCE");
403            }
404            if (hasProperty(properties, PROPERTY_WIFI)) {
405                builder.append(" PROPERTY_WIFI");
406            }
407            if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
408                builder.append(" PROPERTY_HIGH_DEF_AUDIO");
409            }
410            if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
411                builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
412            }
413            builder.append("]");
414            return builder.toString();
415        }
416
417        /**
418         * @return The handle (e.g., phone number) to which the {@code Call} is currently
419         * connected.
420         */
421        public Uri getHandle() {
422            return mHandle;
423        }
424
425        /**
426         * @return The presentation requirements for the handle. See
427         * {@link TelecomManager} for valid values.
428         */
429        public int getHandlePresentation() {
430            return mHandlePresentation;
431        }
432
433        /**
434         * @return The display name for the caller.
435         */
436        public String getCallerDisplayName() {
437            return mCallerDisplayName;
438        }
439
440        /**
441         * @return The presentation requirements for the caller display name. See
442         * {@link TelecomManager} for valid values.
443         */
444        public int getCallerDisplayNamePresentation() {
445            return mCallerDisplayNamePresentation;
446        }
447
448        /**
449         * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
450         * routed.
451         */
452        public PhoneAccountHandle getAccountHandle() {
453            return mAccountHandle;
454        }
455
456        /**
457         * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
458         *         {@code CAPABILITY_*} constants in this class.
459         */
460        public int getCallCapabilities() {
461            return mCallCapabilities;
462        }
463
464        /**
465         * @return A bitmask of the properties of the {@code Call}, as defined by the various
466         *         {@code PROPERTY_*} constants in this class.
467         */
468        public int getCallProperties() {
469            return mCallProperties;
470        }
471
472        /**
473         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
474         * by {@link android.telecom.DisconnectCause}.
475         */
476        public DisconnectCause getDisconnectCause() {
477            return mDisconnectCause;
478        }
479
480        /**
481         * @return The time the {@code Call} has been connected. This information is updated
482         * periodically, but user interfaces should not rely on this to display any "call time
483         * clock".
484         */
485        public final long getConnectTimeMillis() {
486            return mConnectTimeMillis;
487        }
488
489        /**
490         * @return Information about any calling gateway the {@code Call} may be using.
491         */
492        public GatewayInfo getGatewayInfo() {
493            return mGatewayInfo;
494        }
495
496        /**
497         * @return The video state of the {@code Call}.
498         */
499        public int getVideoState() {
500            return mVideoState;
501        }
502
503        /**
504         * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
505         * have been set.
506         */
507        public StatusHints getStatusHints() {
508            return mStatusHints;
509        }
510
511        /**
512         * @return The extras associated with this call.
513         */
514        public Bundle getExtras() {
515            return mExtras;
516        }
517
518        /**
519         * @return The extras used with the original intent to place this call.
520         */
521        public Bundle getIntentExtras() {
522            return mIntentExtras;
523        }
524
525        @Override
526        public boolean equals(Object o) {
527            if (o instanceof Details) {
528                Details d = (Details) o;
529                return
530                        Objects.equals(mHandle, d.mHandle) &&
531                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
532                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
533                        Objects.equals(mCallerDisplayNamePresentation,
534                                d.mCallerDisplayNamePresentation) &&
535                        Objects.equals(mAccountHandle, d.mAccountHandle) &&
536                        Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
537                        Objects.equals(mCallProperties, d.mCallProperties) &&
538                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
539                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
540                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
541                        Objects.equals(mVideoState, d.mVideoState) &&
542                        Objects.equals(mStatusHints, d.mStatusHints) &&
543                        areBundlesEqual(mExtras, d.mExtras) &&
544                        areBundlesEqual(mIntentExtras, d.mIntentExtras);
545            }
546            return false;
547        }
548
549        @Override
550        public int hashCode() {
551            return
552                    Objects.hashCode(mHandle) +
553                    Objects.hashCode(mHandlePresentation) +
554                    Objects.hashCode(mCallerDisplayName) +
555                    Objects.hashCode(mCallerDisplayNamePresentation) +
556                    Objects.hashCode(mAccountHandle) +
557                    Objects.hashCode(mCallCapabilities) +
558                    Objects.hashCode(mCallProperties) +
559                    Objects.hashCode(mDisconnectCause) +
560                    Objects.hashCode(mConnectTimeMillis) +
561                    Objects.hashCode(mGatewayInfo) +
562                    Objects.hashCode(mVideoState) +
563                    Objects.hashCode(mStatusHints) +
564                    Objects.hashCode(mExtras) +
565                    Objects.hashCode(mIntentExtras);
566        }
567
568        /** {@hide} */
569        public Details(
570                Uri handle,
571                int handlePresentation,
572                String callerDisplayName,
573                int callerDisplayNamePresentation,
574                PhoneAccountHandle accountHandle,
575                int capabilities,
576                int properties,
577                DisconnectCause disconnectCause,
578                long connectTimeMillis,
579                GatewayInfo gatewayInfo,
580                int videoState,
581                StatusHints statusHints,
582                Bundle extras,
583                Bundle intentExtras) {
584            mHandle = handle;
585            mHandlePresentation = handlePresentation;
586            mCallerDisplayName = callerDisplayName;
587            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
588            mAccountHandle = accountHandle;
589            mCallCapabilities = capabilities;
590            mCallProperties = properties;
591            mDisconnectCause = disconnectCause;
592            mConnectTimeMillis = connectTimeMillis;
593            mGatewayInfo = gatewayInfo;
594            mVideoState = videoState;
595            mStatusHints = statusHints;
596            mExtras = extras;
597            mIntentExtras = intentExtras;
598        }
599    }
600
601    public static abstract class Callback {
602        /**
603         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
604         *
605         * @param call The {@code Call} invoking this method.
606         * @param state The new state of the {@code Call}.
607         */
608        public void onStateChanged(Call call, int state) {}
609
610        /**
611         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
612         *
613         * @param call The {@code Call} invoking this method.
614         * @param parent The new parent of the {@code Call}.
615         */
616        public void onParentChanged(Call call, Call parent) {}
617
618        /**
619         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
620         *
621         * @param call The {@code Call} invoking this method.
622         * @param children The new children of the {@code Call}.
623         */
624        public void onChildrenChanged(Call call, List<Call> children) {}
625
626        /**
627         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
628         *
629         * @param call The {@code Call} invoking this method.
630         * @param details A {@code Details} object describing the {@code Call}.
631         */
632        public void onDetailsChanged(Call call, Details details) {}
633
634        /**
635         * Invoked when the text messages that can be used as responses to the incoming
636         * {@code Call} are loaded from the relevant database.
637         * See {@link #getCannedTextResponses()}.
638         *
639         * @param call The {@code Call} invoking this method.
640         * @param cannedTextResponses The text messages useable as responses.
641         */
642        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
643
644        /**
645         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
646         * character. This causes the post-dial signals to stop pending user confirmation. An
647         * implementation should present this choice to the user and invoke
648         * {@link #postDialContinue(boolean)} when the user makes the choice.
649         *
650         * @param call The {@code Call} invoking this method.
651         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
652         */
653        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
654
655        /**
656         * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
657         *
658         * @param call The {@code Call} invoking this method.
659         * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
660         */
661        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
662
663        /**
664         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
665         * up their UI for the {@code Call} in response to state transitions. Specifically,
666         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
667         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
668         * clients should wait for this method to be invoked.
669         *
670         * @param call The {@code Call} being destroyed.
671         */
672        public void onCallDestroyed(Call call) {}
673
674        /**
675         * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
676         * conferenced.
677         *
678         * @param call The {@code Call} being updated.
679         * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
680         *          conferenced.
681         */
682        public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
683    }
684
685    /**
686     * @deprecated Use {@code Call.Callback} instead.
687     * @hide
688     */
689    @Deprecated
690    @SystemApi
691    public static abstract class Listener extends Callback { }
692
693    private final Phone mPhone;
694    private final String mTelecomCallId;
695    private final InCallAdapter mInCallAdapter;
696    private final List<String> mChildrenIds = new ArrayList<>();
697    private final List<Call> mChildren = new ArrayList<>();
698    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
699    private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
700    private final List<Call> mConferenceableCalls = new ArrayList<>();
701    private final List<Call> mUnmodifiableConferenceableCalls =
702            Collections.unmodifiableList(mConferenceableCalls);
703
704    private boolean mChildrenCached;
705    private String mParentId = null;
706    private int mState;
707    private List<String> mCannedTextResponses = null;
708    private String mRemainingPostDialSequence;
709    private VideoCallImpl mVideoCallImpl;
710    private Details mDetails;
711
712    /**
713     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
714     *
715     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
716     * remaining or this {@code Call} is not in a post-dial state.
717     */
718    public String getRemainingPostDialSequence() {
719        return mRemainingPostDialSequence;
720    }
721
722    /**
723     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
724     * @param videoState The video state in which to answer the call.
725     */
726    public void answer(int videoState) {
727        mInCallAdapter.answerCall(mTelecomCallId, videoState);
728    }
729
730    /**
731     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
732     *
733     * @param rejectWithMessage Whether to reject with a text message.
734     * @param textMessage An optional text message with which to respond.
735     */
736    public void reject(boolean rejectWithMessage, String textMessage) {
737        mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
738    }
739
740    /**
741     * Instructs this {@code Call} to disconnect.
742     */
743    public void disconnect() {
744        mInCallAdapter.disconnectCall(mTelecomCallId);
745    }
746
747    /**
748     * Instructs this {@code Call} to go on hold.
749     */
750    public void hold() {
751        mInCallAdapter.holdCall(mTelecomCallId);
752    }
753
754    /**
755     * Instructs this {@link #STATE_HOLDING} call to release from hold.
756     */
757    public void unhold() {
758        mInCallAdapter.unholdCall(mTelecomCallId);
759    }
760
761    /**
762     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
763     *
764     * Any other currently playing DTMF tone in the specified call is immediately stopped.
765     *
766     * @param digit A character representing the DTMF digit for which to play the tone. This
767     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
768     */
769    public void playDtmfTone(char digit) {
770        mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
771    }
772
773    /**
774     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
775     * currently playing.
776     *
777     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
778     * currently playing, this method will do nothing.
779     */
780    public void stopDtmfTone() {
781        mInCallAdapter.stopDtmfTone(mTelecomCallId);
782    }
783
784    /**
785     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
786     *
787     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
788     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
789     *
790     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
791     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
792     *
793     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
794     * {@code Call} will pause playing the tones and notify callbacks via
795     * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
796     * should display to the user an indication of this state and an affordance to continue
797     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
798     * app should invoke the {@link #postDialContinue(boolean)} method.
799     *
800     * @param proceed Whether or not to continue with the post-dial sequence.
801     */
802    public void postDialContinue(boolean proceed) {
803        mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
804    }
805
806    /**
807     * Notifies this {@code Call} that an account has been selected and to proceed with placing
808     * an outgoing call. Optionally sets this account as the default account.
809     */
810    public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
811        mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
812
813    }
814
815    /**
816     * Instructs this {@code Call} to enter a conference.
817     *
818     * @param callToConferenceWith The other call with which to conference.
819     */
820    public void conference(Call callToConferenceWith) {
821        if (callToConferenceWith != null) {
822            mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
823        }
824    }
825
826    /**
827     * Instructs this {@code Call} to split from any conference call with which it may be
828     * connected.
829     */
830    public void splitFromConference() {
831        mInCallAdapter.splitFromConference(mTelecomCallId);
832    }
833
834    /**
835     * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
836     */
837    public void mergeConference() {
838        mInCallAdapter.mergeConference(mTelecomCallId);
839    }
840
841    /**
842     * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
843     */
844    public void swapConference() {
845        mInCallAdapter.swapConference(mTelecomCallId);
846    }
847
848    /**
849     * Obtains the parent of this {@code Call} in a conference, if any.
850     *
851     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
852     * child of any conference {@code Call}s.
853     */
854    public Call getParent() {
855        if (mParentId != null) {
856            return mPhone.internalGetCallByTelecomId(mParentId);
857        }
858        return null;
859    }
860
861    /**
862     * Obtains the children of this conference {@code Call}, if any.
863     *
864     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
865     * {@code List} otherwise.
866     */
867    public List<Call> getChildren() {
868        if (!mChildrenCached) {
869            mChildrenCached = true;
870            mChildren.clear();
871
872            for(String id : mChildrenIds) {
873                Call call = mPhone.internalGetCallByTelecomId(id);
874                if (call == null) {
875                    // At least one child was still not found, so do not save true for "cached"
876                    mChildrenCached = false;
877                } else {
878                    mChildren.add(call);
879                }
880            }
881        }
882
883        return mUnmodifiableChildren;
884    }
885
886    /**
887     * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
888     *
889     * @return The list of conferenceable {@code Call}s.
890     */
891    public List<Call> getConferenceableCalls() {
892        return mUnmodifiableConferenceableCalls;
893    }
894
895    /**
896     * Obtains the state of this {@code Call}.
897     *
898     * @return A state value, chosen from the {@code STATE_*} constants.
899     */
900    public int getState() {
901        return mState;
902    }
903
904    /**
905     * Obtains a list of canned, pre-configured message responses to present to the user as
906     * ways of rejecting this {@code Call} using via a text message.
907     *
908     * @see #reject(boolean, String)
909     *
910     * @return A list of canned text message responses.
911     */
912    public List<String> getCannedTextResponses() {
913        return mCannedTextResponses;
914    }
915
916    /**
917     * Obtains an object that can be used to display video from this {@code Call}.
918     *
919     * @return An {@code Call.VideoCall}.
920     */
921    public InCallService.VideoCall getVideoCall() {
922        return mVideoCallImpl;
923    }
924
925    /**
926     * Obtains an object containing call details.
927     *
928     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
929     * result may be {@code null}.
930     */
931    public Details getDetails() {
932        return mDetails;
933    }
934
935    /**
936     * Registers a callback to this {@code Call}.
937     *
938     * @param callback A {@code Callback}.
939     */
940    public void registerCallback(Callback callback) {
941        registerCallback(callback, new Handler());
942    }
943
944    /**
945     * Registers a callback to this {@code Call}.
946     *
947     * @param callback A {@code Callback}.
948     * @param handler A handler which command and status changes will be delivered to.
949     */
950    public void registerCallback(Callback callback, Handler handler) {
951        unregisterCallback(callback);
952        // Don't allow new callback registration if the call is already being destroyed.
953        if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
954            mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
955        }
956    }
957
958    /**
959     * Unregisters a callback from this {@code Call}.
960     *
961     * @param callback A {@code Callback}.
962     */
963    public void unregisterCallback(Callback callback) {
964        // Don't allow callback deregistration if the call is already being destroyed.
965        if (callback != null && mState != STATE_DISCONNECTED) {
966            for (CallbackRecord<Callback> record : mCallbackRecords) {
967                if (record.getCallback() == callback) {
968                    mCallbackRecords.remove(record);
969                    break;
970                }
971            }
972        }
973    }
974
975    /**
976     * Adds a listener to this {@code Call}.
977     *
978     * @param listener A {@code Listener}.
979     * @deprecated Use {@link #registerCallback} instead.
980     * @hide
981     */
982    @Deprecated
983    @SystemApi
984    public void addListener(Listener listener) {
985        registerCallback(listener);
986    }
987
988    /**
989     * Removes a listener from this {@code Call}.
990     *
991     * @param listener A {@code Listener}.
992     * @deprecated Use {@link #unregisterCallback} instead.
993     * @hide
994     */
995    @Deprecated
996    @SystemApi
997    public void removeListener(Listener listener) {
998        unregisterCallback(listener);
999    }
1000
1001    /** {@hide} */
1002    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
1003        mPhone = phone;
1004        mTelecomCallId = telecomCallId;
1005        mInCallAdapter = inCallAdapter;
1006        mState = STATE_NEW;
1007    }
1008
1009    /** {@hide} */
1010    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
1011        mPhone = phone;
1012        mTelecomCallId = telecomCallId;
1013        mInCallAdapter = inCallAdapter;
1014        mState = state;
1015    }
1016
1017    /** {@hide} */
1018    final String internalGetCallId() {
1019        return mTelecomCallId;
1020    }
1021
1022    /** {@hide} */
1023    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
1024        // First, we update the internal state as far as possible before firing any updates.
1025        Details details = new Details(
1026                parcelableCall.getHandle(),
1027                parcelableCall.getHandlePresentation(),
1028                parcelableCall.getCallerDisplayName(),
1029                parcelableCall.getCallerDisplayNamePresentation(),
1030                parcelableCall.getAccountHandle(),
1031                parcelableCall.getCapabilities(),
1032                parcelableCall.getProperties(),
1033                parcelableCall.getDisconnectCause(),
1034                parcelableCall.getConnectTimeMillis(),
1035                parcelableCall.getGatewayInfo(),
1036                parcelableCall.getVideoState(),
1037                parcelableCall.getStatusHints(),
1038                parcelableCall.getExtras(),
1039                parcelableCall.getIntentExtras());
1040        boolean detailsChanged = !Objects.equals(mDetails, details);
1041        if (detailsChanged) {
1042            mDetails = details;
1043        }
1044
1045        boolean cannedTextResponsesChanged = false;
1046        if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
1047                && !parcelableCall.getCannedSmsResponses().isEmpty()) {
1048            mCannedTextResponses =
1049                    Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
1050            cannedTextResponsesChanged = true;
1051        }
1052
1053        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
1054        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
1055                !Objects.equals(mVideoCallImpl, newVideoCallImpl);
1056        if (videoCallChanged) {
1057            mVideoCallImpl = newVideoCallImpl;
1058        }
1059        if (mVideoCallImpl != null) {
1060            mVideoCallImpl.setVideoState(getDetails().getVideoState());
1061        }
1062
1063        int state = parcelableCall.getState();
1064        boolean stateChanged = mState != state;
1065        if (stateChanged) {
1066            mState = state;
1067        }
1068
1069        String parentId = parcelableCall.getParentCallId();
1070        boolean parentChanged = !Objects.equals(mParentId, parentId);
1071        if (parentChanged) {
1072            mParentId = parentId;
1073        }
1074
1075        List<String> childCallIds = parcelableCall.getChildCallIds();
1076        boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
1077        if (childrenChanged) {
1078            mChildrenIds.clear();
1079            mChildrenIds.addAll(parcelableCall.getChildCallIds());
1080            mChildrenCached = false;
1081        }
1082
1083        List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
1084        List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
1085        for (String otherId : conferenceableCallIds) {
1086            if (callIdMap.containsKey(otherId)) {
1087                conferenceableCalls.add(callIdMap.get(otherId));
1088            }
1089        }
1090
1091        if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
1092            mConferenceableCalls.clear();
1093            mConferenceableCalls.addAll(conferenceableCalls);
1094            fireConferenceableCallsChanged();
1095        }
1096
1097        // Now we fire updates, ensuring that any client who listens to any of these notifications
1098        // gets the most up-to-date state.
1099
1100        if (stateChanged) {
1101            fireStateChanged(mState);
1102        }
1103        if (detailsChanged) {
1104            fireDetailsChanged(mDetails);
1105        }
1106        if (cannedTextResponsesChanged) {
1107            fireCannedTextResponsesLoaded(mCannedTextResponses);
1108        }
1109        if (videoCallChanged) {
1110            fireVideoCallChanged(mVideoCallImpl);
1111        }
1112        if (parentChanged) {
1113            fireParentChanged(getParent());
1114        }
1115        if (childrenChanged) {
1116            fireChildrenChanged(getChildren());
1117        }
1118
1119        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
1120        // remove ourselves from the Phone. Note that we do this after completing all state updates
1121        // so a client can cleanly transition all their UI to the state appropriate for a
1122        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
1123        if (mState == STATE_DISCONNECTED) {
1124            fireCallDestroyed();
1125        }
1126    }
1127
1128    /** {@hide} */
1129    final void internalSetPostDialWait(String remaining) {
1130        mRemainingPostDialSequence = remaining;
1131        firePostDialWait(mRemainingPostDialSequence);
1132    }
1133
1134    /** {@hide} */
1135    final void internalSetDisconnected() {
1136        if (mState != Call.STATE_DISCONNECTED) {
1137            mState = Call.STATE_DISCONNECTED;
1138            fireStateChanged(mState);
1139            fireCallDestroyed();
1140        }
1141    }
1142
1143    private void fireStateChanged(final int newState) {
1144        for (CallbackRecord<Callback> record : mCallbackRecords) {
1145            final Call call = this;
1146            final Callback callback = record.getCallback();
1147            record.getHandler().post(new Runnable() {
1148                @Override
1149                public void run() {
1150                    callback.onStateChanged(call, newState);
1151                }
1152            });
1153        }
1154    }
1155
1156    private void fireParentChanged(final Call newParent) {
1157        for (CallbackRecord<Callback> record : mCallbackRecords) {
1158            final Call call = this;
1159            final Callback callback = record.getCallback();
1160            record.getHandler().post(new Runnable() {
1161                @Override
1162                public void run() {
1163                    callback.onParentChanged(call, newParent);
1164                }
1165            });
1166        }
1167    }
1168
1169    private void fireChildrenChanged(final List<Call> children) {
1170        for (CallbackRecord<Callback> record : mCallbackRecords) {
1171            final Call call = this;
1172            final Callback callback = record.getCallback();
1173            record.getHandler().post(new Runnable() {
1174                @Override
1175                public void run() {
1176                    callback.onChildrenChanged(call, children);
1177                }
1178            });
1179        }
1180    }
1181
1182    private void fireDetailsChanged(final Details details) {
1183        for (CallbackRecord<Callback> record : mCallbackRecords) {
1184            final Call call = this;
1185            final Callback callback = record.getCallback();
1186            record.getHandler().post(new Runnable() {
1187                @Override
1188                public void run() {
1189                    callback.onDetailsChanged(call, details);
1190                }
1191            });
1192        }
1193    }
1194
1195    private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
1196        for (CallbackRecord<Callback> record : mCallbackRecords) {
1197            final Call call = this;
1198            final Callback callback = record.getCallback();
1199            record.getHandler().post(new Runnable() {
1200                @Override
1201                public void run() {
1202                    callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
1203                }
1204            });
1205        }
1206    }
1207
1208    private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
1209        for (CallbackRecord<Callback> record : mCallbackRecords) {
1210            final Call call = this;
1211            final Callback callback = record.getCallback();
1212            record.getHandler().post(new Runnable() {
1213                @Override
1214                public void run() {
1215                    callback.onVideoCallChanged(call, videoCall);
1216                }
1217            });
1218        }
1219    }
1220
1221    private void firePostDialWait(final String remainingPostDialSequence) {
1222        for (CallbackRecord<Callback> record : mCallbackRecords) {
1223            final Call call = this;
1224            final Callback callback = record.getCallback();
1225            record.getHandler().post(new Runnable() {
1226                @Override
1227                public void run() {
1228                    callback.onPostDialWait(call, remainingPostDialSequence);
1229                }
1230            });
1231        }
1232    }
1233
1234    private void fireCallDestroyed() {
1235        /**
1236         * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
1237         * onCallRemoved callback, we remove this call from the Phone's record
1238         * only once all of the registered onCallDestroyed callbacks are executed.
1239         * All the callbacks get removed from our records as a part of this operation
1240         * since onCallDestroyed is the final callback.
1241         */
1242        final Call call = this;
1243        if (mCallbackRecords.isEmpty()) {
1244            // No callbacks registered, remove the call from Phone's record.
1245            mPhone.internalRemoveCall(call);
1246        }
1247        for (final CallbackRecord<Callback> record : mCallbackRecords) {
1248            final Callback callback = record.getCallback();
1249            record.getHandler().post(new Runnable() {
1250                @Override
1251                public void run() {
1252                    boolean isFinalRemoval = false;
1253                    RuntimeException toThrow = null;
1254                    try {
1255                        callback.onCallDestroyed(call);
1256                    } catch (RuntimeException e) {
1257                            toThrow = e;
1258                    }
1259                    synchronized(Call.this) {
1260                        mCallbackRecords.remove(record);
1261                        if (mCallbackRecords.isEmpty()) {
1262                            isFinalRemoval = true;
1263                        }
1264                    }
1265                    if (isFinalRemoval) {
1266                        mPhone.internalRemoveCall(call);
1267                    }
1268                    if (toThrow != null) {
1269                        throw toThrow;
1270                    }
1271                }
1272            });
1273        }
1274    }
1275
1276    private void fireConferenceableCallsChanged() {
1277        for (CallbackRecord<Callback> record : mCallbackRecords) {
1278            final Call call = this;
1279            final Callback callback = record.getCallback();
1280            record.getHandler().post(new Runnable() {
1281                @Override
1282                public void run() {
1283                    callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
1284                }
1285            });
1286        }
1287    }
1288
1289    /**
1290     * Determines if two bundles are equal.
1291     *
1292     * @param bundle The original bundle.
1293     * @param newBundle The bundle to compare with.
1294     * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
1295     */
1296    private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
1297        if (bundle == null || newBundle == null) {
1298            return bundle == newBundle;
1299        }
1300
1301        if (bundle.size() != newBundle.size()) {
1302            return false;
1303        }
1304
1305        for(String key : bundle.keySet()) {
1306            if (key != null) {
1307                final Object value = bundle.get(key);
1308                final Object newValue = newBundle.get(key);
1309                if (!Objects.equals(value, newValue)) {
1310                    return false;
1311                }
1312            }
1313        }
1314        return true;
1315    }
1316}
1317