Call.java revision 2a6319022861f8c2fce74b5657d78c008b33115a
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.Arrays;
27import java.util.Collections;
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 state of an external call which is in the process of being pulled from a remote device to
100     * the local device.
101     * <p>
102     * A call can only be in this state if the {@link Details#PROPERTY_IS_EXTERNAL_CALL} property
103     * and {@link Details#CAPABILITY_CAN_PULL_CALL} capability are set on the call.
104     * <p>
105     * An {@link InCallService} will only see this state if it has the
106     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
107     * manifest.
108     */
109    public static final int STATE_PULLING_CALL = 11;
110
111    /**
112     * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
113     * extras. Used to pass the phone accounts to display on the front end to the user in order to
114     * select phone accounts to (for example) place a call.
115     */
116    public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
117
118    public static class Details {
119
120        /** Call can currently be put on hold or unheld. */
121        public static final int CAPABILITY_HOLD = 0x00000001;
122
123        /** Call supports the hold feature. */
124        public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
125
126        /**
127         * Calls within a conference can be merged. A {@link ConnectionService} has the option to
128         * add a {@link Conference} call before the child {@link Connection}s are merged. This is how
129         * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
130         * capability allows a merge button to be shown while the conference call is in the foreground
131         * of the in-call UI.
132         * <p>
133         * This is only intended for use by a {@link Conference}.
134         */
135        public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
136
137        /**
138         * Calls within a conference can be swapped between foreground and background.
139         * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
140         * <p>
141         * This is only intended for use by a {@link Conference}.
142         */
143        public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
144
145        /**
146         * @hide
147         */
148        public static final int CAPABILITY_UNUSED_1 = 0x00000010;
149
150        /** Call supports responding via text option. */
151        public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
152
153        /** Call can be muted. */
154        public static final int CAPABILITY_MUTE = 0x00000040;
155
156        /**
157         * Call supports conference call management. This capability only applies to {@link Conference}
158         * calls which can have {@link Connection}s as children.
159         */
160        public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
161
162        /**
163         * Local device supports receiving video.
164         */
165        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
166
167        /**
168         * Local device supports transmitting video.
169         */
170        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
171
172        /**
173         * Local device supports bidirectional video calling.
174         */
175        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
176                CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
177
178        /**
179         * Remote device supports receiving video.
180         */
181        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
182
183        /**
184         * Remote device supports transmitting video.
185         */
186        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
187
188        /**
189         * Remote device supports bidirectional video calling.
190         */
191        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
192                CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
193
194        /**
195         * Call is able to be separated from its parent {@code Conference}, if any.
196         */
197        public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
198
199        /**
200         * Call is able to be individually disconnected when in a {@code Conference}.
201         */
202        public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
203
204        /**
205         * Speed up audio setup for MT call.
206         * @hide
207         */
208        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
209
210        /**
211         * Call can be upgraded to a video call.
212         * @hide
213         */
214        public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
215
216        /**
217         * For video calls, indicates whether the outgoing video for the call can be paused using
218         * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
219         */
220        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
221
222        /**
223         * Call sends responses through connection.
224         * @hide
225         */
226        public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00200000;
227
228        /**
229         * When set, prevents a video {@code Call} from being downgraded to an audio-only call.
230         * <p>
231         * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
232         * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
233         * downgraded from a video call back to a VideoState of
234         * {@link VideoProfile#STATE_AUDIO_ONLY}.
235         * <p>
236         * Intuitively, a call which can be downgraded to audio should also have local and remote
237         * video
238         * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
239         * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
240         */
241        public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00400000;
242
243        /**
244         * When set for an external call, indicates that this {@code Call} can be pulled from a
245         * remote device to the current device.
246         * <p>
247         * Should only be set on a {@code Call} where {@link #PROPERTY_IS_EXTERNAL_CALL} is set.
248         * <p>
249         * An {@link InCallService} will only see calls with this capability if it has the
250         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
251         * in its manifest.
252         * <p>
253         * See {@link Connection#CAPABILITY_CAN_PULL_CALL} and
254         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
255         */
256        public static final int CAPABILITY_CAN_PULL_CALL = 0x00800000;
257
258        //******************************************************************************************
259        // Next CAPABILITY value: 0x01000000
260        //******************************************************************************************
261
262        /**
263         * Whether the call is currently a conference.
264         */
265        public static final int PROPERTY_CONFERENCE = 0x00000001;
266
267        /**
268         * Whether the call is a generic conference, where we do not know the precise state of
269         * participants in the conference (eg. on CDMA).
270         */
271        public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002;
272
273        /**
274         * Whether the call is made while the device is in emergency callback mode.
275         */
276        public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004;
277
278        /**
279         * Connection is using WIFI.
280         */
281        public static final int PROPERTY_WIFI = 0x00000008;
282
283        /**
284         * Call is using high definition audio.
285         */
286        public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010;
287
288        /**
289         * @deprecated Use {@link #PROPERTY_ENTERPRISE_CALL} instead.
290         */
291        public static final int PROPERTY_WORK_CALL = 0x00000020;
292
293        /**
294         * Whether the call is associated with the work profile.
295         */
296        public static final int PROPERTY_ENTERPRISE_CALL = 0x00000020;
297
298        /**
299         * When set, indicates that this {@code Call} does not actually exist locally for the
300         * {@link ConnectionService}.
301         * <p>
302         * Consider, for example, a scenario where a user has two phones with the same phone number.
303         * When a user places a call on one device, the telephony stack can represent that call on
304         * the other device by adding it to the {@link ConnectionService} with the
305         * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property set.
306         * <p>
307         * An {@link InCallService} will only see calls with this property if it has the
308         * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
309         * in its manifest.
310         * <p>
311         * See {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
312         */
313        public static final int PROPERTY_IS_EXTERNAL_CALL = 0x00000040;
314
315        //******************************************************************************************
316        // Next PROPERTY value: 0x00000100
317        //******************************************************************************************
318
319        private final String mTelecomCallId;
320        private final Uri mHandle;
321        private final int mHandlePresentation;
322        private final String mCallerDisplayName;
323        private final int mCallerDisplayNamePresentation;
324        private final PhoneAccountHandle mAccountHandle;
325        private final int mCallCapabilities;
326        private final int mCallProperties;
327        private final DisconnectCause mDisconnectCause;
328        private final long mConnectTimeMillis;
329        private final GatewayInfo mGatewayInfo;
330        private final int mVideoState;
331        private final StatusHints mStatusHints;
332        private final Bundle mExtras;
333        private final Bundle mIntentExtras;
334
335        /**
336         * Whether the supplied capabilities  supports the specified capability.
337         *
338         * @param capabilities A bit field of capabilities.
339         * @param capability The capability to check capabilities for.
340         * @return Whether the specified capability is supported.
341         */
342        public static boolean can(int capabilities, int capability) {
343            return (capabilities & capability) == capability;
344        }
345
346        /**
347         * Whether the capabilities of this {@code Details} supports the specified capability.
348         *
349         * @param capability The capability to check capabilities for.
350         * @return Whether the specified capability is supported.
351         */
352        public boolean can(int capability) {
353            return can(mCallCapabilities, capability);
354        }
355
356        /**
357         * Render a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
358         *
359         * @param capabilities A capability bit field.
360         * @return A human readable string representation.
361         */
362        public static String capabilitiesToString(int capabilities) {
363            StringBuilder builder = new StringBuilder();
364            builder.append("[Capabilities:");
365            if (can(capabilities, CAPABILITY_HOLD)) {
366                builder.append(" CAPABILITY_HOLD");
367            }
368            if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
369                builder.append(" CAPABILITY_SUPPORT_HOLD");
370            }
371            if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
372                builder.append(" CAPABILITY_MERGE_CONFERENCE");
373            }
374            if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
375                builder.append(" CAPABILITY_SWAP_CONFERENCE");
376            }
377            if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
378                builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
379            }
380            if (can(capabilities, CAPABILITY_MUTE)) {
381                builder.append(" CAPABILITY_MUTE");
382            }
383            if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
384                builder.append(" CAPABILITY_MANAGE_CONFERENCE");
385            }
386            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
387                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
388            }
389            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
390                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
391            }
392            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
393                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
394            }
395            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
396                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
397            }
398            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
399                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
400            }
401            if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
402                builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
403            }
404            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
405                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
406            }
407            if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
408                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
409            }
410            if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
411                builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
412            }
413            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
414                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
415            }
416            if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
417                builder.append(" CAPABILITY_CAN_PULL_CALL");
418            }
419            builder.append("]");
420            return builder.toString();
421        }
422
423        /**
424         * Whether the supplied properties includes the specified property.
425         *
426         * @param properties A bit field of properties.
427         * @param property The property to check properties for.
428         * @return Whether the specified property is supported.
429         */
430        public static boolean hasProperty(int properties, int property) {
431            return (properties & property) == property;
432        }
433
434        /**
435         * Whether the properties of this {@code Details} includes the specified property.
436         *
437         * @param property The property to check properties for.
438         * @return Whether the specified property is supported.
439         */
440        public boolean hasProperty(int property) {
441            return hasProperty(mCallProperties, property);
442        }
443
444        /**
445         * Render a set of property bits ({@code PROPERTY_*}) as a human readable string.
446         *
447         * @param properties A property bit field.
448         * @return A human readable string representation.
449         */
450        public static String propertiesToString(int properties) {
451            StringBuilder builder = new StringBuilder();
452            builder.append("[Properties:");
453            if (hasProperty(properties, PROPERTY_CONFERENCE)) {
454                builder.append(" PROPERTY_CONFERENCE");
455            }
456            if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) {
457                builder.append(" PROPERTY_GENERIC_CONFERENCE");
458            }
459            if (hasProperty(properties, PROPERTY_WIFI)) {
460                builder.append(" PROPERTY_WIFI");
461            }
462            if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) {
463                builder.append(" PROPERTY_HIGH_DEF_AUDIO");
464            }
465            if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
466                builder.append(" PROPERTY_EMERGENCY_CALLBACK_MODE");
467            }
468            if (hasProperty(properties, PROPERTY_IS_EXTERNAL_CALL)) {
469                builder.append(" PROPERTY_IS_EXTERNAL_CALL");
470            }
471            builder.append("]");
472            return builder.toString();
473        }
474
475        /** {@hide} */
476        public String getTelecomCallId() {
477            return mTelecomCallId;
478        }
479
480        /**
481         * @return The handle (e.g., phone number) to which the {@code Call} is currently
482         * connected.
483         */
484        public Uri getHandle() {
485            return mHandle;
486        }
487
488        /**
489         * @return The presentation requirements for the handle. See
490         * {@link TelecomManager} for valid values.
491         */
492        public int getHandlePresentation() {
493            return mHandlePresentation;
494        }
495
496        /**
497         * @return The display name for the caller.
498         */
499        public String getCallerDisplayName() {
500            return mCallerDisplayName;
501        }
502
503        /**
504         * @return The presentation requirements for the caller display name. See
505         * {@link TelecomManager} for valid values.
506         */
507        public int getCallerDisplayNamePresentation() {
508            return mCallerDisplayNamePresentation;
509        }
510
511        /**
512         * @return The {@code PhoneAccountHandle} whereby the {@code Call} is currently being
513         * routed.
514         */
515        public PhoneAccountHandle getAccountHandle() {
516            return mAccountHandle;
517        }
518
519        /**
520         * @return A bitmask of the capabilities of the {@code Call}, as defined by the various
521         *         {@code CAPABILITY_*} constants in this class.
522         */
523        public int getCallCapabilities() {
524            return mCallCapabilities;
525        }
526
527        /**
528         * @return A bitmask of the properties of the {@code Call}, as defined by the various
529         *         {@code PROPERTY_*} constants in this class.
530         */
531        public int getCallProperties() {
532            return mCallProperties;
533        }
534
535        /**
536         * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed
537         * by {@link android.telecom.DisconnectCause}.
538         */
539        public DisconnectCause getDisconnectCause() {
540            return mDisconnectCause;
541        }
542
543        /**
544         * @return The time the {@code Call} has been connected. This information is updated
545         * periodically, but user interfaces should not rely on this to display any "call time
546         * clock".
547         */
548        public final long getConnectTimeMillis() {
549            return mConnectTimeMillis;
550        }
551
552        /**
553         * @return Information about any calling gateway the {@code Call} may be using.
554         */
555        public GatewayInfo getGatewayInfo() {
556            return mGatewayInfo;
557        }
558
559        /**
560         * @return The video state of the {@code Call}.
561         */
562        public int getVideoState() {
563            return mVideoState;
564        }
565
566        /**
567         * @return The current {@link android.telecom.StatusHints}, or {@code null} if none
568         * have been set.
569         */
570        public StatusHints getStatusHints() {
571            return mStatusHints;
572        }
573
574        /**
575         * @return The extras associated with this call.
576         */
577        public Bundle getExtras() {
578            return mExtras;
579        }
580
581        /**
582         * @return The extras used with the original intent to place this call.
583         */
584        public Bundle getIntentExtras() {
585            return mIntentExtras;
586        }
587
588        @Override
589        public boolean equals(Object o) {
590            if (o instanceof Details) {
591                Details d = (Details) o;
592                return
593                        Objects.equals(mHandle, d.mHandle) &&
594                        Objects.equals(mHandlePresentation, d.mHandlePresentation) &&
595                        Objects.equals(mCallerDisplayName, d.mCallerDisplayName) &&
596                        Objects.equals(mCallerDisplayNamePresentation,
597                                d.mCallerDisplayNamePresentation) &&
598                        Objects.equals(mAccountHandle, d.mAccountHandle) &&
599                        Objects.equals(mCallCapabilities, d.mCallCapabilities) &&
600                        Objects.equals(mCallProperties, d.mCallProperties) &&
601                        Objects.equals(mDisconnectCause, d.mDisconnectCause) &&
602                        Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) &&
603                        Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
604                        Objects.equals(mVideoState, d.mVideoState) &&
605                        Objects.equals(mStatusHints, d.mStatusHints) &&
606                        areBundlesEqual(mExtras, d.mExtras) &&
607                        areBundlesEqual(mIntentExtras, d.mIntentExtras);
608            }
609            return false;
610        }
611
612        @Override
613        public int hashCode() {
614            return
615                    Objects.hashCode(mHandle) +
616                    Objects.hashCode(mHandlePresentation) +
617                    Objects.hashCode(mCallerDisplayName) +
618                    Objects.hashCode(mCallerDisplayNamePresentation) +
619                    Objects.hashCode(mAccountHandle) +
620                    Objects.hashCode(mCallCapabilities) +
621                    Objects.hashCode(mCallProperties) +
622                    Objects.hashCode(mDisconnectCause) +
623                    Objects.hashCode(mConnectTimeMillis) +
624                    Objects.hashCode(mGatewayInfo) +
625                    Objects.hashCode(mVideoState) +
626                    Objects.hashCode(mStatusHints) +
627                    Objects.hashCode(mExtras) +
628                    Objects.hashCode(mIntentExtras);
629        }
630
631        /** {@hide} */
632        public Details(
633                String telecomCallId,
634                Uri handle,
635                int handlePresentation,
636                String callerDisplayName,
637                int callerDisplayNamePresentation,
638                PhoneAccountHandle accountHandle,
639                int capabilities,
640                int properties,
641                DisconnectCause disconnectCause,
642                long connectTimeMillis,
643                GatewayInfo gatewayInfo,
644                int videoState,
645                StatusHints statusHints,
646                Bundle extras,
647                Bundle intentExtras) {
648            mTelecomCallId = telecomCallId;
649            mHandle = handle;
650            mHandlePresentation = handlePresentation;
651            mCallerDisplayName = callerDisplayName;
652            mCallerDisplayNamePresentation = callerDisplayNamePresentation;
653            mAccountHandle = accountHandle;
654            mCallCapabilities = capabilities;
655            mCallProperties = properties;
656            mDisconnectCause = disconnectCause;
657            mConnectTimeMillis = connectTimeMillis;
658            mGatewayInfo = gatewayInfo;
659            mVideoState = videoState;
660            mStatusHints = statusHints;
661            mExtras = extras;
662            mIntentExtras = intentExtras;
663        }
664
665        /** {@hide} */
666        public static Details createFromParcelableCall(ParcelableCall parcelableCall) {
667            return new Details(
668                    parcelableCall.getId(),
669                    parcelableCall.getHandle(),
670                    parcelableCall.getHandlePresentation(),
671                    parcelableCall.getCallerDisplayName(),
672                    parcelableCall.getCallerDisplayNamePresentation(),
673                    parcelableCall.getAccountHandle(),
674                    parcelableCall.getCapabilities(),
675                    parcelableCall.getProperties(),
676                    parcelableCall.getDisconnectCause(),
677                    parcelableCall.getConnectTimeMillis(),
678                    parcelableCall.getGatewayInfo(),
679                    parcelableCall.getVideoState(),
680                    parcelableCall.getStatusHints(),
681                    parcelableCall.getExtras(),
682                    parcelableCall.getIntentExtras());
683        }
684
685        @Override
686        public String toString() {
687            StringBuilder sb = new StringBuilder();
688            sb.append("[pa: ");
689            sb.append(mAccountHandle);
690            sb.append(", hdl: ");
691            sb.append(Log.pii(mHandle));
692            sb.append(", caps: ");
693            sb.append(capabilitiesToString(mCallCapabilities));
694            sb.append(", props: ");
695            sb.append(propertiesToString(mCallProperties));
696            sb.append("]");
697            return sb.toString();
698        }
699    }
700
701    public static abstract class Callback {
702        /**
703         * Invoked when the state of this {@code Call} has changed. See {@link #getState()}.
704         *
705         * @param call The {@code Call} invoking this method.
706         * @param state The new state of the {@code Call}.
707         */
708        public void onStateChanged(Call call, int state) {}
709
710        /**
711         * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}.
712         *
713         * @param call The {@code Call} invoking this method.
714         * @param parent The new parent of the {@code Call}.
715         */
716        public void onParentChanged(Call call, Call parent) {}
717
718        /**
719         * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}.
720         *
721         * @param call The {@code Call} invoking this method.
722         * @param children The new children of the {@code Call}.
723         */
724        public void onChildrenChanged(Call call, List<Call> children) {}
725
726        /**
727         * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}.
728         *
729         * @param call The {@code Call} invoking this method.
730         * @param details A {@code Details} object describing the {@code Call}.
731         */
732        public void onDetailsChanged(Call call, Details details) {}
733
734        /**
735         * Invoked when the text messages that can be used as responses to the incoming
736         * {@code Call} are loaded from the relevant database.
737         * See {@link #getCannedTextResponses()}.
738         *
739         * @param call The {@code Call} invoking this method.
740         * @param cannedTextResponses The text messages useable as responses.
741         */
742        public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {}
743
744        /**
745         * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause
746         * character. This causes the post-dial signals to stop pending user confirmation. An
747         * implementation should present this choice to the user and invoke
748         * {@link #postDialContinue(boolean)} when the user makes the choice.
749         *
750         * @param call The {@code Call} invoking this method.
751         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
752         */
753        public void onPostDialWait(Call call, String remainingPostDialSequence) {}
754
755        /**
756         * Invoked when the {@code Call.VideoCall} of the {@code Call} has changed.
757         *
758         * @param call The {@code Call} invoking this method.
759         * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
760         */
761        public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
762
763        /**
764         * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning
765         * up their UI for the {@code Call} in response to state transitions. Specifically,
766         * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of
767         * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather,
768         * clients should wait for this method to be invoked.
769         *
770         * @param call The {@code Call} being destroyed.
771         */
772        public void onCallDestroyed(Call call) {}
773
774        /**
775         * Invoked upon changes to the set of {@code Call}s with which this {@code Call} can be
776         * conferenced.
777         *
778         * @param call The {@code Call} being updated.
779         * @param conferenceableCalls The {@code Call}s with which this {@code Call} can be
780         *          conferenced.
781         */
782        public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) {}
783
784        /**
785         * Invoked when a call receives an event from its associated {@link Connection}.
786         * <p>
787         * See {@link Connection#sendConnectionEvent(String, Bundle)}.
788         *
789         * @param call The {@code Call} receiving the event.
790         * @param event The event.
791         * @param extras Extras associated with the connection event.
792         */
793        public void onConnectionEvent(Call call, String event, Bundle extras) {}
794    }
795
796    /**
797     * @deprecated Use {@code Call.Callback} instead.
798     * @hide
799     */
800    @Deprecated
801    @SystemApi
802    public static abstract class Listener extends Callback { }
803
804    private final Phone mPhone;
805    private final String mTelecomCallId;
806    private final InCallAdapter mInCallAdapter;
807    private final List<String> mChildrenIds = new ArrayList<>();
808    private final List<Call> mChildren = new ArrayList<>();
809    private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren);
810    private final List<CallbackRecord<Callback>> mCallbackRecords = new CopyOnWriteArrayList<>();
811    private final List<Call> mConferenceableCalls = new ArrayList<>();
812    private final List<Call> mUnmodifiableConferenceableCalls =
813            Collections.unmodifiableList(mConferenceableCalls);
814
815    private boolean mChildrenCached;
816    private String mParentId = null;
817    private int mState;
818    private List<String> mCannedTextResponses = null;
819    private String mRemainingPostDialSequence;
820    private VideoCallImpl mVideoCallImpl;
821    private Details mDetails;
822    private Bundle mExtras;
823
824    /**
825     * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any.
826     *
827     * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence
828     * remaining or this {@code Call} is not in a post-dial state.
829     */
830    public String getRemainingPostDialSequence() {
831        return mRemainingPostDialSequence;
832    }
833
834    /**
835     * Instructs this {@link #STATE_RINGING} {@code Call} to answer.
836     * @param videoState The video state in which to answer the call.
837     */
838    public void answer(int videoState) {
839        mInCallAdapter.answerCall(mTelecomCallId, videoState);
840    }
841
842    /**
843     * Instructs this {@link #STATE_RINGING} {@code Call} to reject.
844     *
845     * @param rejectWithMessage Whether to reject with a text message.
846     * @param textMessage An optional text message with which to respond.
847     */
848    public void reject(boolean rejectWithMessage, String textMessage) {
849        mInCallAdapter.rejectCall(mTelecomCallId, rejectWithMessage, textMessage);
850    }
851
852    /**
853     * Instructs this {@code Call} to disconnect.
854     */
855    public void disconnect() {
856        mInCallAdapter.disconnectCall(mTelecomCallId);
857    }
858
859    /**
860     * Instructs this {@code Call} to go on hold.
861     */
862    public void hold() {
863        mInCallAdapter.holdCall(mTelecomCallId);
864    }
865
866    /**
867     * Instructs this {@link #STATE_HOLDING} call to release from hold.
868     */
869    public void unhold() {
870        mInCallAdapter.unholdCall(mTelecomCallId);
871    }
872
873    /**
874     * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone.
875     *
876     * Any other currently playing DTMF tone in the specified call is immediately stopped.
877     *
878     * @param digit A character representing the DTMF digit for which to play the tone. This
879     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
880     */
881    public void playDtmfTone(char digit) {
882        mInCallAdapter.playDtmfTone(mTelecomCallId, digit);
883    }
884
885    /**
886     * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone
887     * currently playing.
888     *
889     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
890     * currently playing, this method will do nothing.
891     */
892    public void stopDtmfTone() {
893        mInCallAdapter.stopDtmfTone(mTelecomCallId);
894    }
895
896    /**
897     * Instructs this {@code Call} to continue playing a post-dial DTMF string.
898     *
899     * A post-dial DTMF string is a string of digits entered after a phone number, when dialed,
900     * that are immediately sent as DTMF tones to the recipient as soon as the connection is made.
901     *
902     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
903     * {@code Call} will temporarily pause playing the tones for a pre-defined period of time.
904     *
905     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
906     * {@code Call} will pause playing the tones and notify callbacks via
907     * {@link Callback#onPostDialWait(Call, String)}. At this point, the in-call app
908     * should display to the user an indication of this state and an affordance to continue
909     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
910     * app should invoke the {@link #postDialContinue(boolean)} method.
911     *
912     * @param proceed Whether or not to continue with the post-dial sequence.
913     */
914    public void postDialContinue(boolean proceed) {
915        mInCallAdapter.postDialContinue(mTelecomCallId, proceed);
916    }
917
918    /**
919     * Notifies this {@code Call} that an account has been selected and to proceed with placing
920     * an outgoing call. Optionally sets this account as the default account.
921     */
922    public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) {
923        mInCallAdapter.phoneAccountSelected(mTelecomCallId, accountHandle, setDefault);
924
925    }
926
927    /**
928     * Instructs this {@code Call} to enter a conference.
929     *
930     * @param callToConferenceWith The other call with which to conference.
931     */
932    public void conference(Call callToConferenceWith) {
933        if (callToConferenceWith != null) {
934            mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
935        }
936    }
937
938    /**
939     * Instructs this {@code Call} to split from any conference call with which it may be
940     * connected.
941     */
942    public void splitFromConference() {
943        mInCallAdapter.splitFromConference(mTelecomCallId);
944    }
945
946    /**
947     * Merges the calls within this conference. See {@link Details#CAPABILITY_MERGE_CONFERENCE}.
948     */
949    public void mergeConference() {
950        mInCallAdapter.mergeConference(mTelecomCallId);
951    }
952
953    /**
954     * Swaps the calls within this conference. See {@link Details#CAPABILITY_SWAP_CONFERENCE}.
955     */
956    public void swapConference() {
957        mInCallAdapter.swapConference(mTelecomCallId);
958    }
959
960    /**
961     * Initiates a request to the {@link ConnectionService} to pull an external call to the local
962     * device.
963     * <p>
964     * Calls to this method are ignored if the call does not have the
965     * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} property set.
966     * <p>
967     * An {@link InCallService} will only see calls which support this method if it has the
968     * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true}
969     * in its manifest.
970     */
971    public void pullExternalCall() {
972        // If this isn't an external call, ignore the request.
973        if (!mDetails.hasProperty(Details.PROPERTY_IS_EXTERNAL_CALL)) {
974            return;
975        }
976
977        mInCallAdapter.pullExternalCall(mTelecomCallId);
978    }
979
980    /**
981     * Sends a {@code Call} event from this {@code Call} to the associated {@link Connection} in
982     * the {@link ConnectionService}.
983     * <p>
984     * Events are exposed to {@link ConnectionService} implementations via
985     * {@link android.telecom.Connection#onCallEvent(String, Bundle)}.
986     * <p>
987     * No assumptions should be made as to how a {@link ConnectionService} will handle these events.
988     * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
989     *
990     * @param event The connection event.
991     * @param extras Bundle containing extra information associated with the event.
992     */
993    public void sendCallEvent(String event, Bundle extras) {
994        mInCallAdapter.sendCallEvent(mTelecomCallId, event, extras);
995    }
996
997    /**
998     * Adds some extras to this {@link Call}.  Existing keys are replaced and new ones are
999     * added.
1000     * <p>
1001     * No assumptions should be made as to how an In-Call UI or service will handle these
1002     * extras.  Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1003     *
1004     * @param extras The extras to add.
1005     */
1006    public final void putExtras(Bundle extras) {
1007        if (extras == null) {
1008            return;
1009        }
1010
1011        if (mExtras == null) {
1012            mExtras = new Bundle();
1013        }
1014        mExtras.putAll(extras);
1015        mInCallAdapter.putExtras(mTelecomCallId, extras);
1016    }
1017
1018    /**
1019     * Adds a boolean extra to this {@link Call}.
1020     *
1021     * @param key The extra key.
1022     * @param value The value.
1023     * @hide
1024     */
1025    public final void putExtra(String key, boolean value) {
1026        if (mExtras == null) {
1027            mExtras = new Bundle();
1028        }
1029        mExtras.putBoolean(key, value);
1030        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1031    }
1032
1033    /**
1034     * Adds an integer extra to this {@link Call}.
1035     *
1036     * @param key The extra key.
1037     * @param value The value.
1038     * @hide
1039     */
1040    public final void putExtra(String key, int value) {
1041        if (mExtras == null) {
1042            mExtras = new Bundle();
1043        }
1044        mExtras.putInt(key, value);
1045        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1046    }
1047
1048    /**
1049     * Adds a string extra to this {@link Call}.
1050     *
1051     * @param key The extra key.
1052     * @param value The value.
1053     * @hide
1054     */
1055    public final void putExtra(String key, String value) {
1056        if (mExtras == null) {
1057            mExtras = new Bundle();
1058        }
1059        mExtras.putString(key, value);
1060        mInCallAdapter.putExtra(mTelecomCallId, key, value);
1061    }
1062
1063    /**
1064     * Removes extras from this {@link Call}.
1065     *
1066     * @param keys The keys of the extras to remove.
1067     */
1068    public final void removeExtras(List<String> keys) {
1069        if (mExtras != null) {
1070            for (String key : keys) {
1071                mExtras.remove(key);
1072            }
1073            if (mExtras.size() == 0) {
1074                mExtras = null;
1075            }
1076        }
1077        mInCallAdapter.removeExtras(mTelecomCallId, keys);
1078    }
1079
1080    /**
1081     * Removes extras from this {@link Call}.
1082     *
1083     * @param keys The keys of the extras to remove.
1084     */
1085    public final void removeExtras(String ... keys) {
1086        removeExtras(Arrays.asList(keys));
1087    }
1088
1089    /**
1090     * Obtains the parent of this {@code Call} in a conference, if any.
1091     *
1092     * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a
1093     * child of any conference {@code Call}s.
1094     */
1095    public Call getParent() {
1096        if (mParentId != null) {
1097            return mPhone.internalGetCallByTelecomId(mParentId);
1098        }
1099        return null;
1100    }
1101
1102    /**
1103     * Obtains the children of this conference {@code Call}, if any.
1104     *
1105     * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty
1106     * {@code List} otherwise.
1107     */
1108    public List<Call> getChildren() {
1109        if (!mChildrenCached) {
1110            mChildrenCached = true;
1111            mChildren.clear();
1112
1113            for(String id : mChildrenIds) {
1114                Call call = mPhone.internalGetCallByTelecomId(id);
1115                if (call == null) {
1116                    // At least one child was still not found, so do not save true for "cached"
1117                    mChildrenCached = false;
1118                } else {
1119                    mChildren.add(call);
1120                }
1121            }
1122        }
1123
1124        return mUnmodifiableChildren;
1125    }
1126
1127    /**
1128     * Returns the list of {@code Call}s with which this {@code Call} is allowed to conference.
1129     *
1130     * @return The list of conferenceable {@code Call}s.
1131     */
1132    public List<Call> getConferenceableCalls() {
1133        return mUnmodifiableConferenceableCalls;
1134    }
1135
1136    /**
1137     * Obtains the state of this {@code Call}.
1138     *
1139     * @return A state value, chosen from the {@code STATE_*} constants.
1140     */
1141    public int getState() {
1142        return mState;
1143    }
1144
1145    /**
1146     * Obtains a list of canned, pre-configured message responses to present to the user as
1147     * ways of rejecting this {@code Call} using via a text message.
1148     *
1149     * @see #reject(boolean, String)
1150     *
1151     * @return A list of canned text message responses.
1152     */
1153    public List<String> getCannedTextResponses() {
1154        return mCannedTextResponses;
1155    }
1156
1157    /**
1158     * Obtains an object that can be used to display video from this {@code Call}.
1159     *
1160     * @return An {@code Call.VideoCall}.
1161     */
1162    public InCallService.VideoCall getVideoCall() {
1163        return mVideoCallImpl;
1164    }
1165
1166    /**
1167     * Obtains an object containing call details.
1168     *
1169     * @return A {@link Details} object. Depending on the state of the {@code Call}, the
1170     * result may be {@code null}.
1171     */
1172    public Details getDetails() {
1173        return mDetails;
1174    }
1175
1176    /**
1177     * Registers a callback to this {@code Call}.
1178     *
1179     * @param callback A {@code Callback}.
1180     */
1181    public void registerCallback(Callback callback) {
1182        registerCallback(callback, new Handler());
1183    }
1184
1185    /**
1186     * Registers a callback to this {@code Call}.
1187     *
1188     * @param callback A {@code Callback}.
1189     * @param handler A handler which command and status changes will be delivered to.
1190     */
1191    public void registerCallback(Callback callback, Handler handler) {
1192        unregisterCallback(callback);
1193        // Don't allow new callback registration if the call is already being destroyed.
1194        if (callback != null && handler != null && mState != STATE_DISCONNECTED) {
1195            mCallbackRecords.add(new CallbackRecord<Callback>(callback, handler));
1196        }
1197    }
1198
1199    /**
1200     * Unregisters a callback from this {@code Call}.
1201     *
1202     * @param callback A {@code Callback}.
1203     */
1204    public void unregisterCallback(Callback callback) {
1205        // Don't allow callback deregistration if the call is already being destroyed.
1206        if (callback != null && mState != STATE_DISCONNECTED) {
1207            for (CallbackRecord<Callback> record : mCallbackRecords) {
1208                if (record.getCallback() == callback) {
1209                    mCallbackRecords.remove(record);
1210                    break;
1211                }
1212            }
1213        }
1214    }
1215
1216    @Override
1217    public String toString() {
1218        return new StringBuilder().
1219                append("Call [id: ").
1220                append(mTelecomCallId).
1221                append(", state: ").
1222                append(stateToString(mState)).
1223                append(", details: ").
1224                append(mDetails).
1225                append("]").toString();
1226    }
1227
1228    /**
1229     * @param state An integer value of a {@code STATE_*} constant.
1230     * @return A string representation of the value.
1231     */
1232    private static String stateToString(int state) {
1233        switch (state) {
1234            case STATE_NEW:
1235                return "NEW";
1236            case STATE_RINGING:
1237                return "RINGING";
1238            case STATE_DIALING:
1239                return "DIALING";
1240            case STATE_ACTIVE:
1241                return "ACTIVE";
1242            case STATE_HOLDING:
1243                return "HOLDING";
1244            case STATE_DISCONNECTED:
1245                return "DISCONNECTED";
1246            case STATE_CONNECTING:
1247                return "CONNECTING";
1248            case STATE_DISCONNECTING:
1249                return "DISCONNECTING";
1250            case STATE_SELECT_PHONE_ACCOUNT:
1251                return "SELECT_PHONE_ACCOUNT";
1252            default:
1253                Log.w(Call.class, "Unknown state %d", state);
1254                return "UNKNOWN";
1255        }
1256    }
1257
1258    /**
1259     * Adds a listener to this {@code Call}.
1260     *
1261     * @param listener A {@code Listener}.
1262     * @deprecated Use {@link #registerCallback} instead.
1263     * @hide
1264     */
1265    @Deprecated
1266    @SystemApi
1267    public void addListener(Listener listener) {
1268        registerCallback(listener);
1269    }
1270
1271    /**
1272     * Removes a listener from this {@code Call}.
1273     *
1274     * @param listener A {@code Listener}.
1275     * @deprecated Use {@link #unregisterCallback} instead.
1276     * @hide
1277     */
1278    @Deprecated
1279    @SystemApi
1280    public void removeListener(Listener listener) {
1281        unregisterCallback(listener);
1282    }
1283
1284    /** {@hide} */
1285    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
1286        mPhone = phone;
1287        mTelecomCallId = telecomCallId;
1288        mInCallAdapter = inCallAdapter;
1289        mState = STATE_NEW;
1290    }
1291
1292    /** {@hide} */
1293    Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
1294        mPhone = phone;
1295        mTelecomCallId = telecomCallId;
1296        mInCallAdapter = inCallAdapter;
1297        mState = state;
1298    }
1299
1300    /** {@hide} */
1301    final String internalGetCallId() {
1302        return mTelecomCallId;
1303    }
1304
1305    /** {@hide} */
1306    final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) {
1307        // First, we update the internal state as far as possible before firing any updates.
1308        Details details = Details.createFromParcelableCall(parcelableCall);
1309        boolean detailsChanged = !Objects.equals(mDetails, details);
1310        if (detailsChanged) {
1311            mDetails = details;
1312        }
1313
1314        boolean cannedTextResponsesChanged = false;
1315        if (mCannedTextResponses == null && parcelableCall.getCannedSmsResponses() != null
1316                && !parcelableCall.getCannedSmsResponses().isEmpty()) {
1317            mCannedTextResponses =
1318                    Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
1319            cannedTextResponsesChanged = true;
1320        }
1321
1322        VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
1323        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
1324                !Objects.equals(mVideoCallImpl, newVideoCallImpl);
1325        if (videoCallChanged) {
1326            mVideoCallImpl = newVideoCallImpl;
1327        }
1328        if (mVideoCallImpl != null) {
1329            mVideoCallImpl.setVideoState(getDetails().getVideoState());
1330        }
1331
1332        int state = parcelableCall.getState();
1333        boolean stateChanged = mState != state;
1334        if (stateChanged) {
1335            mState = state;
1336        }
1337
1338        String parentId = parcelableCall.getParentCallId();
1339        boolean parentChanged = !Objects.equals(mParentId, parentId);
1340        if (parentChanged) {
1341            mParentId = parentId;
1342        }
1343
1344        List<String> childCallIds = parcelableCall.getChildCallIds();
1345        boolean childrenChanged = !Objects.equals(childCallIds, mChildrenIds);
1346        if (childrenChanged) {
1347            mChildrenIds.clear();
1348            mChildrenIds.addAll(parcelableCall.getChildCallIds());
1349            mChildrenCached = false;
1350        }
1351
1352        List<String> conferenceableCallIds = parcelableCall.getConferenceableCallIds();
1353        List<Call> conferenceableCalls = new ArrayList<Call>(conferenceableCallIds.size());
1354        for (String otherId : conferenceableCallIds) {
1355            if (callIdMap.containsKey(otherId)) {
1356                conferenceableCalls.add(callIdMap.get(otherId));
1357            }
1358        }
1359
1360        if (!Objects.equals(mConferenceableCalls, conferenceableCalls)) {
1361            mConferenceableCalls.clear();
1362            mConferenceableCalls.addAll(conferenceableCalls);
1363            fireConferenceableCallsChanged();
1364        }
1365
1366        // Now we fire updates, ensuring that any client who listens to any of these notifications
1367        // gets the most up-to-date state.
1368
1369        if (stateChanged) {
1370            fireStateChanged(mState);
1371        }
1372        if (detailsChanged) {
1373            fireDetailsChanged(mDetails);
1374        }
1375        if (cannedTextResponsesChanged) {
1376            fireCannedTextResponsesLoaded(mCannedTextResponses);
1377        }
1378        if (videoCallChanged) {
1379            fireVideoCallChanged(mVideoCallImpl);
1380        }
1381        if (parentChanged) {
1382            fireParentChanged(getParent());
1383        }
1384        if (childrenChanged) {
1385            fireChildrenChanged(getChildren());
1386        }
1387
1388        // If we have transitioned to DISCONNECTED, that means we need to notify clients and
1389        // remove ourselves from the Phone. Note that we do this after completing all state updates
1390        // so a client can cleanly transition all their UI to the state appropriate for a
1391        // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list.
1392        if (mState == STATE_DISCONNECTED) {
1393            fireCallDestroyed();
1394        }
1395    }
1396
1397    /** {@hide} */
1398    final void internalSetPostDialWait(String remaining) {
1399        mRemainingPostDialSequence = remaining;
1400        firePostDialWait(mRemainingPostDialSequence);
1401    }
1402
1403    /** {@hide} */
1404    final void internalSetDisconnected() {
1405        if (mState != Call.STATE_DISCONNECTED) {
1406            mState = Call.STATE_DISCONNECTED;
1407            fireStateChanged(mState);
1408            fireCallDestroyed();
1409        }
1410    }
1411
1412    /** {@hide} */
1413    final void internalOnConnectionEvent(String event, Bundle extras) {
1414        fireOnConnectionEvent(event, extras);
1415    }
1416
1417    private void fireStateChanged(final int newState) {
1418        for (CallbackRecord<Callback> record : mCallbackRecords) {
1419            final Call call = this;
1420            final Callback callback = record.getCallback();
1421            record.getHandler().post(new Runnable() {
1422                @Override
1423                public void run() {
1424                    callback.onStateChanged(call, newState);
1425                }
1426            });
1427        }
1428    }
1429
1430    private void fireParentChanged(final Call newParent) {
1431        for (CallbackRecord<Callback> record : mCallbackRecords) {
1432            final Call call = this;
1433            final Callback callback = record.getCallback();
1434            record.getHandler().post(new Runnable() {
1435                @Override
1436                public void run() {
1437                    callback.onParentChanged(call, newParent);
1438                }
1439            });
1440        }
1441    }
1442
1443    private void fireChildrenChanged(final List<Call> children) {
1444        for (CallbackRecord<Callback> record : mCallbackRecords) {
1445            final Call call = this;
1446            final Callback callback = record.getCallback();
1447            record.getHandler().post(new Runnable() {
1448                @Override
1449                public void run() {
1450                    callback.onChildrenChanged(call, children);
1451                }
1452            });
1453        }
1454    }
1455
1456    private void fireDetailsChanged(final Details details) {
1457        for (CallbackRecord<Callback> record : mCallbackRecords) {
1458            final Call call = this;
1459            final Callback callback = record.getCallback();
1460            record.getHandler().post(new Runnable() {
1461                @Override
1462                public void run() {
1463                    callback.onDetailsChanged(call, details);
1464                }
1465            });
1466        }
1467    }
1468
1469    private void fireCannedTextResponsesLoaded(final List<String> cannedTextResponses) {
1470        for (CallbackRecord<Callback> record : mCallbackRecords) {
1471            final Call call = this;
1472            final Callback callback = record.getCallback();
1473            record.getHandler().post(new Runnable() {
1474                @Override
1475                public void run() {
1476                    callback.onCannedTextResponsesLoaded(call, cannedTextResponses);
1477                }
1478            });
1479        }
1480    }
1481
1482    private void fireVideoCallChanged(final InCallService.VideoCall videoCall) {
1483        for (CallbackRecord<Callback> record : mCallbackRecords) {
1484            final Call call = this;
1485            final Callback callback = record.getCallback();
1486            record.getHandler().post(new Runnable() {
1487                @Override
1488                public void run() {
1489                    callback.onVideoCallChanged(call, videoCall);
1490                }
1491            });
1492        }
1493    }
1494
1495    private void firePostDialWait(final String remainingPostDialSequence) {
1496        for (CallbackRecord<Callback> record : mCallbackRecords) {
1497            final Call call = this;
1498            final Callback callback = record.getCallback();
1499            record.getHandler().post(new Runnable() {
1500                @Override
1501                public void run() {
1502                    callback.onPostDialWait(call, remainingPostDialSequence);
1503                }
1504            });
1505        }
1506    }
1507
1508    private void fireCallDestroyed() {
1509        /**
1510         * To preserve the ordering of the Call's onCallDestroyed callback and Phone's
1511         * onCallRemoved callback, we remove this call from the Phone's record
1512         * only once all of the registered onCallDestroyed callbacks are executed.
1513         * All the callbacks get removed from our records as a part of this operation
1514         * since onCallDestroyed is the final callback.
1515         */
1516        final Call call = this;
1517        if (mCallbackRecords.isEmpty()) {
1518            // No callbacks registered, remove the call from Phone's record.
1519            mPhone.internalRemoveCall(call);
1520        }
1521        for (final CallbackRecord<Callback> record : mCallbackRecords) {
1522            final Callback callback = record.getCallback();
1523            record.getHandler().post(new Runnable() {
1524                @Override
1525                public void run() {
1526                    boolean isFinalRemoval = false;
1527                    RuntimeException toThrow = null;
1528                    try {
1529                        callback.onCallDestroyed(call);
1530                    } catch (RuntimeException e) {
1531                            toThrow = e;
1532                    }
1533                    synchronized(Call.this) {
1534                        mCallbackRecords.remove(record);
1535                        if (mCallbackRecords.isEmpty()) {
1536                            isFinalRemoval = true;
1537                        }
1538                    }
1539                    if (isFinalRemoval) {
1540                        mPhone.internalRemoveCall(call);
1541                    }
1542                    if (toThrow != null) {
1543                        throw toThrow;
1544                    }
1545                }
1546            });
1547        }
1548    }
1549
1550    private void fireConferenceableCallsChanged() {
1551        for (CallbackRecord<Callback> record : mCallbackRecords) {
1552            final Call call = this;
1553            final Callback callback = record.getCallback();
1554            record.getHandler().post(new Runnable() {
1555                @Override
1556                public void run() {
1557                    callback.onConferenceableCallsChanged(call, mUnmodifiableConferenceableCalls);
1558                }
1559            });
1560        }
1561    }
1562
1563    /**
1564     * Notifies listeners of an incoming connection event.
1565     * <p>
1566     * Connection events are issued via {@link Connection#sendConnectionEvent(String, Bundle)}.
1567     *
1568     * @param event
1569     * @param extras
1570     */
1571    private void fireOnConnectionEvent(final String event, final Bundle extras) {
1572        for (CallbackRecord<Callback> record : mCallbackRecords) {
1573            final Call call = this;
1574            final Callback callback = record.getCallback();
1575            record.getHandler().post(new Runnable() {
1576                @Override
1577                public void run() {
1578                    callback.onConnectionEvent(call, event, extras);
1579                }
1580            });
1581        }
1582    }
1583
1584    /**
1585     * Determines if two bundles are equal.
1586     *
1587     * @param bundle The original bundle.
1588     * @param newBundle The bundle to compare with.
1589     * @retrun {@code true} if the bundles are equal, {@code false} otherwise.
1590     */
1591    private static boolean areBundlesEqual(Bundle bundle, Bundle newBundle) {
1592        if (bundle == null || newBundle == null) {
1593            return bundle == newBundle;
1594        }
1595
1596        if (bundle.size() != newBundle.size()) {
1597            return false;
1598        }
1599
1600        for(String key : bundle.keySet()) {
1601            if (key != null) {
1602                final Object value = bundle.get(key);
1603                final Object newValue = newBundle.get(key);
1604                if (!Objects.equals(value, newValue)) {
1605                    return false;
1606                }
1607            }
1608        }
1609        return true;
1610    }
1611}
1612