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