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