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