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 com.android.internal.os.SomeArgs;
20 import com.android.internal.telecom.IVideoCallback;
21 import com.android.internal.telecom.IVideoProvider;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SystemApi;
26 import android.hardware.camera2.CameraManager;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.os.IBinder;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.util.ArraySet;
35 import android.view.Surface;
36 
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.List;
40 import java.util.Set;
41 import java.util.concurrent.ConcurrentHashMap;
42 
43 /**
44  * Represents a phone call or connection to a remote endpoint that carries voice and/or video
45  * traffic.
46  * <p>
47  * Implementations create a custom subclass of {@code Connection} and return it to the framework
48  * as the return value of
49  * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
50  * or
51  * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
52  * Implementations are then responsible for updating the state of the {@code Connection}, and
53  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
54  * longer used and associated resources may be recovered.
55  */
56 public abstract class Connection extends Conferenceable {
57 
58     /**
59      * The connection is initializing. This is generally the first state for a {@code Connection}
60      * returned by a {@link ConnectionService}.
61      */
62     public static final int STATE_INITIALIZING = 0;
63 
64     /**
65      * The connection is new and not connected.
66      */
67     public static final int STATE_NEW = 1;
68 
69     /**
70      * An incoming connection is in the ringing state. During this state, the user's ringer or
71      * vibration feature will be activated.
72      */
73     public static final int STATE_RINGING = 2;
74 
75     /**
76      * An outgoing connection is in the dialing state. In this state the other party has not yet
77      * answered the call and the user traditionally hears a ringback tone.
78      */
79     public static final int STATE_DIALING = 3;
80 
81     /**
82      * A connection is active. Both parties are connected to the call and can actively communicate.
83      */
84     public static final int STATE_ACTIVE = 4;
85 
86     /**
87      * A connection is on hold.
88      */
89     public static final int STATE_HOLDING = 5;
90 
91     /**
92      * A connection has been disconnected. This is the final state once the user has been
93      * disconnected from a call either locally, remotely or by an error in the service.
94      */
95     public static final int STATE_DISCONNECTED = 6;
96 
97     /**
98      * The state of an external connection which is in the process of being pulled from a remote
99      * device to the local device.
100      * <p>
101      * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
102      * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
103      * @hide
104      */
105     public static final int STATE_PULLING_CALL = 7;
106 
107     /**
108      * Connection can currently be put on hold or unheld. This is distinct from
109      * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
110      * it does not at the moment support the function. This can be true while the call is in the
111      * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
112      * display a disabled 'hold' button.
113      */
114     public static final int CAPABILITY_HOLD = 0x00000001;
115 
116     /** Connection supports the hold feature. */
117     public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
118 
119     /**
120      * Connections within a conference can be merged. A {@link ConnectionService} has the option to
121      * add a {@link Conference} before the child {@link Connection}s are merged. This is how
122      * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
123      * capability allows a merge button to be shown while the conference is in the foreground
124      * of the in-call UI.
125      * <p>
126      * This is only intended for use by a {@link Conference}.
127      */
128     public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
129 
130     /**
131      * Connections within a conference can be swapped between foreground and background.
132      * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
133      * <p>
134      * This is only intended for use by a {@link Conference}.
135      */
136     public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
137 
138     /**
139      * @hide
140      */
141     public static final int CAPABILITY_UNUSED = 0x00000010;
142 
143     /** Connection supports responding via text option. */
144     public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
145 
146     /** Connection can be muted. */
147     public static final int CAPABILITY_MUTE = 0x00000040;
148 
149     /**
150      * Connection supports conference management. This capability only applies to
151      * {@link Conference}s which can have {@link Connection}s as children.
152      */
153     public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
154 
155     /**
156      * Local device supports receiving video.
157      */
158     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
159 
160     /**
161      * Local device supports transmitting video.
162      */
163     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
164 
165     /**
166      * Local device supports bidirectional video calling.
167      */
168     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
169             CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
170 
171     /**
172      * Remote device supports receiving video.
173      */
174     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
175 
176     /**
177      * Remote device supports transmitting video.
178      */
179     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
180 
181     /**
182      * Remote device supports bidirectional video calling.
183      */
184     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
185             CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
186 
187     /**
188      * Connection is able to be separated from its parent {@code Conference}, if any.
189      */
190     public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
191 
192     /**
193      * Connection is able to be individually disconnected when in a {@code Conference}.
194      */
195     public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
196 
197     /**
198      * Un-used.
199      * @hide
200      */
201     public static final int CAPABILITY_UNUSED_2 = 0x00004000;
202 
203     /**
204      * Un-used.
205      * @hide
206      */
207     public static final int CAPABILITY_UNUSED_3 = 0x00008000;
208 
209     /**
210      * Un-used.
211      * @hide
212      */
213     public static final int CAPABILITY_UNUSED_4 = 0x00010000;
214 
215     /**
216      * Un-used.
217      * @hide
218      */
219     public static final int CAPABILITY_UNUSED_5 = 0x00020000;
220 
221     /**
222      * Speed up audio setup for MT call.
223      * @hide
224      */
225     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
226 
227     /**
228      * Call can be upgraded to a video call.
229      */
230     public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
231 
232     /**
233      * For video calls, indicates whether the outgoing video for the call can be paused using
234      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
235      */
236     public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
237 
238     /**
239      * For a conference, indicates the conference will not have child connections.
240      * <p>
241      * An example of a conference with child connections is a GSM conference call, where the radio
242      * retains connections to the individual participants of the conference.  Another example is an
243      * IMS conference call where conference event package functionality is supported; in this case
244      * the conference server ensures the radio is aware of the participants in the conference, which
245      * are represented by child connections.
246      * <p>
247      * An example of a conference with no child connections is an IMS conference call with no
248      * conference event package support.  Such a conference is represented by the radio as a single
249      * connection to the IMS conference server.
250      * <p>
251      * Indicating whether a conference has children or not is important to help user interfaces
252      * visually represent a conference.  A conference with no children, for example, will have the
253      * conference connection shown in the list of calls on a Bluetooth device, where if the
254      * conference has children, only the children will be shown in the list of calls on a Bluetooth
255      * device.
256      * @hide
257      */
258     public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
259 
260     /**
261      * Indicates that the connection itself wants to handle any sort of reply response, rather than
262      * relying on SMS.
263      */
264     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
265 
266     /**
267      * When set, prevents a video call from being downgraded to an audio-only call.
268      * <p>
269      * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
270      * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
271      * downgraded from a video call back to a VideoState of
272      * {@link VideoProfile#STATE_AUDIO_ONLY}.
273      * <p>
274      * Intuitively, a call which can be downgraded to audio should also have local and remote
275      * video
276      * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
277      * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
278      */
279     public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
280 
281     /**
282      * When set for an external connection, indicates that this {@code Connection} can be pulled
283      * from a remote device to the current device.
284      * <p>
285      * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
286      * is set.
287      * @hide
288      */
289     public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
290 
291     //**********************************************************************************************
292     // Next CAPABILITY value: 0x02000000
293     //**********************************************************************************************
294 
295     /**
296      * Indicates that the current device callback number should be shown.
297      *
298      * @hide
299      */
300     public static final int PROPERTY_SHOW_CALLBACK_NUMBER = 1<<0;
301 
302     /**
303      * Whether the call is a generic conference, where we do not know the precise state of
304      * participants in the conference (eg. on CDMA).
305      *
306      * @hide
307      */
308     public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
309 
310     /**
311      * Connection is using high definition audio.
312      * @hide
313      */
314     public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
315 
316     /**
317      * Connection is using WIFI.
318      * @hide
319      */
320     public static final int PROPERTY_WIFI = 1<<3;
321 
322     /**
323      * When set, indicates that the {@code Connection} does not actually exist locally for the
324      * {@link ConnectionService}.
325      * <p>
326      * Consider, for example, a scenario where a user has two devices with the same phone number.
327      * When a user places a call on one devices, the telephony stack can represent that call on the
328      * other device by adding is to the {@link ConnectionService} with the
329      * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
330      * <p>
331      * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
332      * external connections.  Only those {@link InCallService}s which have the
333      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
334      * manifest will see external connections.
335      * @hide
336      */
337     public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
338 
339 
340     //**********************************************************************************************
341     // Next PROPERTY value: 1<<5
342     //**********************************************************************************************
343 
344     /**
345      * Connection extra key used to store the last forwarded number associated with the current
346      * connection.  Used to communicate to the user interface that the connection was forwarded via
347      * the specified number.
348      */
349     public static final String EXTRA_LAST_FORWARDED_NUMBER =
350             "android.telecom.extra.LAST_FORWARDED_NUMBER";
351 
352     /**
353      * Connection extra key used to store a child number associated with the current connection.
354      * Used to communicate to the user interface that the connection was received via
355      * a child address (i.e. phone number) associated with the {@link PhoneAccount}'s primary
356      * address.
357      */
358     public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
359 
360     /**
361      * Connection extra key used to store the subject for an incoming call.  The user interface can
362      * query this extra and display its contents for incoming calls.  Will only be used if the
363      * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
364      */
365     public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
366 
367     /**
368      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
369      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
370      * {@link #sendConnectionEvent(String)}.
371      * @hide
372      */
373     public static final String EVENT_ON_HOLD_TONE_START =
374             "android.telecom.event.ON_HOLD_TONE_START";
375 
376     /**
377      * Connection event used to inform Telecom that it should stop the on hold tone.  This is used
378      * to stop a tone when the peer puts the current call on hold.  Sent to Telecom via
379      * {@link #sendConnectionEvent(String)}.
380      * @hide
381      */
382     public static final String EVENT_ON_HOLD_TONE_END =
383             "android.telecom.event.ON_HOLD_TONE_END";
384 
385     /**
386      * Connection event used to inform {@link InCallService}s when pulling of an external call has
387      * failed.  The user interface should inform the user of the error.
388      * <p>
389      * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
390      * API is called on a {@link Call} with the properties
391      * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
392      * {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
393      * pull the external call due to an error condition.
394      * @hide
395      */
396     public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
397 
398     // Flag controlling whether PII is emitted into the logs
399     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
400 
401     /**
402      * Whether the given capabilities support the specified capability.
403      *
404      * @param capabilities A capability bit field.
405      * @param capability The capability to check capabilities for.
406      * @return Whether the specified capability is supported.
407      * @hide
408      */
can(int capabilities, int capability)409     public static boolean can(int capabilities, int capability) {
410         return (capabilities & capability) == capability;
411     }
412 
413     /**
414      * Whether the capabilities of this {@code Connection} supports the specified capability.
415      *
416      * @param capability The capability to check capabilities for.
417      * @return Whether the specified capability is supported.
418      * @hide
419      */
can(int capability)420     public boolean can(int capability) {
421         return can(mConnectionCapabilities, capability);
422     }
423 
424     /**
425      * Removes the specified capability from the set of capabilities of this {@code Connection}.
426      *
427      * @param capability The capability to remove from the set.
428      * @hide
429      */
removeCapability(int capability)430     public void removeCapability(int capability) {
431         mConnectionCapabilities &= ~capability;
432     }
433 
434     /**
435      * Adds the specified capability to the set of capabilities of this {@code Connection}.
436      *
437      * @param capability The capability to add to the set.
438      * @hide
439      */
addCapability(int capability)440     public void addCapability(int capability) {
441         mConnectionCapabilities |= capability;
442     }
443 
444 
capabilitiesToString(int capabilities)445     public static String capabilitiesToString(int capabilities) {
446         StringBuilder builder = new StringBuilder();
447         builder.append("[Capabilities:");
448         if (can(capabilities, CAPABILITY_HOLD)) {
449             builder.append(" CAPABILITY_HOLD");
450         }
451         if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
452             builder.append(" CAPABILITY_SUPPORT_HOLD");
453         }
454         if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
455             builder.append(" CAPABILITY_MERGE_CONFERENCE");
456         }
457         if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
458             builder.append(" CAPABILITY_SWAP_CONFERENCE");
459         }
460         if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
461             builder.append(" CAPABILITY_RESPOND_VIA_TEXT");
462         }
463         if (can(capabilities, CAPABILITY_MUTE)) {
464             builder.append(" CAPABILITY_MUTE");
465         }
466         if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
467             builder.append(" CAPABILITY_MANAGE_CONFERENCE");
468         }
469         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
470             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
471         }
472         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
473             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
474         }
475         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
476             builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
477         }
478         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
479             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
480         }
481         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
482             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
483         }
484         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
485             builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
486         }
487         if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
488             builder.append(" CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO");
489         }
490         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
491             builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
492         }
493         if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
494             builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
495         }
496         if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
497             builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
498         }
499         if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
500             builder.append(" CAPABILITY_SINGLE_PARTY_CONFERENCE");
501         }
502         if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
503             builder.append(" CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION");
504         }
505         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
506             builder.append(" CAPABILITY_CAN_PULL_CALL");
507         }
508 
509         builder.append("]");
510         return builder.toString();
511     }
512 
513     /**
514      * Builds a string representation of a properties bit-mask.
515      *
516      * @param properties The properties bit-mask.
517      * @return String representation.
518      * @hide
519      */
propertiesToString(int properties)520     public static String propertiesToString(int properties) {
521         StringBuilder builder = new StringBuilder();
522         builder.append("[Properties:");
523 
524         if (can(properties, PROPERTY_SHOW_CALLBACK_NUMBER)) {
525             builder.append(" PROPERTY_SHOW_CALLBACK_NUMBER");
526         }
527 
528         if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
529             builder.append(" PROPERTY_HIGH_DEF_AUDIO");
530         }
531 
532         if (can(properties, PROPERTY_WIFI)) {
533             builder.append(" PROPERTY_WIFI");
534         }
535 
536         if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
537             builder.append(" PROPERTY_GENERIC_CONFERENCE");
538         }
539 
540         if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
541             builder.append(" PROPERTY_IS_EXTERNAL_CALL");
542         }
543 
544         builder.append("]");
545         return builder.toString();
546     }
547 
548     /** @hide */
549     public abstract static class Listener {
onStateChanged(Connection c, int state)550         public void onStateChanged(Connection c, int state) {}
onAddressChanged(Connection c, Uri newAddress, int presentation)551         public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
onCallerDisplayNameChanged( Connection c, String callerDisplayName, int presentation)552         public void onCallerDisplayNameChanged(
553                 Connection c, String callerDisplayName, int presentation) {}
onVideoStateChanged(Connection c, int videoState)554         public void onVideoStateChanged(Connection c, int videoState) {}
onDisconnected(Connection c, DisconnectCause disconnectCause)555         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
onPostDialWait(Connection c, String remaining)556         public void onPostDialWait(Connection c, String remaining) {}
onPostDialChar(Connection c, char nextChar)557         public void onPostDialChar(Connection c, char nextChar) {}
onRingbackRequested(Connection c, boolean ringback)558         public void onRingbackRequested(Connection c, boolean ringback) {}
onDestroyed(Connection c)559         public void onDestroyed(Connection c) {}
onConnectionCapabilitiesChanged(Connection c, int capabilities)560         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
onConnectionPropertiesChanged(Connection c, int properties)561         public void onConnectionPropertiesChanged(Connection c, int properties) {}
onVideoProviderChanged( Connection c, VideoProvider videoProvider)562         public void onVideoProviderChanged(
563                 Connection c, VideoProvider videoProvider) {}
onAudioModeIsVoipChanged(Connection c, boolean isVoip)564         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
onStatusHintsChanged(Connection c, StatusHints statusHints)565         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
onConferenceablesChanged( Connection c, List<Conferenceable> conferenceables)566         public void onConferenceablesChanged(
567                 Connection c, List<Conferenceable> conferenceables) {}
onConferenceChanged(Connection c, Conference conference)568         public void onConferenceChanged(Connection c, Conference conference) {}
569         /** @hide */
onConferenceParticipantsChanged(Connection c, List<ConferenceParticipant> participants)570         public void onConferenceParticipantsChanged(Connection c,
571                 List<ConferenceParticipant> participants) {}
onConferenceStarted()572         public void onConferenceStarted() {}
onConferenceMergeFailed(Connection c)573         public void onConferenceMergeFailed(Connection c) {}
onExtrasChanged(Connection c, Bundle extras)574         public void onExtrasChanged(Connection c, Bundle extras) {}
onExtrasRemoved(Connection c, List<String> keys)575         public void onExtrasRemoved(Connection c, List<String> keys) {}
onConnectionEvent(Connection c, String event, Bundle extras)576         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
577     }
578 
579     /**
580      * Provides a means of controlling the video session associated with a {@link Connection}.
581      * <p>
582      * Implementations create a custom subclass of {@link VideoProvider} and the
583      * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
584      * {@link Connection#setVideoProvider(VideoProvider)}.  Any connection which supports video
585      * should set the {@link VideoProvider}.
586      * <p>
587      * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
588      * {@link InCallService} implementations to issue requests related to the video session;
589      * it provides a means for the {@link ConnectionService} to report events and information
590      * related to the video session to Telecom and the {@link InCallService} implementations.
591      * <p>
592      * {@link InCallService} implementations interact with the {@link VideoProvider} via
593      * {@link android.telecom.InCallService.VideoCall}.
594      */
595     public static abstract class VideoProvider {
596 
597         /**
598          * Video is not being received (no protocol pause was issued).
599          * @see #handleCallSessionEvent(int)
600          */
601         public static final int SESSION_EVENT_RX_PAUSE = 1;
602 
603         /**
604          * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
605          * @see #handleCallSessionEvent(int)
606          */
607         public static final int SESSION_EVENT_RX_RESUME = 2;
608 
609         /**
610          * Video transmission has begun. This occurs after a negotiated start of video transmission
611          * when the underlying protocol has actually begun transmitting video to the remote party.
612          * @see #handleCallSessionEvent(int)
613          */
614         public static final int SESSION_EVENT_TX_START = 3;
615 
616         /**
617          * Video transmission has stopped. This occurs after a negotiated stop of video transmission
618          * when the underlying protocol has actually stopped transmitting video to the remote party.
619          * @see #handleCallSessionEvent(int)
620          */
621         public static final int SESSION_EVENT_TX_STOP = 4;
622 
623         /**
624          * A camera failure has occurred for the selected camera.  The {@link InCallService} can use
625          * this as a cue to inform the user the camera is not available.
626          * @see #handleCallSessionEvent(int)
627          */
628         public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
629 
630         /**
631          * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
632          * for operation.  The {@link InCallService} can use this as a cue to inform the user that
633          * the camera has become available again.
634          * @see #handleCallSessionEvent(int)
635          */
636         public static final int SESSION_EVENT_CAMERA_READY = 6;
637 
638         /**
639          * Session modify request was successful.
640          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
641          */
642         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
643 
644         /**
645          * Session modify request failed.
646          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
647          */
648         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
649 
650         /**
651          * Session modify request ignored due to invalid parameters.
652          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
653          */
654         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
655 
656         /**
657          * Session modify request timed out.
658          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
659          */
660         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
661 
662         /**
663          * Session modify request rejected by remote user.
664          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
665          */
666         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
667 
668         private static final int MSG_ADD_VIDEO_CALLBACK = 1;
669         private static final int MSG_SET_CAMERA = 2;
670         private static final int MSG_SET_PREVIEW_SURFACE = 3;
671         private static final int MSG_SET_DISPLAY_SURFACE = 4;
672         private static final int MSG_SET_DEVICE_ORIENTATION = 5;
673         private static final int MSG_SET_ZOOM = 6;
674         private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
675         private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
676         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
677         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
678         private static final int MSG_SET_PAUSE_IMAGE = 11;
679         private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
680 
681         private VideoProvider.VideoProviderHandler mMessageHandler;
682         private final VideoProvider.VideoProviderBinder mBinder;
683 
684         /**
685          * Stores a list of the video callbacks, keyed by IBinder.
686          *
687          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
688          * load factor before resizing, 1 means we only expect a single thread to
689          * access the map so make only a single shard
690          */
691         private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
692                 new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
693 
694         /**
695          * Default handler used to consolidate binder method calls onto a single thread.
696          */
697         private final class VideoProviderHandler extends Handler {
VideoProviderHandler()698             public VideoProviderHandler() {
699                 super();
700             }
701 
VideoProviderHandler(Looper looper)702             public VideoProviderHandler(Looper looper) {
703                 super(looper);
704             }
705 
706             @Override
handleMessage(Message msg)707             public void handleMessage(Message msg) {
708                 switch (msg.what) {
709                     case MSG_ADD_VIDEO_CALLBACK: {
710                         IBinder binder = (IBinder) msg.obj;
711                         IVideoCallback callback = IVideoCallback.Stub
712                                 .asInterface((IBinder) msg.obj);
713                         if (callback == null) {
714                             Log.w(this, "addVideoProvider - skipped; callback is null.");
715                             break;
716                         }
717 
718                         if (mVideoCallbacks.containsKey(binder)) {
719                             Log.i(this, "addVideoProvider - skipped; already present.");
720                             break;
721                         }
722                         mVideoCallbacks.put(binder, callback);
723                         break;
724                     }
725                     case MSG_REMOVE_VIDEO_CALLBACK: {
726                         IBinder binder = (IBinder) msg.obj;
727                         IVideoCallback callback = IVideoCallback.Stub
728                                 .asInterface((IBinder) msg.obj);
729                         if (!mVideoCallbacks.containsKey(binder)) {
730                             Log.i(this, "removeVideoProvider - skipped; not present.");
731                             break;
732                         }
733                         mVideoCallbacks.remove(binder);
734                         break;
735                     }
736                     case MSG_SET_CAMERA:
737                         onSetCamera((String) msg.obj);
738                         break;
739                     case MSG_SET_PREVIEW_SURFACE:
740                         onSetPreviewSurface((Surface) msg.obj);
741                         break;
742                     case MSG_SET_DISPLAY_SURFACE:
743                         onSetDisplaySurface((Surface) msg.obj);
744                         break;
745                     case MSG_SET_DEVICE_ORIENTATION:
746                         onSetDeviceOrientation(msg.arg1);
747                         break;
748                     case MSG_SET_ZOOM:
749                         onSetZoom((Float) msg.obj);
750                         break;
751                     case MSG_SEND_SESSION_MODIFY_REQUEST: {
752                         SomeArgs args = (SomeArgs) msg.obj;
753                         try {
754                             onSendSessionModifyRequest((VideoProfile) args.arg1,
755                                     (VideoProfile) args.arg2);
756                         } finally {
757                             args.recycle();
758                         }
759                         break;
760                     }
761                     case MSG_SEND_SESSION_MODIFY_RESPONSE:
762                         onSendSessionModifyResponse((VideoProfile) msg.obj);
763                         break;
764                     case MSG_REQUEST_CAMERA_CAPABILITIES:
765                         onRequestCameraCapabilities();
766                         break;
767                     case MSG_REQUEST_CONNECTION_DATA_USAGE:
768                         onRequestConnectionDataUsage();
769                         break;
770                     case MSG_SET_PAUSE_IMAGE:
771                         onSetPauseImage((Uri) msg.obj);
772                         break;
773                     default:
774                         break;
775                 }
776             }
777         }
778 
779         /**
780          * IVideoProvider stub implementation.
781          */
782         private final class VideoProviderBinder extends IVideoProvider.Stub {
addVideoCallback(IBinder videoCallbackBinder)783             public void addVideoCallback(IBinder videoCallbackBinder) {
784                 mMessageHandler.obtainMessage(
785                         MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
786             }
787 
removeVideoCallback(IBinder videoCallbackBinder)788             public void removeVideoCallback(IBinder videoCallbackBinder) {
789                 mMessageHandler.obtainMessage(
790                         MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
791             }
792 
setCamera(String cameraId)793             public void setCamera(String cameraId) {
794                 mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
795             }
796 
setPreviewSurface(Surface surface)797             public void setPreviewSurface(Surface surface) {
798                 mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
799             }
800 
setDisplaySurface(Surface surface)801             public void setDisplaySurface(Surface surface) {
802                 mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
803             }
804 
setDeviceOrientation(int rotation)805             public void setDeviceOrientation(int rotation) {
806                 mMessageHandler.obtainMessage(
807                         MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
808             }
809 
setZoom(float value)810             public void setZoom(float value) {
811                 mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
812             }
813 
sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)814             public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
815                 SomeArgs args = SomeArgs.obtain();
816                 args.arg1 = fromProfile;
817                 args.arg2 = toProfile;
818                 mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
819             }
820 
sendSessionModifyResponse(VideoProfile responseProfile)821             public void sendSessionModifyResponse(VideoProfile responseProfile) {
822                 mMessageHandler.obtainMessage(
823                         MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
824             }
825 
requestCameraCapabilities()826             public void requestCameraCapabilities() {
827                 mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
828             }
829 
requestCallDataUsage()830             public void requestCallDataUsage() {
831                 mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
832             }
833 
setPauseImage(Uri uri)834             public void setPauseImage(Uri uri) {
835                 mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
836             }
837         }
838 
VideoProvider()839         public VideoProvider() {
840             mBinder = new VideoProvider.VideoProviderBinder();
841             mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
842         }
843 
844         /**
845          * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
846          *
847          * @param looper The looper.
848          * @hide
849          */
VideoProvider(Looper looper)850         public VideoProvider(Looper looper) {
851             mBinder = new VideoProvider.VideoProviderBinder();
852             mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
853         }
854 
855         /**
856          * Returns binder object which can be used across IPC methods.
857          * @hide
858          */
getInterface()859         public final IVideoProvider getInterface() {
860             return mBinder;
861         }
862 
863         /**
864          * Sets the camera to be used for the outgoing video.
865          * <p>
866          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
867          * camera via
868          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
869          * <p>
870          * Sent from the {@link InCallService} via
871          * {@link InCallService.VideoCall#setCamera(String)}.
872          *
873          * @param cameraId The id of the camera (use ids as reported by
874          * {@link CameraManager#getCameraIdList()}).
875          */
onSetCamera(String cameraId)876         public abstract void onSetCamera(String cameraId);
877 
878         /**
879          * Sets the surface to be used for displaying a preview of what the user's camera is
880          * currently capturing.  When video transmission is enabled, this is the video signal which
881          * is sent to the remote device.
882          * <p>
883          * Sent from the {@link InCallService} via
884          * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
885          *
886          * @param surface The {@link Surface}.
887          */
onSetPreviewSurface(Surface surface)888         public abstract void onSetPreviewSurface(Surface surface);
889 
890         /**
891          * Sets the surface to be used for displaying the video received from the remote device.
892          * <p>
893          * Sent from the {@link InCallService} via
894          * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
895          *
896          * @param surface The {@link Surface}.
897          */
onSetDisplaySurface(Surface surface)898         public abstract void onSetDisplaySurface(Surface surface);
899 
900         /**
901          * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
902          * the device is 0 degrees.
903          * <p>
904          * Sent from the {@link InCallService} via
905          * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
906          *
907          * @param rotation The device orientation, in degrees.
908          */
onSetDeviceOrientation(int rotation)909         public abstract void onSetDeviceOrientation(int rotation);
910 
911         /**
912          * Sets camera zoom ratio.
913          * <p>
914          * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
915          *
916          * @param value The camera zoom ratio.
917          */
onSetZoom(float value)918         public abstract void onSetZoom(float value);
919 
920         /**
921          * Issues a request to modify the properties of the current video session.
922          * <p>
923          * Example scenarios include: requesting an audio-only call to be upgraded to a
924          * bi-directional video call, turning on or off the user's camera, sending a pause signal
925          * when the {@link InCallService} is no longer the foreground application.
926          * <p>
927          * If the {@link VideoProvider} determines a request to be invalid, it should call
928          * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
929          * invalid request back to the {@link InCallService}.
930          * <p>
931          * Where a request requires confirmation from the user of the peer device, the
932          * {@link VideoProvider} must communicate the request to the peer device and handle the
933          * user's response.  {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
934          * is used to inform the {@link InCallService} of the result of the request.
935          * <p>
936          * Sent from the {@link InCallService} via
937          * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
938          *
939          * @param fromProfile The video profile prior to the request.
940          * @param toProfile The video profile with the requested changes made.
941          */
onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)942         public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
943                 VideoProfile toProfile);
944 
945         /**
946          * Provides a response to a request to change the current video session properties.
947          * <p>
948          * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
949          * video call, could decline the request and keep the call as audio-only.
950          * In such a scenario, the {@code responseProfile} would have a video state of
951          * {@link VideoProfile#STATE_AUDIO_ONLY}.  If the user had decided to accept the request,
952          * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
953          * <p>
954          * Sent from the {@link InCallService} via
955          * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
956          * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
957          * callback.
958          *
959          * @param responseProfile The response video profile.
960          */
onSendSessionModifyResponse(VideoProfile responseProfile)961         public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
962 
963         /**
964          * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
965          * <p>
966          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
967          * camera via
968          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
969          * <p>
970          * Sent from the {@link InCallService} via
971          * {@link InCallService.VideoCall#requestCameraCapabilities()}.
972          */
onRequestCameraCapabilities()973         public abstract void onRequestCameraCapabilities();
974 
975         /**
976          * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
977          * video component of the current {@link Connection}.
978          * <p>
979          * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
980          * via {@link VideoProvider#setCallDataUsage(long)}.
981          * <p>
982          * Sent from the {@link InCallService} via
983          * {@link InCallService.VideoCall#requestCallDataUsage()}.
984          */
onRequestConnectionDataUsage()985         public abstract void onRequestConnectionDataUsage();
986 
987         /**
988          * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
989          * the peer device when the video signal is paused.
990          * <p>
991          * Sent from the {@link InCallService} via
992          * {@link InCallService.VideoCall#setPauseImage(Uri)}.
993          *
994          * @param uri URI of image to display.
995          */
onSetPauseImage(Uri uri)996         public abstract void onSetPauseImage(Uri uri);
997 
998         /**
999          * Used to inform listening {@link InCallService} implementations when the
1000          * {@link VideoProvider} receives a session modification request.
1001          * <p>
1002          * Received by the {@link InCallService} via
1003          * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
1004          *
1005          * @param videoProfile The requested video profile.
1006          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1007          */
receiveSessionModifyRequest(VideoProfile videoProfile)1008         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
1009             if (mVideoCallbacks != null) {
1010                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1011                     try {
1012                         callback.receiveSessionModifyRequest(videoProfile);
1013                     } catch (RemoteException ignored) {
1014                         Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
1015                     }
1016                 }
1017             }
1018         }
1019 
1020         /**
1021          * Used to inform listening {@link InCallService} implementations when the
1022          * {@link VideoProvider} receives a response to a session modification request.
1023          * <p>
1024          * Received by the {@link InCallService} via
1025          * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
1026          * VideoProfile, VideoProfile)}.
1027          *
1028          * @param status Status of the session modify request.  Valid values are
1029          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
1030          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
1031          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
1032          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
1033          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
1034          * @param requestedProfile The original request which was sent to the peer device.
1035          * @param responseProfile The actual profile changes agreed to by the peer device.
1036          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1037          */
receiveSessionModifyResponse(int status, VideoProfile requestedProfile, VideoProfile responseProfile)1038         public void receiveSessionModifyResponse(int status,
1039                 VideoProfile requestedProfile, VideoProfile responseProfile) {
1040             if (mVideoCallbacks != null) {
1041                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1042                     try {
1043                         callback.receiveSessionModifyResponse(status, requestedProfile,
1044                                 responseProfile);
1045                     } catch (RemoteException ignored) {
1046                         Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
1047                     }
1048                 }
1049             }
1050         }
1051 
1052         /**
1053          * Used to inform listening {@link InCallService} implementations when the
1054          * {@link VideoProvider} reports a call session event.
1055          * <p>
1056          * Received by the {@link InCallService} via
1057          * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
1058          *
1059          * @param event The event.  Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
1060          *      {@link VideoProvider#SESSION_EVENT_RX_RESUME},
1061          *      {@link VideoProvider#SESSION_EVENT_TX_START},
1062          *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
1063          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
1064          *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY}.
1065          */
handleCallSessionEvent(int event)1066         public void handleCallSessionEvent(int event) {
1067             if (mVideoCallbacks != null) {
1068                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1069                     try {
1070                         callback.handleCallSessionEvent(event);
1071                     } catch (RemoteException ignored) {
1072                         Log.w(this, "handleCallSessionEvent callback failed", ignored);
1073                     }
1074                 }
1075             }
1076         }
1077 
1078         /**
1079          * Used to inform listening {@link InCallService} implementations when the dimensions of the
1080          * peer's video have changed.
1081          * <p>
1082          * This could occur if, for example, the peer rotates their device, changing the aspect
1083          * ratio of the video, or if the user switches between the back and front cameras.
1084          * <p>
1085          * Received by the {@link InCallService} via
1086          * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
1087          *
1088          * @param width  The updated peer video width.
1089          * @param height The updated peer video height.
1090          */
changePeerDimensions(int width, int height)1091         public void changePeerDimensions(int width, int height) {
1092             if (mVideoCallbacks != null) {
1093                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1094                     try {
1095                         callback.changePeerDimensions(width, height);
1096                     } catch (RemoteException ignored) {
1097                         Log.w(this, "changePeerDimensions callback failed", ignored);
1098                     }
1099                 }
1100             }
1101         }
1102 
1103         /**
1104          * Used to inform listening {@link InCallService} implementations when the data usage of the
1105          * video associated with the current {@link Connection} has changed.
1106          * <p>
1107          * This could be in response to a preview request via
1108          * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
1109          * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
1110          * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
1111          * <p>
1112          * Received by the {@link InCallService} via
1113          * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
1114          *
1115          * @param dataUsage The updated data usage (in bytes).  Reported as the cumulative bytes
1116          *                  used since the start of the call.
1117          */
setCallDataUsage(long dataUsage)1118         public void setCallDataUsage(long dataUsage) {
1119             if (mVideoCallbacks != null) {
1120                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1121                     try {
1122                         callback.changeCallDataUsage(dataUsage);
1123                     } catch (RemoteException ignored) {
1124                         Log.w(this, "setCallDataUsage callback failed", ignored);
1125                     }
1126                 }
1127             }
1128         }
1129 
1130         /**
1131          * @see #setCallDataUsage(long)
1132          *
1133          * @param dataUsage The updated data usage (in byes).
1134          * @deprecated - Use {@link #setCallDataUsage(long)} instead.
1135          * @hide
1136          */
changeCallDataUsage(long dataUsage)1137         public void changeCallDataUsage(long dataUsage) {
1138             setCallDataUsage(dataUsage);
1139         }
1140 
1141         /**
1142          * Used to inform listening {@link InCallService} implementations when the capabilities of
1143          * the current camera have changed.
1144          * <p>
1145          * The {@link VideoProvider} should call this in response to
1146          * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
1147          * changed via {@link VideoProvider#onSetCamera(String)}.
1148          * <p>
1149          * Received by the {@link InCallService} via
1150          * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
1151          * VideoProfile.CameraCapabilities)}.
1152          *
1153          * @param cameraCapabilities The new camera capabilities.
1154          */
changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities)1155         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
1156             if (mVideoCallbacks != null) {
1157                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1158                     try {
1159                         callback.changeCameraCapabilities(cameraCapabilities);
1160                     } catch (RemoteException ignored) {
1161                         Log.w(this, "changeCameraCapabilities callback failed", ignored);
1162                     }
1163                 }
1164             }
1165         }
1166 
1167         /**
1168          * Used to inform listening {@link InCallService} implementations when the video quality
1169          * of the call has changed.
1170          * <p>
1171          * Received by the {@link InCallService} via
1172          * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
1173          *
1174          * @param videoQuality The updated video quality.  Valid values:
1175          *      {@link VideoProfile#QUALITY_HIGH},
1176          *      {@link VideoProfile#QUALITY_MEDIUM},
1177          *      {@link VideoProfile#QUALITY_LOW},
1178          *      {@link VideoProfile#QUALITY_DEFAULT}.
1179          */
changeVideoQuality(int videoQuality)1180         public void changeVideoQuality(int videoQuality) {
1181             if (mVideoCallbacks != null) {
1182                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1183                     try {
1184                         callback.changeVideoQuality(videoQuality);
1185                     } catch (RemoteException ignored) {
1186                         Log.w(this, "changeVideoQuality callback failed", ignored);
1187                     }
1188                 }
1189             }
1190         }
1191     }
1192 
1193     private final Listener mConnectionDeathListener = new Listener() {
1194         @Override
1195         public void onDestroyed(Connection c) {
1196             if (mConferenceables.remove(c)) {
1197                 fireOnConferenceableConnectionsChanged();
1198             }
1199         }
1200     };
1201 
1202     private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
1203         @Override
1204         public void onDestroyed(Conference c) {
1205             if (mConferenceables.remove(c)) {
1206                 fireOnConferenceableConnectionsChanged();
1207             }
1208         }
1209     };
1210 
1211     /**
1212      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
1213      * load factor before resizing, 1 means we only expect a single thread to
1214      * access the map so make only a single shard
1215      */
1216     private final Set<Listener> mListeners = Collections.newSetFromMap(
1217             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
1218     private final List<Conferenceable> mConferenceables = new ArrayList<>();
1219     private final List<Conferenceable> mUnmodifiableConferenceables =
1220             Collections.unmodifiableList(mConferenceables);
1221 
1222     // The internal telecom call ID associated with this connection.
1223     private String mTelecomCallId;
1224     private int mState = STATE_NEW;
1225     private CallAudioState mCallAudioState;
1226     private Uri mAddress;
1227     private int mAddressPresentation;
1228     private String mCallerDisplayName;
1229     private int mCallerDisplayNamePresentation;
1230     private boolean mRingbackRequested = false;
1231     private int mConnectionCapabilities;
1232     private int mConnectionProperties;
1233     private VideoProvider mVideoProvider;
1234     private boolean mAudioModeIsVoip;
1235     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
1236     private StatusHints mStatusHints;
1237     private int mVideoState;
1238     private DisconnectCause mDisconnectCause;
1239     private Conference mConference;
1240     private ConnectionService mConnectionService;
1241     private Bundle mExtras;
1242     private final Object mExtrasLock = new Object();
1243 
1244     /**
1245      * Tracks the key set for the extras bundle provided on the last invocation of
1246      * {@link #setExtras(Bundle)}.  Used so that on subsequent invocations we can remove any extras
1247      * keys which were set previously but are no longer present in the replacement Bundle.
1248      */
1249     private Set<String> mPreviousExtraKeys;
1250 
1251     /**
1252      * Create a new Connection.
1253      */
Connection()1254     public Connection() {}
1255 
1256     /**
1257      * Returns the Telecom internal call ID associated with this connection.  Should only be used
1258      * for debugging and tracing purposes.
1259      *
1260      * @return The Telecom call ID.
1261      * @hide
1262      */
getTelecomCallId()1263     public final String getTelecomCallId() {
1264         return mTelecomCallId;
1265     }
1266 
1267     /**
1268      * @return The address (e.g., phone number) to which this Connection is currently communicating.
1269      */
getAddress()1270     public final Uri getAddress() {
1271         return mAddress;
1272     }
1273 
1274     /**
1275      * @return The presentation requirements for the address.
1276      *         See {@link TelecomManager} for valid values.
1277      */
getAddressPresentation()1278     public final int getAddressPresentation() {
1279         return mAddressPresentation;
1280     }
1281 
1282     /**
1283      * @return The caller display name (CNAP).
1284      */
getCallerDisplayName()1285     public final String getCallerDisplayName() {
1286         return mCallerDisplayName;
1287     }
1288 
1289     /**
1290      * @return The presentation requirements for the handle.
1291      *         See {@link TelecomManager} for valid values.
1292      */
getCallerDisplayNamePresentation()1293     public final int getCallerDisplayNamePresentation() {
1294         return mCallerDisplayNamePresentation;
1295     }
1296 
1297     /**
1298      * @return The state of this Connection.
1299      */
getState()1300     public final int getState() {
1301         return mState;
1302     }
1303 
1304     /**
1305      * Returns the video state of the connection.
1306      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1307      * {@link VideoProfile#STATE_BIDIRECTIONAL},
1308      * {@link VideoProfile#STATE_TX_ENABLED},
1309      * {@link VideoProfile#STATE_RX_ENABLED}.
1310      *
1311      * @return The video state of the connection.
1312      * @hide
1313      */
getVideoState()1314     public final int getVideoState() {
1315         return mVideoState;
1316     }
1317 
1318     /**
1319      * @return The audio state of the connection, describing how its audio is currently
1320      *         being routed by the system. This is {@code null} if this Connection
1321      *         does not directly know about its audio state.
1322      * @deprecated Use {@link #getCallAudioState()} instead.
1323      * @hide
1324      */
1325     @SystemApi
1326     @Deprecated
getAudioState()1327     public final AudioState getAudioState() {
1328         if (mCallAudioState == null) {
1329           return null;
1330         }
1331         return new AudioState(mCallAudioState);
1332     }
1333 
1334     /**
1335      * @return The audio state of the connection, describing how its audio is currently
1336      *         being routed by the system. This is {@code null} if this Connection
1337      *         does not directly know about its audio state.
1338      */
getCallAudioState()1339     public final CallAudioState getCallAudioState() {
1340         return mCallAudioState;
1341     }
1342 
1343     /**
1344      * @return The conference that this connection is a part of.  Null if it is not part of any
1345      *         conference.
1346      */
getConference()1347     public final Conference getConference() {
1348         return mConference;
1349     }
1350 
1351     /**
1352      * Returns whether this connection is requesting that the system play a ringback tone
1353      * on its behalf.
1354      */
isRingbackRequested()1355     public final boolean isRingbackRequested() {
1356         return mRingbackRequested;
1357     }
1358 
1359     /**
1360      * @return True if the connection's audio mode is VOIP.
1361      */
getAudioModeIsVoip()1362     public final boolean getAudioModeIsVoip() {
1363         return mAudioModeIsVoip;
1364     }
1365 
1366     /**
1367      * Retrieves the connection start time of the {@code Connnection}, if specified.  A value of
1368      * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
1369      * start time of the conference.
1370      *
1371      * @return The time at which the {@code Connnection} was connected.
1372      *
1373      * @hide
1374      */
getConnectTimeMillis()1375     public final long getConnectTimeMillis() {
1376         return mConnectTimeMillis;
1377     }
1378 
1379     /**
1380      * @return The status hints for this connection.
1381      */
getStatusHints()1382     public final StatusHints getStatusHints() {
1383         return mStatusHints;
1384     }
1385 
1386     /**
1387      * Returns the extras associated with this connection.
1388      *
1389      * @return The extras associated with this connection.
1390      */
getExtras()1391     public final Bundle getExtras() {
1392         Bundle extras = null;
1393         synchronized (mExtrasLock) {
1394             if (mExtras != null) {
1395                 extras = new Bundle(mExtras);
1396             }
1397         }
1398         return extras;
1399     }
1400 
1401     /**
1402      * Assign a listener to be notified of state changes.
1403      *
1404      * @param l A listener.
1405      * @return This Connection.
1406      *
1407      * @hide
1408      */
addConnectionListener(Listener l)1409     public final Connection addConnectionListener(Listener l) {
1410         mListeners.add(l);
1411         return this;
1412     }
1413 
1414     /**
1415      * Remove a previously assigned listener that was being notified of state changes.
1416      *
1417      * @param l A Listener.
1418      * @return This Connection.
1419      *
1420      * @hide
1421      */
removeConnectionListener(Listener l)1422     public final Connection removeConnectionListener(Listener l) {
1423         if (l != null) {
1424             mListeners.remove(l);
1425         }
1426         return this;
1427     }
1428 
1429     /**
1430      * @return The {@link DisconnectCause} for this connection.
1431      */
getDisconnectCause()1432     public final DisconnectCause getDisconnectCause() {
1433         return mDisconnectCause;
1434     }
1435 
1436     /**
1437      * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
1438      * ONLY for debugging purposes.
1439      *
1440      * @param callId The telecom call ID.
1441      * @hide
1442      */
setTelecomCallId(String callId)1443     public void setTelecomCallId(String callId) {
1444         mTelecomCallId = callId;
1445     }
1446 
1447     /**
1448      * Inform this Connection that the state of its audio output has been changed externally.
1449      *
1450      * @param state The new audio state.
1451      * @hide
1452      */
setCallAudioState(CallAudioState state)1453     final void setCallAudioState(CallAudioState state) {
1454         checkImmutable();
1455         Log.d(this, "setAudioState %s", state);
1456         mCallAudioState = state;
1457         onAudioStateChanged(getAudioState());
1458         onCallAudioStateChanged(state);
1459     }
1460 
1461     /**
1462      * @param state An integer value of a {@code STATE_*} constant.
1463      * @return A string representation of the value.
1464      */
stateToString(int state)1465     public static String stateToString(int state) {
1466         switch (state) {
1467             case STATE_INITIALIZING:
1468                 return "INITIALIZING";
1469             case STATE_NEW:
1470                 return "NEW";
1471             case STATE_RINGING:
1472                 return "RINGING";
1473             case STATE_DIALING:
1474                 return "DIALING";
1475             case STATE_ACTIVE:
1476                 return "ACTIVE";
1477             case STATE_HOLDING:
1478                 return "HOLDING";
1479             case STATE_DISCONNECTED:
1480                 return "DISCONNECTED";
1481             default:
1482                 Log.wtf(Connection.class, "Unknown state %d", state);
1483                 return "UNKNOWN";
1484         }
1485     }
1486 
1487     /**
1488      * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
1489      */
getConnectionCapabilities()1490     public final int getConnectionCapabilities() {
1491         return mConnectionCapabilities;
1492     }
1493 
1494     /**
1495      * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
1496      * @hide
1497      */
getConnectionProperties()1498     public final int getConnectionProperties() {
1499         return mConnectionProperties;
1500     }
1501 
1502     /**
1503      * Sets the value of the {@link #getAddress()} property.
1504      *
1505      * @param address The new address.
1506      * @param presentation The presentation requirements for the address.
1507      *        See {@link TelecomManager} for valid values.
1508      */
setAddress(Uri address, int presentation)1509     public final void setAddress(Uri address, int presentation) {
1510         checkImmutable();
1511         Log.d(this, "setAddress %s", address);
1512         mAddress = address;
1513         mAddressPresentation = presentation;
1514         for (Listener l : mListeners) {
1515             l.onAddressChanged(this, address, presentation);
1516         }
1517     }
1518 
1519     /**
1520      * Sets the caller display name (CNAP).
1521      *
1522      * @param callerDisplayName The new display name.
1523      * @param presentation The presentation requirements for the handle.
1524      *        See {@link TelecomManager} for valid values.
1525      */
setCallerDisplayName(String callerDisplayName, int presentation)1526     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
1527         checkImmutable();
1528         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
1529         mCallerDisplayName = callerDisplayName;
1530         mCallerDisplayNamePresentation = presentation;
1531         for (Listener l : mListeners) {
1532             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1533         }
1534     }
1535 
1536     /**
1537      * Set the video state for the connection.
1538      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1539      * {@link VideoProfile#STATE_BIDIRECTIONAL},
1540      * {@link VideoProfile#STATE_TX_ENABLED},
1541      * {@link VideoProfile#STATE_RX_ENABLED}.
1542      *
1543      * @param videoState The new video state.
1544      */
setVideoState(int videoState)1545     public final void setVideoState(int videoState) {
1546         checkImmutable();
1547         Log.d(this, "setVideoState %d", videoState);
1548         mVideoState = videoState;
1549         for (Listener l : mListeners) {
1550             l.onVideoStateChanged(this, mVideoState);
1551         }
1552     }
1553 
1554     /**
1555      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
1556      * communicate).
1557      */
setActive()1558     public final void setActive() {
1559         checkImmutable();
1560         setRingbackRequested(false);
1561         setState(STATE_ACTIVE);
1562     }
1563 
1564     /**
1565      * Sets state to ringing (e.g., an inbound ringing connection).
1566      */
setRinging()1567     public final void setRinging() {
1568         checkImmutable();
1569         setState(STATE_RINGING);
1570     }
1571 
1572     /**
1573      * Sets state to initializing (this Connection is not yet ready to be used).
1574      */
setInitializing()1575     public final void setInitializing() {
1576         checkImmutable();
1577         setState(STATE_INITIALIZING);
1578     }
1579 
1580     /**
1581      * Sets state to initialized (the Connection has been set up and is now ready to be used).
1582      */
setInitialized()1583     public final void setInitialized() {
1584         checkImmutable();
1585         setState(STATE_NEW);
1586     }
1587 
1588     /**
1589      * Sets state to dialing (e.g., dialing an outbound connection).
1590      */
setDialing()1591     public final void setDialing() {
1592         checkImmutable();
1593         setState(STATE_DIALING);
1594     }
1595 
1596     /**
1597      * Sets state to be on hold.
1598      */
setOnHold()1599     public final void setOnHold() {
1600         checkImmutable();
1601         setState(STATE_HOLDING);
1602     }
1603 
1604     /**
1605      * Sets the video connection provider.
1606      * @param videoProvider The video provider.
1607      */
setVideoProvider(VideoProvider videoProvider)1608     public final void setVideoProvider(VideoProvider videoProvider) {
1609         checkImmutable();
1610         mVideoProvider = videoProvider;
1611         for (Listener l : mListeners) {
1612             l.onVideoProviderChanged(this, videoProvider);
1613         }
1614     }
1615 
getVideoProvider()1616     public final VideoProvider getVideoProvider() {
1617         return mVideoProvider;
1618     }
1619 
1620     /**
1621      * Sets state to disconnected.
1622      *
1623      * @param disconnectCause The reason for the disconnection, as specified by
1624      *         {@link DisconnectCause}.
1625      */
setDisconnected(DisconnectCause disconnectCause)1626     public final void setDisconnected(DisconnectCause disconnectCause) {
1627         checkImmutable();
1628         mDisconnectCause = disconnectCause;
1629         setState(STATE_DISCONNECTED);
1630         Log.d(this, "Disconnected with cause %s", disconnectCause);
1631         for (Listener l : mListeners) {
1632             l.onDisconnected(this, disconnectCause);
1633         }
1634     }
1635 
1636     /**
1637      * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
1638      * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
1639      * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
1640      * to send an {@link #onPostDialContinue(boolean)} signal.
1641      *
1642      * @param remaining The DTMF character sequence remaining to be emitted once the
1643      *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
1644      *         that remaining sequence may contain.
1645      */
setPostDialWait(String remaining)1646     public final void setPostDialWait(String remaining) {
1647         checkImmutable();
1648         for (Listener l : mListeners) {
1649             l.onPostDialWait(this, remaining);
1650         }
1651     }
1652 
1653     /**
1654      * Informs listeners that this {@code Connection} has processed a character in the post-dial
1655      * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
1656      * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
1657      *
1658      * @param nextChar The DTMF character that was just processed by the {@code Connection}.
1659      */
setNextPostDialChar(char nextChar)1660     public final void setNextPostDialChar(char nextChar) {
1661         checkImmutable();
1662         for (Listener l : mListeners) {
1663             l.onPostDialChar(this, nextChar);
1664         }
1665     }
1666 
1667     /**
1668      * Requests that the framework play a ringback tone. This is to be invoked by implementations
1669      * that do not play a ringback tone themselves in the connection's audio stream.
1670      *
1671      * @param ringback Whether the ringback tone is to be played.
1672      */
setRingbackRequested(boolean ringback)1673     public final void setRingbackRequested(boolean ringback) {
1674         checkImmutable();
1675         if (mRingbackRequested != ringback) {
1676             mRingbackRequested = ringback;
1677             for (Listener l : mListeners) {
1678                 l.onRingbackRequested(this, ringback);
1679             }
1680         }
1681     }
1682 
1683     /**
1684      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
1685      *
1686      * @param connectionCapabilities The new connection capabilities.
1687      */
setConnectionCapabilities(int connectionCapabilities)1688     public final void setConnectionCapabilities(int connectionCapabilities) {
1689         checkImmutable();
1690         if (mConnectionCapabilities != connectionCapabilities) {
1691             mConnectionCapabilities = connectionCapabilities;
1692             for (Listener l : mListeners) {
1693                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
1694             }
1695         }
1696     }
1697 
1698     /**
1699      * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
1700      *
1701      * @param connectionProperties The new connection properties.
1702      * @hide
1703      */
setConnectionProperties(int connectionProperties)1704     public final void setConnectionProperties(int connectionProperties) {
1705         checkImmutable();
1706         if (mConnectionProperties != connectionProperties) {
1707             mConnectionProperties = connectionProperties;
1708             for (Listener l : mListeners) {
1709                 l.onConnectionPropertiesChanged(this, mConnectionProperties);
1710             }
1711         }
1712     }
1713 
1714     /**
1715      * Tears down the Connection object.
1716      */
destroy()1717     public final void destroy() {
1718         for (Listener l : mListeners) {
1719             l.onDestroyed(this);
1720         }
1721     }
1722 
1723     /**
1724      * Requests that the framework use VOIP audio mode for this connection.
1725      *
1726      * @param isVoip True if the audio mode is VOIP.
1727      */
setAudioModeIsVoip(boolean isVoip)1728     public final void setAudioModeIsVoip(boolean isVoip) {
1729         checkImmutable();
1730         mAudioModeIsVoip = isVoip;
1731         for (Listener l : mListeners) {
1732             l.onAudioModeIsVoipChanged(this, isVoip);
1733         }
1734     }
1735 
1736     /**
1737      * Sets the time at which a call became active on this Connection. This is set only
1738      * when a conference call becomes active on this connection.
1739      *
1740      * @param connectionTimeMillis The connection time, in milliseconds.
1741      *
1742      * @hide
1743      */
setConnectTimeMillis(long connectTimeMillis)1744     public final void setConnectTimeMillis(long connectTimeMillis) {
1745         mConnectTimeMillis = connectTimeMillis;
1746     }
1747 
1748     /**
1749      * Sets the label and icon status to display in the in-call UI.
1750      *
1751      * @param statusHints The status label and icon to set.
1752      */
setStatusHints(StatusHints statusHints)1753     public final void setStatusHints(StatusHints statusHints) {
1754         checkImmutable();
1755         mStatusHints = statusHints;
1756         for (Listener l : mListeners) {
1757             l.onStatusHintsChanged(this, statusHints);
1758         }
1759     }
1760 
1761     /**
1762      * Sets the connections with which this connection can be conferenced.
1763      *
1764      * @param conferenceableConnections The set of connections this connection can conference with.
1765      */
setConferenceableConnections(List<Connection> conferenceableConnections)1766     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
1767         checkImmutable();
1768         clearConferenceableList();
1769         for (Connection c : conferenceableConnections) {
1770             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1771             // small amount of items here.
1772             if (!mConferenceables.contains(c)) {
1773                 c.addConnectionListener(mConnectionDeathListener);
1774                 mConferenceables.add(c);
1775             }
1776         }
1777         fireOnConferenceableConnectionsChanged();
1778     }
1779 
1780     /**
1781      * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
1782      * or conferences with which this connection can be conferenced.
1783      *
1784      * @param conferenceables The conferenceables.
1785      */
setConferenceables(List<Conferenceable> conferenceables)1786     public final void setConferenceables(List<Conferenceable> conferenceables) {
1787         clearConferenceableList();
1788         for (Conferenceable c : conferenceables) {
1789             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
1790             // small amount of items here.
1791             if (!mConferenceables.contains(c)) {
1792                 if (c instanceof Connection) {
1793                     Connection connection = (Connection) c;
1794                     connection.addConnectionListener(mConnectionDeathListener);
1795                 } else if (c instanceof Conference) {
1796                     Conference conference = (Conference) c;
1797                     conference.addListener(mConferenceDeathListener);
1798                 }
1799                 mConferenceables.add(c);
1800             }
1801         }
1802         fireOnConferenceableConnectionsChanged();
1803     }
1804 
1805     /**
1806      * Returns the connections or conferences with which this connection can be conferenced.
1807      */
getConferenceables()1808     public final List<Conferenceable> getConferenceables() {
1809         return mUnmodifiableConferenceables;
1810     }
1811 
1812     /**
1813      * @hide
1814      */
setConnectionService(ConnectionService connectionService)1815     public final void setConnectionService(ConnectionService connectionService) {
1816         checkImmutable();
1817         if (mConnectionService != null) {
1818             Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
1819                     "which is already associated with another ConnectionService.");
1820         } else {
1821             mConnectionService = connectionService;
1822         }
1823     }
1824 
1825     /**
1826      * @hide
1827      */
unsetConnectionService(ConnectionService connectionService)1828     public final void unsetConnectionService(ConnectionService connectionService) {
1829         if (mConnectionService != connectionService) {
1830             Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
1831                     "that does not belong to the ConnectionService.");
1832         } else {
1833             mConnectionService = null;
1834         }
1835     }
1836 
1837     /**
1838      * @hide
1839      */
getConnectionService()1840     public final ConnectionService getConnectionService() {
1841         return mConnectionService;
1842     }
1843 
1844     /**
1845      * Sets the conference that this connection is a part of. This will fail if the connection is
1846      * already part of a conference. {@link #resetConference} to un-set the conference first.
1847      *
1848      * @param conference The conference.
1849      * @return {@code true} if the conference was successfully set.
1850      * @hide
1851      */
setConference(Conference conference)1852     public final boolean setConference(Conference conference) {
1853         checkImmutable();
1854         // We check to see if it is already part of another conference.
1855         if (mConference == null) {
1856             mConference = conference;
1857             if (mConnectionService != null && mConnectionService.containsConference(conference)) {
1858                 fireConferenceChanged();
1859             }
1860             return true;
1861         }
1862         return false;
1863     }
1864 
1865     /**
1866      * Resets the conference that this connection is a part of.
1867      * @hide
1868      */
resetConference()1869     public final void resetConference() {
1870         if (mConference != null) {
1871             Log.d(this, "Conference reset");
1872             mConference = null;
1873             fireConferenceChanged();
1874         }
1875     }
1876 
1877     /**
1878      * Set some extras that can be associated with this {@code Connection}.
1879      * <p>
1880      * New or existing keys are replaced in the {@code Connection} extras.  Keys which are no longer
1881      * in the new extras, but were present the last time {@code setExtras} was called are removed.
1882      * <p>
1883      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
1884      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1885      *
1886      * @param extras The extras associated with this {@code Connection}.
1887      */
setExtras(@ullable Bundle extras)1888     public final void setExtras(@Nullable Bundle extras) {
1889         checkImmutable();
1890 
1891         // Add/replace any new or changed extras values.
1892         putExtras(extras);
1893 
1894         // If we have used "setExtras" in the past, compare the key set from the last invocation to
1895         // the current one and remove any keys that went away.
1896         if (mPreviousExtraKeys != null) {
1897             List<String> toRemove = new ArrayList<String>();
1898             for (String oldKey : mPreviousExtraKeys) {
1899                 if (extras == null || !extras.containsKey(oldKey)) {
1900                     toRemove.add(oldKey);
1901                 }
1902             }
1903             if (!toRemove.isEmpty()) {
1904                 removeExtras(toRemove);
1905             }
1906         }
1907 
1908         // Track the keys the last time set called setExtras.  This way, the next time setExtras is
1909         // called we can see if the caller has removed any extras values.
1910         if (mPreviousExtraKeys == null) {
1911             mPreviousExtraKeys = new ArraySet<String>();
1912         }
1913         mPreviousExtraKeys.clear();
1914         if (extras != null) {
1915             mPreviousExtraKeys.addAll(extras.keySet());
1916         }
1917     }
1918 
1919     /**
1920      * Adds some extras to this {@code Connection}.  Existing keys are replaced and new ones are
1921      * added.
1922      * <p>
1923      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
1924      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
1925      *
1926      * @param extras The extras to add.
1927      * @hide
1928      */
putExtras(@onNull Bundle extras)1929     public final void putExtras(@NonNull Bundle extras) {
1930         checkImmutable();
1931         if (extras == null) {
1932             return;
1933         }
1934         // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
1935         // the listeners.
1936         Bundle listenerExtras;
1937         synchronized (mExtrasLock) {
1938             if (mExtras == null) {
1939                 mExtras = new Bundle();
1940             }
1941             mExtras.putAll(extras);
1942             listenerExtras = new Bundle(mExtras);
1943         }
1944         for (Listener l : mListeners) {
1945             // Create a new clone of the extras for each listener so that they don't clobber
1946             // each other
1947             l.onExtrasChanged(this, new Bundle(listenerExtras));
1948         }
1949     }
1950 
1951     /**
1952      * Adds a boolean extra to this {@code Connection}.
1953      *
1954      * @param key The extra key.
1955      * @param value The value.
1956      * @hide
1957      */
putExtra(String key, boolean value)1958     public final void putExtra(String key, boolean value) {
1959         Bundle newExtras = new Bundle();
1960         newExtras.putBoolean(key, value);
1961         putExtras(newExtras);
1962     }
1963 
1964     /**
1965      * Adds an integer extra to this {@code Connection}.
1966      *
1967      * @param key The extra key.
1968      * @param value The value.
1969      * @hide
1970      */
putExtra(String key, int value)1971     public final void putExtra(String key, int value) {
1972         Bundle newExtras = new Bundle();
1973         newExtras.putInt(key, value);
1974         putExtras(newExtras);
1975     }
1976 
1977     /**
1978      * Adds a string extra to this {@code Connection}.
1979      *
1980      * @param key The extra key.
1981      * @param value The value.
1982      * @hide
1983      */
putExtra(String key, String value)1984     public final void putExtra(String key, String value) {
1985         Bundle newExtras = new Bundle();
1986         newExtras.putString(key, value);
1987         putExtras(newExtras);
1988     }
1989 
1990     /**
1991      * Removes an extra from this {@code Connection}.
1992      *
1993      * @param keys The key of the extra key to remove.
1994      * @hide
1995      */
removeExtras(List<String> keys)1996     public final void removeExtras(List<String> keys) {
1997         synchronized (mExtrasLock) {
1998             if (mExtras != null) {
1999                 for (String key : keys) {
2000                     mExtras.remove(key);
2001                 }
2002             }
2003         }
2004         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
2005         for (Listener l : mListeners) {
2006             l.onExtrasRemoved(this, unmodifiableKeys);
2007         }
2008     }
2009 
2010     /**
2011      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
2012      *
2013      * @param state The new connection audio state.
2014      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
2015      * @hide
2016      */
2017     @SystemApi
2018     @Deprecated
onAudioStateChanged(AudioState state)2019     public void onAudioStateChanged(AudioState state) {}
2020 
2021     /**
2022      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
2023      *
2024      * @param state The new connection audio state.
2025      */
onCallAudioStateChanged(CallAudioState state)2026     public void onCallAudioStateChanged(CallAudioState state) {}
2027 
2028     /**
2029      * Notifies this Connection of an internal state change. This method is called after the
2030      * state is changed.
2031      *
2032      * @param state The new state, one of the {@code STATE_*} constants.
2033      */
onStateChanged(int state)2034     public void onStateChanged(int state) {}
2035 
2036     /**
2037      * Notifies this Connection of a request to play a DTMF tone.
2038      *
2039      * @param c A DTMF character.
2040      */
onPlayDtmfTone(char c)2041     public void onPlayDtmfTone(char c) {}
2042 
2043     /**
2044      * Notifies this Connection of a request to stop any currently playing DTMF tones.
2045      */
onStopDtmfTone()2046     public void onStopDtmfTone() {}
2047 
2048     /**
2049      * Notifies this Connection of a request to disconnect.
2050      */
onDisconnect()2051     public void onDisconnect() {}
2052 
2053     /**
2054      * Notifies this Connection of a request to disconnect a participant of the conference managed
2055      * by the connection.
2056      *
2057      * @param endpoint the {@link Uri} of the participant to disconnect.
2058      * @hide
2059      */
onDisconnectConferenceParticipant(Uri endpoint)2060     public void onDisconnectConferenceParticipant(Uri endpoint) {}
2061 
2062     /**
2063      * Notifies this Connection of a request to separate from its parent conference.
2064      */
onSeparate()2065     public void onSeparate() {}
2066 
2067     /**
2068      * Notifies this Connection of a request to abort.
2069      */
onAbort()2070     public void onAbort() {}
2071 
2072     /**
2073      * Notifies this Connection of a request to hold.
2074      */
onHold()2075     public void onHold() {}
2076 
2077     /**
2078      * Notifies this Connection of a request to exit a hold state.
2079      */
onUnhold()2080     public void onUnhold() {}
2081 
2082     /**
2083      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2084      * a request to accept.
2085      *
2086      * @param videoState The video state in which to answer the connection.
2087      */
onAnswer(int videoState)2088     public void onAnswer(int videoState) {}
2089 
2090     /**
2091      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2092      * a request to accept.
2093      */
onAnswer()2094     public void onAnswer() {
2095         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
2096     }
2097 
2098     /**
2099      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2100      * a request to reject.
2101      */
onReject()2102     public void onReject() {}
2103 
2104     /**
2105      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2106      * a request to reject with a message.
2107      */
onReject(String replyMessage)2108     public void onReject(String replyMessage) {}
2109 
2110     /**
2111      * Notifies the Connection of a request to silence the ringer.
2112      *
2113      * @hide
2114      */
onSilence()2115     public void onSilence() {}
2116 
2117     /**
2118      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
2119      */
onPostDialContinue(boolean proceed)2120     public void onPostDialContinue(boolean proceed) {}
2121 
2122     /**
2123      * Notifies this Connection of a request to pull an external call to the local device.
2124      * <p>
2125      * The {@link InCallService} issues a request to pull an external call to the local device via
2126      * {@link Call#pullExternalCall()}.
2127      * <p>
2128      * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
2129      * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
2130      * <p>
2131      * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
2132      * @hide
2133      */
onPullExternalCall()2134     public void onPullExternalCall() {}
2135 
2136     /**
2137      * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
2138      * <p>
2139      * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
2140      * <p>
2141      * See also {@link Call#sendCallEvent(String, Bundle)}.
2142      *
2143      * @param event The call event.
2144      * @param extras Extras associated with the call event.
2145      * @hide
2146      */
onCallEvent(String event, Bundle extras)2147     public void onCallEvent(String event, Bundle extras) {}
2148 
2149     /**
2150      * Notifies this {@link Connection} of a change to the extras made outside the
2151      * {@link ConnectionService}.
2152      * <p>
2153      * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
2154      * the {@link android.telecom.Call#putExtras(Bundle)} and
2155      * {@link Call#removeExtras(List)}.
2156      *
2157      * @param extras The new extras bundle.
2158      * @hide
2159      */
onExtrasChanged(Bundle extras)2160     public void onExtrasChanged(Bundle extras) {}
2161 
toLogSafePhoneNumber(String number)2162     static String toLogSafePhoneNumber(String number) {
2163         // For unknown number, log empty string.
2164         if (number == null) {
2165             return "";
2166         }
2167 
2168         if (PII_DEBUG) {
2169             // When PII_DEBUG is true we emit PII.
2170             return number;
2171         }
2172 
2173         // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
2174         // sanitized phone numbers.
2175         StringBuilder builder = new StringBuilder();
2176         for (int i = 0; i < number.length(); i++) {
2177             char c = number.charAt(i);
2178             if (c == '-' || c == '@' || c == '.') {
2179                 builder.append(c);
2180             } else {
2181                 builder.append('x');
2182             }
2183         }
2184         return builder.toString();
2185     }
2186 
setState(int state)2187     private void setState(int state) {
2188         checkImmutable();
2189         if (mState == STATE_DISCONNECTED && mState != state) {
2190             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
2191             return;
2192         }
2193         if (mState != state) {
2194             Log.d(this, "setState: %s", stateToString(state));
2195             mState = state;
2196             onStateChanged(state);
2197             for (Listener l : mListeners) {
2198                 l.onStateChanged(this, state);
2199             }
2200         }
2201     }
2202 
2203     private static class FailureSignalingConnection extends Connection {
2204         private boolean mImmutable = false;
FailureSignalingConnection(DisconnectCause disconnectCause)2205         public FailureSignalingConnection(DisconnectCause disconnectCause) {
2206             setDisconnected(disconnectCause);
2207             mImmutable = true;
2208         }
2209 
checkImmutable()2210         public void checkImmutable() {
2211             if (mImmutable) {
2212                 throw new UnsupportedOperationException("Connection is immutable");
2213             }
2214         }
2215     }
2216 
2217     /**
2218      * Return a {@code Connection} which represents a failed connection attempt. The returned
2219      * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
2220      * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
2221      * <p>
2222      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
2223      * so users of this method need not maintain a reference to its return value to destroy it.
2224      *
2225      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
2226      * @return A {@code Connection} which indicates failure.
2227      */
createFailedConnection(DisconnectCause disconnectCause)2228     public static Connection createFailedConnection(DisconnectCause disconnectCause) {
2229         return new FailureSignalingConnection(disconnectCause);
2230     }
2231 
2232     /**
2233      * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
2234      * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
2235      * this should never be un-@hide-den.
2236      *
2237      * @hide
2238      */
checkImmutable()2239     public void checkImmutable() {}
2240 
2241     /**
2242      * Return a {@code Connection} which represents a canceled connection attempt. The returned
2243      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
2244      * that state. This connection should not be used for anything, and no other
2245      * {@code Connection}s should be attempted.
2246      * <p>
2247      * so users of this method need not maintain a reference to its return value to destroy it.
2248      *
2249      * @return A {@code Connection} which indicates that the underlying connection should
2250      * be canceled.
2251      */
createCanceledConnection()2252     public static Connection createCanceledConnection() {
2253         return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
2254     }
2255 
fireOnConferenceableConnectionsChanged()2256     private final void fireOnConferenceableConnectionsChanged() {
2257         for (Listener l : mListeners) {
2258             l.onConferenceablesChanged(this, getConferenceables());
2259         }
2260     }
2261 
fireConferenceChanged()2262     private final void fireConferenceChanged() {
2263         for (Listener l : mListeners) {
2264             l.onConferenceChanged(this, mConference);
2265         }
2266     }
2267 
clearConferenceableList()2268     private final void clearConferenceableList() {
2269         for (Conferenceable c : mConferenceables) {
2270             if (c instanceof Connection) {
2271                 Connection connection = (Connection) c;
2272                 connection.removeConnectionListener(mConnectionDeathListener);
2273             } else if (c instanceof Conference) {
2274                 Conference conference = (Conference) c;
2275                 conference.removeListener(mConferenceDeathListener);
2276             }
2277         }
2278         mConferenceables.clear();
2279     }
2280 
2281     /**
2282      * Handles a change to extras received from Telecom.
2283      *
2284      * @param extras The new extras.
2285      * @hide
2286      */
handleExtrasChanged(Bundle extras)2287     final void handleExtrasChanged(Bundle extras) {
2288         Bundle b = null;
2289         synchronized (mExtrasLock) {
2290             mExtras = extras;
2291             if (mExtras != null) {
2292                 b = new Bundle(mExtras);
2293             }
2294         }
2295         onExtrasChanged(b);
2296     }
2297 
2298     /**
2299      * Notifies listeners that the merge request failed.
2300      *
2301      * @hide
2302      */
notifyConferenceMergeFailed()2303     protected final void notifyConferenceMergeFailed() {
2304         for (Listener l : mListeners) {
2305             l.onConferenceMergeFailed(this);
2306         }
2307     }
2308 
2309     /**
2310      * Notifies listeners of a change to conference participant(s).
2311      *
2312      * @param conferenceParticipants The participants.
2313      * @hide
2314      */
updateConferenceParticipants( List<ConferenceParticipant> conferenceParticipants)2315     protected final void updateConferenceParticipants(
2316             List<ConferenceParticipant> conferenceParticipants) {
2317         for (Listener l : mListeners) {
2318             l.onConferenceParticipantsChanged(this, conferenceParticipants);
2319         }
2320     }
2321 
2322     /**
2323      * Notifies listeners that a conference call has been started.
2324      * @hide
2325      */
notifyConferenceStarted()2326     protected void notifyConferenceStarted() {
2327         for (Listener l : mListeners) {
2328             l.onConferenceStarted();
2329         }
2330     }
2331 
2332     /**
2333      * Sends an event associated with this {@code Connection}, with associated event extras.
2334      *
2335      * Events are exposed to {@link InCallService} implementations via the
2336      * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)} API.
2337      *
2338      * No assumptions should be made as to how an In-Call UI or service will handle these events.
2339      * Events should be fully qualified (e.g., com.example.event.MY_EVENT) to avoid conflicts.
2340      *
2341      * @param event The connection event.
2342      * @param extras Bundle containing extra information associated with the event.
2343      * @hide
2344      */
sendConnectionEvent(String event, Bundle extras)2345     public void sendConnectionEvent(String event, Bundle extras) {
2346         for (Listener l : mListeners) {
2347             l.onConnectionEvent(this, event, extras);
2348         }
2349     }
2350 }
2351