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