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.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.SystemApi;
27 import android.app.Notification;
28 import android.content.Intent;
29 import android.hardware.camera2.CameraManager;
30 import android.net.Uri;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.ParcelFileDescriptor;
38 import android.os.RemoteException;
39 import android.util.ArraySet;
40 import android.view.Surface;
41 
42 import java.io.IOException;
43 import java.io.InputStreamReader;
44 import java.io.OutputStreamWriter;
45 import java.lang.annotation.Retention;
46 import java.lang.annotation.RetentionPolicy;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.List;
51 import java.util.Set;
52 import java.util.concurrent.ConcurrentHashMap;
53 
54 /**
55  * Represents a phone call or connection to a remote endpoint that carries voice and/or video
56  * traffic.
57  * <p>
58  * Implementations create a custom subclass of {@code Connection} and return it to the framework
59  * as the return value of
60  * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
61  * or
62  * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
63  * Implementations are then responsible for updating the state of the {@code Connection}, and
64  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
65  * longer used and associated resources may be recovered.
66  * <p>
67  * Subclasses of {@code Connection} override the {@code on*} methods to provide the the
68  * {@link ConnectionService}'s implementation of calling functionality.  The {@code on*} methods are
69  * called by Telecom to inform an instance of a {@code Connection} of actions specific to that
70  * {@code Connection} instance.
71  * <p>
72  * Basic call support requires overriding the following methods: {@link #onAnswer()},
73  * {@link #onDisconnect()}, {@link #onReject()}, {@link #onAbort()}
74  * <p>
75  * Where a {@code Connection} has {@link #CAPABILITY_SUPPORT_HOLD}, the {@link #onHold()} and
76  * {@link #onUnhold()} methods should be overridden to provide hold support for the
77  * {@code Connection}.
78  * <p>
79  * Where a {@code Connection} supports a variation of video calling (e.g. the
80  * {@code CAPABILITY_SUPPORTS_VT_*} capability bits), {@link #onAnswer(int)} should be overridden
81  * to support answering a call as a video call.
82  * <p>
83  * Where a {@code Connection} has {@link #PROPERTY_IS_EXTERNAL_CALL} and
84  * {@link #CAPABILITY_CAN_PULL_CALL}, {@link #onPullExternalCall()} should be overridden to provide
85  * support for pulling the external call.
86  * <p>
87  * Where a {@code Connection} supports conference calling {@link #onSeparate()} should be
88  * overridden.
89  * <p>
90  * There are a number of other {@code on*} methods which a {@code Connection} can choose to
91  * implement, depending on whether it is concerned with the associated calls from Telecom.  If,
92  * for example, call events from a {@link InCallService} are handled,
93  * {@link #onCallEvent(String, Bundle)} should be overridden.  Another example is
94  * {@link #onExtrasChanged(Bundle)}, which should be overridden if the {@code Connection} wishes to
95  * make use of extra information provided via the {@link Call#putExtras(Bundle)} and
96  * {@link Call#removeExtras(String...)} methods.
97  */
98 public abstract class Connection extends Conferenceable {
99 
100     /**
101      * The connection is initializing. This is generally the first state for a {@code Connection}
102      * returned by a {@link ConnectionService}.
103      */
104     public static final int STATE_INITIALIZING = 0;
105 
106     /**
107      * The connection is new and not connected.
108      */
109     public static final int STATE_NEW = 1;
110 
111     /**
112      * An incoming connection is in the ringing state. During this state, the user's ringer or
113      * vibration feature will be activated.
114      */
115     public static final int STATE_RINGING = 2;
116 
117     /**
118      * An outgoing connection is in the dialing state. In this state the other party has not yet
119      * answered the call and the user traditionally hears a ringback tone.
120      */
121     public static final int STATE_DIALING = 3;
122 
123     /**
124      * A connection is active. Both parties are connected to the call and can actively communicate.
125      */
126     public static final int STATE_ACTIVE = 4;
127 
128     /**
129      * A connection is on hold.
130      */
131     public static final int STATE_HOLDING = 5;
132 
133     /**
134      * A connection has been disconnected. This is the final state once the user has been
135      * disconnected from a call either locally, remotely or by an error in the service.
136      */
137     public static final int STATE_DISCONNECTED = 6;
138 
139     /**
140      * The state of an external connection which is in the process of being pulled from a remote
141      * device to the local device.
142      * <p>
143      * A connection can only be in this state if the {@link #PROPERTY_IS_EXTERNAL_CALL} property and
144      * {@link #CAPABILITY_CAN_PULL_CALL} capability bits are set on the connection.
145      */
146     public static final int STATE_PULLING_CALL = 7;
147 
148     /**
149      * Connection can currently be put on hold or unheld. This is distinct from
150      * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
151      * it does not at the moment support the function. This can be true while the call is in the
152      * state {@link #STATE_DIALING}, for example. During this condition, an in-call UI may
153      * display a disabled 'hold' button.
154      */
155     public static final int CAPABILITY_HOLD = 0x00000001;
156 
157     /** Connection supports the hold feature. */
158     public static final int CAPABILITY_SUPPORT_HOLD = 0x00000002;
159 
160     /**
161      * Connections within a conference can be merged. A {@link ConnectionService} has the option to
162      * add a {@link Conference} before the child {@link Connection}s are merged. This is how
163      * CDMA-based {@link Connection}s are implemented. For these unmerged {@link Conference}s, this
164      * capability allows a merge button to be shown while the conference is in the foreground
165      * of the in-call UI.
166      * <p>
167      * This is only intended for use by a {@link Conference}.
168      */
169     public static final int CAPABILITY_MERGE_CONFERENCE = 0x00000004;
170 
171     /**
172      * Connections within a conference can be swapped between foreground and background.
173      * See {@link #CAPABILITY_MERGE_CONFERENCE} for additional information.
174      * <p>
175      * This is only intended for use by a {@link Conference}.
176      */
177     public static final int CAPABILITY_SWAP_CONFERENCE = 0x00000008;
178 
179     /**
180      * @hide
181      */
182     public static final int CAPABILITY_UNUSED = 0x00000010;
183 
184     /** Connection supports responding via text option. */
185     public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020;
186 
187     /** Connection can be muted. */
188     public static final int CAPABILITY_MUTE = 0x00000040;
189 
190     /**
191      * Connection supports conference management. This capability only applies to
192      * {@link Conference}s which can have {@link Connection}s as children.
193      */
194     public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
195 
196     /**
197      * Local device supports receiving video.
198      */
199     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
200 
201     /**
202      * Local device supports transmitting video.
203      */
204     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
205 
206     /**
207      * Local device supports bidirectional video calling.
208      */
209     public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
210             CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
211 
212     /**
213      * Remote device supports receiving video.
214      */
215     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
216 
217     /**
218      * Remote device supports transmitting video.
219      */
220     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
221 
222     /**
223      * Remote device supports bidirectional video calling.
224      */
225     public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
226             CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
227 
228     /**
229      * Connection is able to be separated from its parent {@code Conference}, if any.
230      */
231     public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 0x00001000;
232 
233     /**
234      * Connection is able to be individually disconnected when in a {@code Conference}.
235      */
236     public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
237 
238     /**
239      * Un-used.
240      * @hide
241      */
242     public static final int CAPABILITY_UNUSED_2 = 0x00004000;
243 
244     /**
245      * Un-used.
246      * @hide
247      */
248     public static final int CAPABILITY_UNUSED_3 = 0x00008000;
249 
250     /**
251      * Un-used.
252      * @hide
253      */
254     public static final int CAPABILITY_UNUSED_4 = 0x00010000;
255 
256     /**
257      * Un-used.
258      * @hide
259      */
260     public static final int CAPABILITY_UNUSED_5 = 0x00020000;
261 
262     /**
263      * Speed up audio setup for MT call.
264      * @hide
265      */
266     public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
267 
268     /**
269      * Call can be upgraded to a video call.
270      */
271     public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
272 
273     /**
274      * For video calls, indicates whether the outgoing video for the call can be paused using
275      * the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
276      */
277     public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
278 
279     /**
280      * For a conference, indicates the conference will not have child connections.
281      * <p>
282      * An example of a conference with child connections is a GSM conference call, where the radio
283      * retains connections to the individual participants of the conference.  Another example is an
284      * IMS conference call where conference event package functionality is supported; in this case
285      * the conference server ensures the radio is aware of the participants in the conference, which
286      * are represented by child connections.
287      * <p>
288      * An example of a conference with no child connections is an IMS conference call with no
289      * conference event package support.  Such a conference is represented by the radio as a single
290      * connection to the IMS conference server.
291      * <p>
292      * Indicating whether a conference has children or not is important to help user interfaces
293      * visually represent a conference.  A conference with no children, for example, will have the
294      * conference connection shown in the list of calls on a Bluetooth device, where if the
295      * conference has children, only the children will be shown in the list of calls on a Bluetooth
296      * device.
297      * @hide
298      */
299     public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000;
300 
301     /**
302      * Indicates that the connection itself wants to handle any sort of reply response, rather than
303      * relying on SMS.
304      */
305     public static final int CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION = 0x00400000;
306 
307     /**
308      * When set, prevents a video call from being downgraded to an audio-only call.
309      * <p>
310      * Should be set when the VideoState has the {@link VideoProfile#STATE_TX_ENABLED} or
311      * {@link VideoProfile#STATE_RX_ENABLED} bits set to indicate that the connection cannot be
312      * downgraded from a video call back to a VideoState of
313      * {@link VideoProfile#STATE_AUDIO_ONLY}.
314      * <p>
315      * Intuitively, a call which can be downgraded to audio should also have local and remote
316      * video
317      * capabilities (see {@link #CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL} and
318      * {@link #CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL}).
319      */
320     public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 0x00800000;
321 
322     /**
323      * When set for an external connection, indicates that this {@code Connection} can be pulled
324      * from a remote device to the current device.
325      * <p>
326      * Should only be set on a {@code Connection} where {@link #PROPERTY_IS_EXTERNAL_CALL}
327      * is set.
328      */
329     public static final int CAPABILITY_CAN_PULL_CALL = 0x01000000;
330 
331     //**********************************************************************************************
332     // Next CAPABILITY value: 0x02000000
333     //**********************************************************************************************
334 
335     /**
336      * Indicates that the current device callback number should be shown.
337      *
338      * @hide
339      */
340     public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0;
341 
342     /**
343      * Whether the call is a generic conference, where we do not know the precise state of
344      * participants in the conference (eg. on CDMA).
345      *
346      * @hide
347      */
348     public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1;
349 
350     /**
351      * Connection is using high definition audio.
352      * @hide
353      */
354     public static final int PROPERTY_HIGH_DEF_AUDIO = 1<<2;
355 
356     /**
357      * Connection is using WIFI.
358      * @hide
359      */
360     public static final int PROPERTY_WIFI = 1<<3;
361 
362     /**
363      * When set, indicates that the {@code Connection} does not actually exist locally for the
364      * {@link ConnectionService}.
365      * <p>
366      * Consider, for example, a scenario where a user has two devices with the same phone number.
367      * When a user places a call on one devices, the telephony stack can represent that call on the
368      * other device by adding is to the {@link ConnectionService} with the
369      * {@link #PROPERTY_IS_EXTERNAL_CALL} capability set.
370      * <p>
371      * An {@link ConnectionService} should not assume that all {@link InCallService}s will handle
372      * external connections.  Only those {@link InCallService}s which have the
373      * {@link TelecomManager#METADATA_INCLUDE_EXTERNAL_CALLS} metadata set to {@code true} in its
374      * manifest will see external connections.
375      */
376     public static final int PROPERTY_IS_EXTERNAL_CALL = 1<<4;
377 
378     /**
379      * Indicates that the connection has CDMA Enhanced Voice Privacy enabled.
380      */
381     public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 1<<5;
382 
383     /**
384      * Indicates that the connection represents a downgraded IMS conference.
385      * @hide
386      */
387     public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
388 
389     /**
390      * Set by the framework to indicate that the {@link Connection} originated from a self-managed
391      * {@link ConnectionService}.
392      * <p>
393      * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
394      */
395     public static final int PROPERTY_SELF_MANAGED = 1<<7;
396 
397     /**
398      * When set, indicates that a connection has an active RTT session associated with it.
399      * @hide
400      */
401     public static final int PROPERTY_IS_RTT = 1 << 8;
402 
403     //**********************************************************************************************
404     // Next PROPERTY value: 1<<9
405     //**********************************************************************************************
406 
407     /**
408      * Connection extra key used to store the last forwarded number associated with the current
409      * connection.  Used to communicate to the user interface that the connection was forwarded via
410      * the specified number.
411      */
412     public static final String EXTRA_LAST_FORWARDED_NUMBER =
413             "android.telecom.extra.LAST_FORWARDED_NUMBER";
414 
415     /**
416      * Connection extra key used to store a child number associated with the current connection.
417      * Used to communicate to the user interface that the connection was received via
418      * a child address (i.e. phone number) associated with the {@link PhoneAccount}'s primary
419      * address.
420      */
421     public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
422 
423     /**
424      * Connection extra key used to store the subject for an incoming call.  The user interface can
425      * query this extra and display its contents for incoming calls.  Will only be used if the
426      * {@link PhoneAccount} supports the capability {@link PhoneAccount#CAPABILITY_CALL_SUBJECT}.
427      */
428     public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
429 
430     /**
431      * Boolean connection extra key set on a {@link Connection} in
432      * {@link Connection#STATE_RINGING} state to indicate that answering the call will cause the
433      * current active foreground call to be dropped.
434      */
435     public static final String EXTRA_ANSWERING_DROPS_FG_CALL =
436             "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
437 
438     /**
439      * String connection extra key set on a {@link Connection} in {@link Connection#STATE_RINGING}
440      * state to indicate the name of the third-party app which is responsible for the current
441      * foreground call.
442      * <p>
443      * Used when {@link #EXTRA_ANSWERING_DROPS_FG_CALL} is true to ensure that the default Phone app
444      * is able to inform the user that answering the new incoming call will cause a call owned by
445      * another app to be dropped when the incoming call is answered.
446      */
447     public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME =
448             "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
449 
450     /**
451      * Boolean connection extra key on a {@link Connection} which indicates that adding an
452      * additional call is disallowed.
453      * @hide
454      */
455     public static final String EXTRA_DISABLE_ADD_CALL =
456             "android.telecom.extra.DISABLE_ADD_CALL";
457 
458     /**
459      * String connection extra key on a {@link Connection} or {@link Conference} which contains the
460      * original Connection ID associated with the connection.  Used in
461      * {@link RemoteConnectionService} to track the Connection ID which was originally assigned to a
462      * connection/conference added via
463      * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)} and
464      * {@link ConnectionService#addConference(Conference)} APIs.  This is important to pass to
465      * Telecom for when it deals with RemoteConnections.  When the ConnectionManager wraps the
466      * {@link RemoteConnection} and {@link RemoteConference} and adds it to Telecom, there needs to
467      * be a way to ensure that we don't add the connection again as a duplicate.
468      * <p>
469      * For example, the TelephonyCS calls addExistingConnection for a Connection with ID
470      * {@code TelephonyCS@1}.  The ConnectionManager learns of this via
471      * {@link ConnectionService#onRemoteExistingConnectionAdded(RemoteConnection)}, and wraps this
472      * in a new {@link Connection} which it adds to Telecom via
473      * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)}.  As part of
474      * this process, the wrapped RemoteConnection gets assigned a new ID (e.g. {@code ConnMan@1}).
475      * The TelephonyCS will ALSO try to add the existing connection to Telecom, except with the
476      * ID it originally referred to the connection as.  Thus Telecom needs to know that the
477      * Connection with ID {@code ConnMan@1} is really the same as {@code TelephonyCS@1}.
478      * @hide
479      */
480     public static final String EXTRA_ORIGINAL_CONNECTION_ID =
481             "android.telecom.extra.ORIGINAL_CONNECTION_ID";
482 
483     /**
484      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
485      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
486      * {@link #sendConnectionEvent(String, Bundle)}.
487      * @hide
488      */
489     public static final String EVENT_ON_HOLD_TONE_START =
490             "android.telecom.event.ON_HOLD_TONE_START";
491 
492     /**
493      * Connection event used to inform Telecom that it should stop the on hold tone.  This is used
494      * to stop a tone when the peer puts the current call on hold.  Sent to Telecom via
495      * {@link #sendConnectionEvent(String, Bundle)}.
496      * @hide
497      */
498     public static final String EVENT_ON_HOLD_TONE_END =
499             "android.telecom.event.ON_HOLD_TONE_END";
500 
501     /**
502      * Connection event used to inform {@link InCallService}s when pulling of an external call has
503      * failed.  The user interface should inform the user of the error.
504      * <p>
505      * Expected to be used by the {@link ConnectionService} when the {@link Call#pullExternalCall()}
506      * API is called on a {@link Call} with the properties
507      * {@link Call.Details#PROPERTY_IS_EXTERNAL_CALL} and
508      * {@link Call.Details#CAPABILITY_CAN_PULL_CALL}, but the {@link ConnectionService} could not
509      * pull the external call due to an error condition.
510      * <p>
511      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
512      * expected to be null when this connection event is used.
513      */
514     public static final String EVENT_CALL_PULL_FAILED = "android.telecom.event.CALL_PULL_FAILED";
515 
516     /**
517      * Connection event used to inform {@link InCallService}s when the merging of two calls has
518      * failed. The User Interface should use this message to inform the user of the error.
519      * <p>
520      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
521      * expected to be null when this connection event is used.
522      */
523     public static final String EVENT_CALL_MERGE_FAILED = "android.telecom.event.CALL_MERGE_FAILED";
524 
525     /**
526      * Connection event used to inform {@link InCallService}s when the process of merging a
527      * Connection into a conference has begun.
528      * <p>
529      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
530      * expected to be null when this connection event is used.
531      * @hide
532      */
533     public static final String EVENT_MERGE_START = "android.telecom.event.MERGE_START";
534 
535     /**
536      * Connection event used to inform {@link InCallService}s when the process of merging a
537      * Connection into a conference has completed.
538      * <p>
539      * Sent via {@link #sendConnectionEvent(String, Bundle)}.  The {@link Bundle} parameter is
540      * expected to be null when this connection event is used.
541      * @hide
542      */
543     public static final String EVENT_MERGE_COMPLETE = "android.telecom.event.MERGE_COMPLETE";
544 
545     /**
546      * Connection event used to inform {@link InCallService}s when a call has been put on hold by
547      * the remote party.
548      * <p>
549      * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
550      * call is being held locally on the device.  When a capable {@link ConnectionService} receives
551      * signalling to indicate that the remote party has put the call on hold, it can send this
552      * connection event.
553      * @hide
554      */
555     public static final String EVENT_CALL_REMOTELY_HELD =
556             "android.telecom.event.CALL_REMOTELY_HELD";
557 
558     /**
559      * Connection event used to inform {@link InCallService}s when a call which was remotely held
560      * (see {@link #EVENT_CALL_REMOTELY_HELD}) has been un-held by the remote party.
561      * <p>
562      * This is different than the {@link Connection#STATE_HOLDING} state which indicates that the
563      * call is being held locally on the device.  When a capable {@link ConnectionService} receives
564      * signalling to indicate that the remote party has taken the call off hold, it can send this
565      * connection event.
566      * @hide
567      */
568     public static final String EVENT_CALL_REMOTELY_UNHELD =
569             "android.telecom.event.CALL_REMOTELY_UNHELD";
570 
571     // Flag controlling whether PII is emitted into the logs
572     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
573 
574     /**
575      * Whether the given capabilities support the specified capability.
576      *
577      * @param capabilities A capability bit field.
578      * @param capability The capability to check capabilities for.
579      * @return Whether the specified capability is supported.
580      * @hide
581      */
can(int capabilities, int capability)582     public static boolean can(int capabilities, int capability) {
583         return (capabilities & capability) == capability;
584     }
585 
586     /**
587      * Whether the capabilities of this {@code Connection} supports the specified capability.
588      *
589      * @param capability The capability to check capabilities for.
590      * @return Whether the specified capability is supported.
591      * @hide
592      */
can(int capability)593     public boolean can(int capability) {
594         return can(mConnectionCapabilities, capability);
595     }
596 
597     /**
598      * Removes the specified capability from the set of capabilities of this {@code Connection}.
599      *
600      * @param capability The capability to remove from the set.
601      * @hide
602      */
removeCapability(int capability)603     public void removeCapability(int capability) {
604         mConnectionCapabilities &= ~capability;
605     }
606 
607     /**
608      * Adds the specified capability to the set of capabilities of this {@code Connection}.
609      *
610      * @param capability The capability to add to the set.
611      * @hide
612      */
addCapability(int capability)613     public void addCapability(int capability) {
614         mConnectionCapabilities |= capability;
615     }
616 
617     /**
618      * Renders a set of capability bits ({@code CAPABILITY_*}) as a human readable string.
619      *
620      * @param capabilities A capability bit field.
621      * @return A human readable string representation.
622      */
capabilitiesToString(int capabilities)623     public static String capabilitiesToString(int capabilities) {
624         return capabilitiesToStringInternal(capabilities, true /* isLong */);
625     }
626 
627     /**
628      * Renders a set of capability bits ({@code CAPABILITY_*}) as a *short* human readable
629      * string.
630      *
631      * @param capabilities A capability bit field.
632      * @return A human readable string representation.
633      * @hide
634      */
capabilitiesToStringShort(int capabilities)635     public static String capabilitiesToStringShort(int capabilities) {
636         return capabilitiesToStringInternal(capabilities, false /* isLong */);
637     }
638 
capabilitiesToStringInternal(int capabilities, boolean isLong)639     private static String capabilitiesToStringInternal(int capabilities, boolean isLong) {
640         StringBuilder builder = new StringBuilder();
641         builder.append("[");
642         if (isLong) {
643             builder.append("Capabilities:");
644         }
645 
646         if (can(capabilities, CAPABILITY_HOLD)) {
647             builder.append(isLong ? " CAPABILITY_HOLD" : " hld");
648         }
649         if (can(capabilities, CAPABILITY_SUPPORT_HOLD)) {
650             builder.append(isLong ? " CAPABILITY_SUPPORT_HOLD" : " sup_hld");
651         }
652         if (can(capabilities, CAPABILITY_MERGE_CONFERENCE)) {
653             builder.append(isLong ? " CAPABILITY_MERGE_CONFERENCE" : " mrg_cnf");
654         }
655         if (can(capabilities, CAPABILITY_SWAP_CONFERENCE)) {
656             builder.append(isLong ? " CAPABILITY_SWAP_CONFERENCE" : " swp_cnf");
657         }
658         if (can(capabilities, CAPABILITY_RESPOND_VIA_TEXT)) {
659             builder.append(isLong ? " CAPABILITY_RESPOND_VIA_TEXT" : " txt");
660         }
661         if (can(capabilities, CAPABILITY_MUTE)) {
662             builder.append(isLong ? " CAPABILITY_MUTE" : " mut");
663         }
664         if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
665             builder.append(isLong ? " CAPABILITY_MANAGE_CONFERENCE" : " mng_cnf");
666         }
667         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
668             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_RX" : " VTlrx");
669         }
670         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
671             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_TX" : " VTltx");
672         }
673         if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
674             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL" : " VTlbi");
675         }
676         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
677             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_RX" : " VTrrx");
678         }
679         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
680             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_TX" : " VTrtx");
681         }
682         if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
683             builder.append(isLong ? " CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL" : " VTrbi");
684         }
685         if (can(capabilities, CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO)) {
686             builder.append(isLong ? " CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO" : " !v2a");
687         }
688         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
689             builder.append(isLong ? " CAPABILITY_SPEED_UP_MT_AUDIO" : " spd_aud");
690         }
691         if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
692             builder.append(isLong ? " CAPABILITY_CAN_UPGRADE_TO_VIDEO" : " a2v");
693         }
694         if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
695             builder.append(isLong ? " CAPABILITY_CAN_PAUSE_VIDEO" : " paus_VT");
696         }
697         if (can(capabilities, CAPABILITY_CONFERENCE_HAS_NO_CHILDREN)) {
698             builder.append(isLong ? " CAPABILITY_SINGLE_PARTY_CONFERENCE" : " 1p_cnf");
699         }
700         if (can(capabilities, CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
701             builder.append(isLong ? " CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION" : " rsp_by_con");
702         }
703         if (can(capabilities, CAPABILITY_CAN_PULL_CALL)) {
704             builder.append(isLong ? " CAPABILITY_CAN_PULL_CALL" : " pull");
705         }
706 
707         builder.append("]");
708         return builder.toString();
709     }
710 
711     /**
712      * Renders a set of property bits ({@code PROPERTY_*}) as a human readable string.
713      *
714      * @param properties A property bit field.
715      * @return A human readable string representation.
716      */
propertiesToString(int properties)717     public static String propertiesToString(int properties) {
718         return propertiesToStringInternal(properties, true /* isLong */);
719     }
720 
721     /**
722      * Renders a set of property bits ({@code PROPERTY_*}) as a *short* human readable string.
723      *
724      * @param properties A property bit field.
725      * @return A human readable string representation.
726      * @hide
727      */
propertiesToStringShort(int properties)728     public static String propertiesToStringShort(int properties) {
729         return propertiesToStringInternal(properties, false /* isLong */);
730     }
731 
propertiesToStringInternal(int properties, boolean isLong)732     private static String propertiesToStringInternal(int properties, boolean isLong) {
733         StringBuilder builder = new StringBuilder();
734         builder.append("[");
735         if (isLong) {
736             builder.append("Properties:");
737         }
738 
739         if (can(properties, PROPERTY_SELF_MANAGED)) {
740             builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
741         }
742 
743         if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
744             builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
745         }
746 
747         if (can(properties, PROPERTY_HIGH_DEF_AUDIO)) {
748             builder.append(isLong ? " PROPERTY_HIGH_DEF_AUDIO" : " HD");
749         }
750 
751         if (can(properties, PROPERTY_WIFI)) {
752             builder.append(isLong ? " PROPERTY_WIFI" : " wifi");
753         }
754 
755         if (can(properties, PROPERTY_GENERIC_CONFERENCE)) {
756             builder.append(isLong ? " PROPERTY_GENERIC_CONFERENCE" : " gen_conf");
757         }
758 
759         if (can(properties, PROPERTY_IS_EXTERNAL_CALL)) {
760             builder.append(isLong ? " PROPERTY_IS_EXTERNAL_CALL" : " xtrnl");
761         }
762 
763         if (can(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
764             builder.append(isLong ? " PROPERTY_HAS_CDMA_VOICE_PRIVACY" : " priv");
765         }
766 
767         builder.append("]");
768         return builder.toString();
769     }
770 
771     /** @hide */
772     public abstract static class Listener {
onStateChanged(Connection c, int state)773         public void onStateChanged(Connection c, int state) {}
onAddressChanged(Connection c, Uri newAddress, int presentation)774         public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
onCallerDisplayNameChanged( Connection c, String callerDisplayName, int presentation)775         public void onCallerDisplayNameChanged(
776                 Connection c, String callerDisplayName, int presentation) {}
onVideoStateChanged(Connection c, int videoState)777         public void onVideoStateChanged(Connection c, int videoState) {}
onDisconnected(Connection c, DisconnectCause disconnectCause)778         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
onPostDialWait(Connection c, String remaining)779         public void onPostDialWait(Connection c, String remaining) {}
onPostDialChar(Connection c, char nextChar)780         public void onPostDialChar(Connection c, char nextChar) {}
onRingbackRequested(Connection c, boolean ringback)781         public void onRingbackRequested(Connection c, boolean ringback) {}
onDestroyed(Connection c)782         public void onDestroyed(Connection c) {}
onConnectionCapabilitiesChanged(Connection c, int capabilities)783         public void onConnectionCapabilitiesChanged(Connection c, int capabilities) {}
onConnectionPropertiesChanged(Connection c, int properties)784         public void onConnectionPropertiesChanged(Connection c, int properties) {}
onSupportedAudioRoutesChanged(Connection c, int supportedAudioRoutes)785         public void onSupportedAudioRoutesChanged(Connection c, int supportedAudioRoutes) {}
onVideoProviderChanged( Connection c, VideoProvider videoProvider)786         public void onVideoProviderChanged(
787                 Connection c, VideoProvider videoProvider) {}
onAudioModeIsVoipChanged(Connection c, boolean isVoip)788         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
onStatusHintsChanged(Connection c, StatusHints statusHints)789         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
onConferenceablesChanged( Connection c, List<Conferenceable> conferenceables)790         public void onConferenceablesChanged(
791                 Connection c, List<Conferenceable> conferenceables) {}
onConferenceChanged(Connection c, Conference conference)792         public void onConferenceChanged(Connection c, Conference conference) {}
793         /** @hide */
onConferenceParticipantsChanged(Connection c, List<ConferenceParticipant> participants)794         public void onConferenceParticipantsChanged(Connection c,
795                 List<ConferenceParticipant> participants) {}
onConferenceStarted()796         public void onConferenceStarted() {}
onConferenceMergeFailed(Connection c)797         public void onConferenceMergeFailed(Connection c) {}
onExtrasChanged(Connection c, Bundle extras)798         public void onExtrasChanged(Connection c, Bundle extras) {}
onExtrasRemoved(Connection c, List<String> keys)799         public void onExtrasRemoved(Connection c, List<String> keys) {}
onConnectionEvent(Connection c, String event, Bundle extras)800         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
801         /** @hide */
onConferenceSupportedChanged(Connection c, boolean isConferenceSupported)802         public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
onAudioRouteChanged(Connection c, int audioRoute)803         public void onAudioRouteChanged(Connection c, int audioRoute) {}
onRttInitiationSuccess(Connection c)804         public void onRttInitiationSuccess(Connection c) {}
onRttInitiationFailure(Connection c, int reason)805         public void onRttInitiationFailure(Connection c, int reason) {}
onRttSessionRemotelyTerminated(Connection c)806         public void onRttSessionRemotelyTerminated(Connection c) {}
onRemoteRttRequest(Connection c)807         public void onRemoteRttRequest(Connection c) {}
808     }
809 
810     /**
811      * Provides methods to read and write RTT data to/from the in-call app.
812      * @hide
813      */
814     public static final class RttTextStream {
815         private static final int READ_BUFFER_SIZE = 1000;
816         private final InputStreamReader mPipeFromInCall;
817         private final OutputStreamWriter mPipeToInCall;
818         private final ParcelFileDescriptor mFdFromInCall;
819         private final ParcelFileDescriptor mFdToInCall;
820         private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
821 
822         /**
823          * @hide
824          */
RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall)825         public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
826             mFdFromInCall = fromInCall;
827             mFdToInCall = toInCall;
828             mPipeFromInCall = new InputStreamReader(
829                     new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
830             mPipeToInCall = new OutputStreamWriter(
831                     new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
832         }
833 
834         /**
835          * Writes the string {@param input} into the text stream to the UI for this RTT call. Since
836          * RTT transmits text in real-time, this method should be called as often as text snippets
837          * are received from the remote user, even if it is only one character.
838          *
839          * This method is not thread-safe -- calling it from multiple threads simultaneously may
840          * lead to interleaved text.
841          * @param input The message to send to the in-call app.
842          */
write(String input)843         public void write(String input) throws IOException {
844             mPipeToInCall.write(input);
845             mPipeToInCall.flush();
846         }
847 
848 
849         /**
850          * Reads a string from the in-call app, blocking if there is no data available. Returns
851          * {@code null} if the RTT conversation has been terminated and there is no further data
852          * to read.
853          *
854          * This method is not thread-safe -- calling it from multiple threads simultaneously may
855          * lead to interleaved text.
856          * @return A string containing text entered by the user, or {@code null} if the
857          * conversation has been terminated or if there was an error while reading.
858          */
read()859         public String read() {
860             try {
861                 int numRead = mPipeFromInCall.read(mReadBuffer, 0, READ_BUFFER_SIZE);
862                 if (numRead < 0) {
863                     return null;
864                 }
865                 return new String(mReadBuffer, 0, numRead);
866             } catch (IOException e) {
867                 Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e);
868                 return null;
869             }
870         }
871 
872         /** @hide */
getFdFromInCall()873         public ParcelFileDescriptor getFdFromInCall() {
874             return mFdFromInCall;
875         }
876 
877         /** @hide */
getFdToInCall()878         public ParcelFileDescriptor getFdToInCall() {
879             return mFdToInCall;
880         }
881     }
882 
883     /**
884      * Provides constants to represent the results of responses to session modify requests sent via
885      * {@link Call#sendRttRequest()}
886      */
887     public static final class RttModifyStatus {
RttModifyStatus()888         private RttModifyStatus() {}
889         /**
890          * Session modify request was successful.
891          */
892         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
893 
894         /**
895          * Session modify request failed.
896          */
897         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
898 
899         /**
900          * Session modify request ignored due to invalid parameters.
901          */
902         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
903 
904         /**
905          * Session modify request timed out.
906          */
907         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
908 
909         /**
910          * Session modify request rejected by remote user.
911          */
912         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
913     }
914 
915     /**
916      * Provides a means of controlling the video session associated with a {@link Connection}.
917      * <p>
918      * Implementations create a custom subclass of {@link VideoProvider} and the
919      * {@link ConnectionService} creates an instance sets it on the {@link Connection} using
920      * {@link Connection#setVideoProvider(VideoProvider)}.  Any connection which supports video
921      * should set the {@link VideoProvider}.
922      * <p>
923      * The {@link VideoProvider} serves two primary purposes: it provides a means for Telecom and
924      * {@link InCallService} implementations to issue requests related to the video session;
925      * it provides a means for the {@link ConnectionService} to report events and information
926      * related to the video session to Telecom and the {@link InCallService} implementations.
927      * <p>
928      * {@link InCallService} implementations interact with the {@link VideoProvider} via
929      * {@link android.telecom.InCallService.VideoCall}.
930      */
931     public static abstract class VideoProvider {
932         /**
933          * Video is not being received (no protocol pause was issued).
934          * @see #handleCallSessionEvent(int)
935          */
936         public static final int SESSION_EVENT_RX_PAUSE = 1;
937 
938         /**
939          * Video reception has resumed after a {@link #SESSION_EVENT_RX_PAUSE}.
940          * @see #handleCallSessionEvent(int)
941          */
942         public static final int SESSION_EVENT_RX_RESUME = 2;
943 
944         /**
945          * Video transmission has begun. This occurs after a negotiated start of video transmission
946          * when the underlying protocol has actually begun transmitting video to the remote party.
947          * @see #handleCallSessionEvent(int)
948          */
949         public static final int SESSION_EVENT_TX_START = 3;
950 
951         /**
952          * Video transmission has stopped. This occurs after a negotiated stop of video transmission
953          * when the underlying protocol has actually stopped transmitting video to the remote party.
954          * @see #handleCallSessionEvent(int)
955          */
956         public static final int SESSION_EVENT_TX_STOP = 4;
957 
958         /**
959          * A camera failure has occurred for the selected camera.  The {@link VideoProvider} can use
960          * this as a cue to inform the user the camera is not available.
961          * @see #handleCallSessionEvent(int)
962          */
963         public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
964 
965         /**
966          * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready
967          * for operation.  The {@link VideoProvider} can use this as a cue to inform the user that
968          * the camera has become available again.
969          * @see #handleCallSessionEvent(int)
970          */
971         public static final int SESSION_EVENT_CAMERA_READY = 6;
972 
973         /**
974          * Session event raised by Telecom when
975          * {@link android.telecom.InCallService.VideoCall#setCamera(String)} is called and the
976          * caller does not have the necessary {@link android.Manifest.permission#CAMERA} permission.
977          * @see #handleCallSessionEvent(int)
978          */
979         public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7;
980 
981         /**
982          * Session modify request was successful.
983          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
984          */
985         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
986 
987         /**
988          * Session modify request failed.
989          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
990          */
991         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
992 
993         /**
994          * Session modify request ignored due to invalid parameters.
995          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
996          */
997         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
998 
999         /**
1000          * Session modify request timed out.
1001          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
1002          */
1003         public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
1004 
1005         /**
1006          * Session modify request rejected by remote user.
1007          * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)
1008          */
1009         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
1010 
1011         private static final int MSG_ADD_VIDEO_CALLBACK = 1;
1012         private static final int MSG_SET_CAMERA = 2;
1013         private static final int MSG_SET_PREVIEW_SURFACE = 3;
1014         private static final int MSG_SET_DISPLAY_SURFACE = 4;
1015         private static final int MSG_SET_DEVICE_ORIENTATION = 5;
1016         private static final int MSG_SET_ZOOM = 6;
1017         private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
1018         private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
1019         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
1020         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
1021         private static final int MSG_SET_PAUSE_IMAGE = 11;
1022         private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
1023 
1024         private static final String SESSION_EVENT_RX_PAUSE_STR = "RX_PAUSE";
1025         private static final String SESSION_EVENT_RX_RESUME_STR = "RX_RESUME";
1026         private static final String SESSION_EVENT_TX_START_STR = "TX_START";
1027         private static final String SESSION_EVENT_TX_STOP_STR = "TX_STOP";
1028         private static final String SESSION_EVENT_CAMERA_FAILURE_STR = "CAMERA_FAIL";
1029         private static final String SESSION_EVENT_CAMERA_READY_STR = "CAMERA_READY";
1030         private static final String SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR =
1031                 "CAMERA_PERMISSION_ERROR";
1032         private static final String SESSION_EVENT_UNKNOWN_STR = "UNKNOWN";
1033 
1034         private VideoProvider.VideoProviderHandler mMessageHandler;
1035         private final VideoProvider.VideoProviderBinder mBinder;
1036 
1037         /**
1038          * Stores a list of the video callbacks, keyed by IBinder.
1039          *
1040          * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
1041          * load factor before resizing, 1 means we only expect a single thread to
1042          * access the map so make only a single shard
1043          */
1044         private ConcurrentHashMap<IBinder, IVideoCallback> mVideoCallbacks =
1045                 new ConcurrentHashMap<IBinder, IVideoCallback>(8, 0.9f, 1);
1046 
1047         /**
1048          * Default handler used to consolidate binder method calls onto a single thread.
1049          */
1050         private final class VideoProviderHandler extends Handler {
VideoProviderHandler()1051             public VideoProviderHandler() {
1052                 super();
1053             }
1054 
VideoProviderHandler(Looper looper)1055             public VideoProviderHandler(Looper looper) {
1056                 super(looper);
1057             }
1058 
1059             @Override
handleMessage(Message msg)1060             public void handleMessage(Message msg) {
1061                 switch (msg.what) {
1062                     case MSG_ADD_VIDEO_CALLBACK: {
1063                         IBinder binder = (IBinder) msg.obj;
1064                         IVideoCallback callback = IVideoCallback.Stub
1065                                 .asInterface((IBinder) msg.obj);
1066                         if (callback == null) {
1067                             Log.w(this, "addVideoProvider - skipped; callback is null.");
1068                             break;
1069                         }
1070 
1071                         if (mVideoCallbacks.containsKey(binder)) {
1072                             Log.i(this, "addVideoProvider - skipped; already present.");
1073                             break;
1074                         }
1075                         mVideoCallbacks.put(binder, callback);
1076                         break;
1077                     }
1078                     case MSG_REMOVE_VIDEO_CALLBACK: {
1079                         IBinder binder = (IBinder) msg.obj;
1080                         IVideoCallback callback = IVideoCallback.Stub
1081                                 .asInterface((IBinder) msg.obj);
1082                         if (!mVideoCallbacks.containsKey(binder)) {
1083                             Log.i(this, "removeVideoProvider - skipped; not present.");
1084                             break;
1085                         }
1086                         mVideoCallbacks.remove(binder);
1087                         break;
1088                     }
1089                     case MSG_SET_CAMERA:
1090                     {
1091                         SomeArgs args = (SomeArgs) msg.obj;
1092                         try {
1093                             onSetCamera((String) args.arg1);
1094                             onSetCamera((String) args.arg1, (String) args.arg2, args.argi1,
1095                                     args.argi2, args.argi3);
1096                         } finally {
1097                             args.recycle();
1098                         }
1099                     }
1100                     break;
1101                     case MSG_SET_PREVIEW_SURFACE:
1102                         onSetPreviewSurface((Surface) msg.obj);
1103                         break;
1104                     case MSG_SET_DISPLAY_SURFACE:
1105                         onSetDisplaySurface((Surface) msg.obj);
1106                         break;
1107                     case MSG_SET_DEVICE_ORIENTATION:
1108                         onSetDeviceOrientation(msg.arg1);
1109                         break;
1110                     case MSG_SET_ZOOM:
1111                         onSetZoom((Float) msg.obj);
1112                         break;
1113                     case MSG_SEND_SESSION_MODIFY_REQUEST: {
1114                         SomeArgs args = (SomeArgs) msg.obj;
1115                         try {
1116                             onSendSessionModifyRequest((VideoProfile) args.arg1,
1117                                     (VideoProfile) args.arg2);
1118                         } finally {
1119                             args.recycle();
1120                         }
1121                         break;
1122                     }
1123                     case MSG_SEND_SESSION_MODIFY_RESPONSE:
1124                         onSendSessionModifyResponse((VideoProfile) msg.obj);
1125                         break;
1126                     case MSG_REQUEST_CAMERA_CAPABILITIES:
1127                         onRequestCameraCapabilities();
1128                         break;
1129                     case MSG_REQUEST_CONNECTION_DATA_USAGE:
1130                         onRequestConnectionDataUsage();
1131                         break;
1132                     case MSG_SET_PAUSE_IMAGE:
1133                         onSetPauseImage((Uri) msg.obj);
1134                         break;
1135                     default:
1136                         break;
1137                 }
1138             }
1139         }
1140 
1141         /**
1142          * IVideoProvider stub implementation.
1143          */
1144         private final class VideoProviderBinder extends IVideoProvider.Stub {
addVideoCallback(IBinder videoCallbackBinder)1145             public void addVideoCallback(IBinder videoCallbackBinder) {
1146                 mMessageHandler.obtainMessage(
1147                         MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
1148             }
1149 
removeVideoCallback(IBinder videoCallbackBinder)1150             public void removeVideoCallback(IBinder videoCallbackBinder) {
1151                 mMessageHandler.obtainMessage(
1152                         MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
1153             }
1154 
setCamera(String cameraId, String callingPackageName, int targetSdkVersion)1155             public void setCamera(String cameraId, String callingPackageName,
1156                                   int targetSdkVersion) {
1157 
1158                 SomeArgs args = SomeArgs.obtain();
1159                 args.arg1 = cameraId;
1160                 // Propagate the calling package; originally determined in
1161                 // android.telecom.InCallService.VideoCall#setCamera(String) from the calling
1162                 // process.
1163                 args.arg2 = callingPackageName;
1164                 // Pass along the uid and pid of the calling app; this gets lost when we put the
1165                 // message onto the handler.  These are required for Telecom to perform a permission
1166                 // check to see if the calling app is able to use the camera.
1167                 args.argi1 = Binder.getCallingUid();
1168                 args.argi2 = Binder.getCallingPid();
1169                 // Pass along the target SDK version of the calling InCallService.  This is used to
1170                 // maintain backwards compatibility of the API for older callers.
1171                 args.argi3 = targetSdkVersion;
1172                 mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
1173             }
1174 
setPreviewSurface(Surface surface)1175             public void setPreviewSurface(Surface surface) {
1176                 mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
1177             }
1178 
setDisplaySurface(Surface surface)1179             public void setDisplaySurface(Surface surface) {
1180                 mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
1181             }
1182 
setDeviceOrientation(int rotation)1183             public void setDeviceOrientation(int rotation) {
1184                 mMessageHandler.obtainMessage(
1185                         MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
1186             }
1187 
setZoom(float value)1188             public void setZoom(float value) {
1189                 mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
1190             }
1191 
sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)1192             public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
1193                 SomeArgs args = SomeArgs.obtain();
1194                 args.arg1 = fromProfile;
1195                 args.arg2 = toProfile;
1196                 mMessageHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
1197             }
1198 
sendSessionModifyResponse(VideoProfile responseProfile)1199             public void sendSessionModifyResponse(VideoProfile responseProfile) {
1200                 mMessageHandler.obtainMessage(
1201                         MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
1202             }
1203 
requestCameraCapabilities()1204             public void requestCameraCapabilities() {
1205                 mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
1206             }
1207 
requestCallDataUsage()1208             public void requestCallDataUsage() {
1209                 mMessageHandler.obtainMessage(MSG_REQUEST_CONNECTION_DATA_USAGE).sendToTarget();
1210             }
1211 
setPauseImage(Uri uri)1212             public void setPauseImage(Uri uri) {
1213                 mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
1214             }
1215         }
1216 
VideoProvider()1217         public VideoProvider() {
1218             mBinder = new VideoProvider.VideoProviderBinder();
1219             mMessageHandler = new VideoProvider.VideoProviderHandler(Looper.getMainLooper());
1220         }
1221 
1222         /**
1223          * Creates an instance of the {@link VideoProvider}, specifying the looper to use.
1224          *
1225          * @param looper The looper.
1226          * @hide
1227          */
VideoProvider(Looper looper)1228         public VideoProvider(Looper looper) {
1229             mBinder = new VideoProvider.VideoProviderBinder();
1230             mMessageHandler = new VideoProvider.VideoProviderHandler(looper);
1231         }
1232 
1233         /**
1234          * Returns binder object which can be used across IPC methods.
1235          * @hide
1236          */
getInterface()1237         public final IVideoProvider getInterface() {
1238             return mBinder;
1239         }
1240 
1241         /**
1242          * Sets the camera to be used for the outgoing video.
1243          * <p>
1244          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
1245          * camera via
1246          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
1247          * <p>
1248          * Sent from the {@link InCallService} via
1249          * {@link InCallService.VideoCall#setCamera(String)}.
1250          *
1251          * @param cameraId The id of the camera (use ids as reported by
1252          * {@link CameraManager#getCameraIdList()}).
1253          */
onSetCamera(String cameraId)1254         public abstract void onSetCamera(String cameraId);
1255 
1256         /**
1257          * Sets the camera to be used for the outgoing video.
1258          * <p>
1259          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
1260          * camera via
1261          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
1262          * <p>
1263          * This prototype is used internally to ensure that the calling package name, UID and PID
1264          * are sent to Telecom so that can perform a camera permission check on the caller.
1265          * <p>
1266          * Sent from the {@link InCallService} via
1267          * {@link InCallService.VideoCall#setCamera(String)}.
1268          *
1269          * @param cameraId The id of the camera (use ids as reported by
1270          * {@link CameraManager#getCameraIdList()}).
1271          * @param callingPackageName The AppOpps package name of the caller.
1272          * @param callingUid The UID of the caller.
1273          * @param callingPid The PID of the caller.
1274          * @param targetSdkVersion The target SDK version of the caller.
1275          * @hide
1276          */
onSetCamera(String cameraId, String callingPackageName, int callingUid, int callingPid, int targetSdkVersion)1277         public void onSetCamera(String cameraId, String callingPackageName, int callingUid,
1278                 int callingPid, int targetSdkVersion) {}
1279 
1280         /**
1281          * Sets the surface to be used for displaying a preview of what the user's camera is
1282          * currently capturing.  When video transmission is enabled, this is the video signal which
1283          * is sent to the remote device.
1284          * <p>
1285          * Sent from the {@link InCallService} via
1286          * {@link InCallService.VideoCall#setPreviewSurface(Surface)}.
1287          *
1288          * @param surface The {@link Surface}.
1289          */
onSetPreviewSurface(Surface surface)1290         public abstract void onSetPreviewSurface(Surface surface);
1291 
1292         /**
1293          * Sets the surface to be used for displaying the video received from the remote device.
1294          * <p>
1295          * Sent from the {@link InCallService} via
1296          * {@link InCallService.VideoCall#setDisplaySurface(Surface)}.
1297          *
1298          * @param surface The {@link Surface}.
1299          */
onSetDisplaySurface(Surface surface)1300         public abstract void onSetDisplaySurface(Surface surface);
1301 
1302         /**
1303          * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
1304          * the device is 0 degrees.
1305          * <p>
1306          * Sent from the {@link InCallService} via
1307          * {@link InCallService.VideoCall#setDeviceOrientation(int)}.
1308          *
1309          * @param rotation The device orientation, in degrees.
1310          */
onSetDeviceOrientation(int rotation)1311         public abstract void onSetDeviceOrientation(int rotation);
1312 
1313         /**
1314          * Sets camera zoom ratio.
1315          * <p>
1316          * Sent from the {@link InCallService} via {@link InCallService.VideoCall#setZoom(float)}.
1317          *
1318          * @param value The camera zoom ratio.
1319          */
onSetZoom(float value)1320         public abstract void onSetZoom(float value);
1321 
1322         /**
1323          * Issues a request to modify the properties of the current video session.
1324          * <p>
1325          * Example scenarios include: requesting an audio-only call to be upgraded to a
1326          * bi-directional video call, turning on or off the user's camera, sending a pause signal
1327          * when the {@link InCallService} is no longer the foreground application.
1328          * <p>
1329          * If the {@link VideoProvider} determines a request to be invalid, it should call
1330          * {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)} to report the
1331          * invalid request back to the {@link InCallService}.
1332          * <p>
1333          * Where a request requires confirmation from the user of the peer device, the
1334          * {@link VideoProvider} must communicate the request to the peer device and handle the
1335          * user's response.  {@link #receiveSessionModifyResponse(int, VideoProfile, VideoProfile)}
1336          * is used to inform the {@link InCallService} of the result of the request.
1337          * <p>
1338          * Sent from the {@link InCallService} via
1339          * {@link InCallService.VideoCall#sendSessionModifyRequest(VideoProfile)}.
1340          *
1341          * @param fromProfile The video profile prior to the request.
1342          * @param toProfile The video profile with the requested changes made.
1343          */
onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile)1344         public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
1345                 VideoProfile toProfile);
1346 
1347         /**
1348          * Provides a response to a request to change the current video session properties.
1349          * <p>
1350          * For example, if the peer requests and upgrade from an audio-only call to a bi-directional
1351          * video call, could decline the request and keep the call as audio-only.
1352          * In such a scenario, the {@code responseProfile} would have a video state of
1353          * {@link VideoProfile#STATE_AUDIO_ONLY}.  If the user had decided to accept the request,
1354          * the video state would be {@link VideoProfile#STATE_BIDIRECTIONAL}.
1355          * <p>
1356          * Sent from the {@link InCallService} via
1357          * {@link InCallService.VideoCall#sendSessionModifyResponse(VideoProfile)} in response to
1358          * a {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)}
1359          * callback.
1360          *
1361          * @param responseProfile The response video profile.
1362          */
onSendSessionModifyResponse(VideoProfile responseProfile)1363         public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
1364 
1365         /**
1366          * Issues a request to the {@link VideoProvider} to retrieve the camera capabilities.
1367          * <p>
1368          * The {@link VideoProvider} should respond by communicating the capabilities of the chosen
1369          * camera via
1370          * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}.
1371          * <p>
1372          * Sent from the {@link InCallService} via
1373          * {@link InCallService.VideoCall#requestCameraCapabilities()}.
1374          */
onRequestCameraCapabilities()1375         public abstract void onRequestCameraCapabilities();
1376 
1377         /**
1378          * Issues a request to the {@link VideoProvider} to retrieve the current data usage for the
1379          * video component of the current {@link Connection}.
1380          * <p>
1381          * The {@link VideoProvider} should respond by communicating current data usage, in bytes,
1382          * via {@link VideoProvider#setCallDataUsage(long)}.
1383          * <p>
1384          * Sent from the {@link InCallService} via
1385          * {@link InCallService.VideoCall#requestCallDataUsage()}.
1386          */
onRequestConnectionDataUsage()1387         public abstract void onRequestConnectionDataUsage();
1388 
1389         /**
1390          * Provides the {@link VideoProvider} with the {@link Uri} of an image to be displayed to
1391          * the peer device when the video signal is paused.
1392          * <p>
1393          * Sent from the {@link InCallService} via
1394          * {@link InCallService.VideoCall#setPauseImage(Uri)}.
1395          *
1396          * @param uri URI of image to display.
1397          */
onSetPauseImage(Uri uri)1398         public abstract void onSetPauseImage(Uri uri);
1399 
1400         /**
1401          * Used to inform listening {@link InCallService} implementations when the
1402          * {@link VideoProvider} receives a session modification request.
1403          * <p>
1404          * Received by the {@link InCallService} via
1405          * {@link InCallService.VideoCall.Callback#onSessionModifyRequestReceived(VideoProfile)},
1406          *
1407          * @param videoProfile The requested video profile.
1408          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1409          */
receiveSessionModifyRequest(VideoProfile videoProfile)1410         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
1411             if (mVideoCallbacks != null) {
1412                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1413                     try {
1414                         callback.receiveSessionModifyRequest(videoProfile);
1415                     } catch (RemoteException ignored) {
1416                         Log.w(this, "receiveSessionModifyRequest callback failed", ignored);
1417                     }
1418                 }
1419             }
1420         }
1421 
1422         /**
1423          * Used to inform listening {@link InCallService} implementations when the
1424          * {@link VideoProvider} receives a response to a session modification request.
1425          * <p>
1426          * Received by the {@link InCallService} via
1427          * {@link InCallService.VideoCall.Callback#onSessionModifyResponseReceived(int,
1428          * VideoProfile, VideoProfile)}.
1429          *
1430          * @param status Status of the session modify request.  Valid values are
1431          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
1432          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
1433          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID},
1434          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_TIMED_OUT},
1435          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE}
1436          * @param requestedProfile The original request which was sent to the peer device.
1437          * @param responseProfile The actual profile changes agreed to by the peer device.
1438          * @see #onSendSessionModifyRequest(VideoProfile, VideoProfile)
1439          */
receiveSessionModifyResponse(int status, VideoProfile requestedProfile, VideoProfile responseProfile)1440         public void receiveSessionModifyResponse(int status,
1441                 VideoProfile requestedProfile, VideoProfile responseProfile) {
1442             if (mVideoCallbacks != null) {
1443                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1444                     try {
1445                         callback.receiveSessionModifyResponse(status, requestedProfile,
1446                                 responseProfile);
1447                     } catch (RemoteException ignored) {
1448                         Log.w(this, "receiveSessionModifyResponse callback failed", ignored);
1449                     }
1450                 }
1451             }
1452         }
1453 
1454         /**
1455          * Used to inform listening {@link InCallService} implementations when the
1456          * {@link VideoProvider} reports a call session event.
1457          * <p>
1458          * Received by the {@link InCallService} via
1459          * {@link InCallService.VideoCall.Callback#onCallSessionEvent(int)}.
1460          *
1461          * @param event The event.  Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
1462          *      {@link VideoProvider#SESSION_EVENT_RX_RESUME},
1463          *      {@link VideoProvider#SESSION_EVENT_TX_START},
1464          *      {@link VideoProvider#SESSION_EVENT_TX_STOP},
1465          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE},
1466          *      {@link VideoProvider#SESSION_EVENT_CAMERA_READY},
1467          *      {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}.
1468          */
handleCallSessionEvent(int event)1469         public void handleCallSessionEvent(int event) {
1470             if (mVideoCallbacks != null) {
1471                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1472                     try {
1473                         callback.handleCallSessionEvent(event);
1474                     } catch (RemoteException ignored) {
1475                         Log.w(this, "handleCallSessionEvent callback failed", ignored);
1476                     }
1477                 }
1478             }
1479         }
1480 
1481         /**
1482          * Used to inform listening {@link InCallService} implementations when the dimensions of the
1483          * peer's video have changed.
1484          * <p>
1485          * This could occur if, for example, the peer rotates their device, changing the aspect
1486          * ratio of the video, or if the user switches between the back and front cameras.
1487          * <p>
1488          * Received by the {@link InCallService} via
1489          * {@link InCallService.VideoCall.Callback#onPeerDimensionsChanged(int, int)}.
1490          *
1491          * @param width  The updated peer video width.
1492          * @param height The updated peer video height.
1493          */
changePeerDimensions(int width, int height)1494         public void changePeerDimensions(int width, int height) {
1495             if (mVideoCallbacks != null) {
1496                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1497                     try {
1498                         callback.changePeerDimensions(width, height);
1499                     } catch (RemoteException ignored) {
1500                         Log.w(this, "changePeerDimensions callback failed", ignored);
1501                     }
1502                 }
1503             }
1504         }
1505 
1506         /**
1507          * Used to inform listening {@link InCallService} implementations when the data usage of the
1508          * video associated with the current {@link Connection} has changed.
1509          * <p>
1510          * This could be in response to a preview request via
1511          * {@link #onRequestConnectionDataUsage()}, or as a periodic update by the
1512          * {@link VideoProvider}.  Where periodic updates of data usage are provided, they should be
1513          * provided at most for every 1 MB of data transferred and no more than once every 10 sec.
1514          * <p>
1515          * Received by the {@link InCallService} via
1516          * {@link InCallService.VideoCall.Callback#onCallDataUsageChanged(long)}.
1517          *
1518          * @param dataUsage The updated data usage (in bytes).  Reported as the cumulative bytes
1519          *                  used since the start of the call.
1520          */
setCallDataUsage(long dataUsage)1521         public void setCallDataUsage(long dataUsage) {
1522             if (mVideoCallbacks != null) {
1523                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1524                     try {
1525                         callback.changeCallDataUsage(dataUsage);
1526                     } catch (RemoteException ignored) {
1527                         Log.w(this, "setCallDataUsage callback failed", ignored);
1528                     }
1529                 }
1530             }
1531         }
1532 
1533         /**
1534          * @see #setCallDataUsage(long)
1535          *
1536          * @param dataUsage The updated data usage (in byes).
1537          * @deprecated - Use {@link #setCallDataUsage(long)} instead.
1538          * @hide
1539          */
changeCallDataUsage(long dataUsage)1540         public void changeCallDataUsage(long dataUsage) {
1541             setCallDataUsage(dataUsage);
1542         }
1543 
1544         /**
1545          * Used to inform listening {@link InCallService} implementations when the capabilities of
1546          * the current camera have changed.
1547          * <p>
1548          * The {@link VideoProvider} should call this in response to
1549          * {@link VideoProvider#onRequestCameraCapabilities()}, or when the current camera is
1550          * changed via {@link VideoProvider#onSetCamera(String)}.
1551          * <p>
1552          * Received by the {@link InCallService} via
1553          * {@link InCallService.VideoCall.Callback#onCameraCapabilitiesChanged(
1554          * VideoProfile.CameraCapabilities)}.
1555          *
1556          * @param cameraCapabilities The new camera capabilities.
1557          */
changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities)1558         public void changeCameraCapabilities(VideoProfile.CameraCapabilities cameraCapabilities) {
1559             if (mVideoCallbacks != null) {
1560                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1561                     try {
1562                         callback.changeCameraCapabilities(cameraCapabilities);
1563                     } catch (RemoteException ignored) {
1564                         Log.w(this, "changeCameraCapabilities callback failed", ignored);
1565                     }
1566                 }
1567             }
1568         }
1569 
1570         /**
1571          * Used to inform listening {@link InCallService} implementations when the video quality
1572          * of the call has changed.
1573          * <p>
1574          * Received by the {@link InCallService} via
1575          * {@link InCallService.VideoCall.Callback#onVideoQualityChanged(int)}.
1576          *
1577          * @param videoQuality The updated video quality.  Valid values:
1578          *      {@link VideoProfile#QUALITY_HIGH},
1579          *      {@link VideoProfile#QUALITY_MEDIUM},
1580          *      {@link VideoProfile#QUALITY_LOW},
1581          *      {@link VideoProfile#QUALITY_DEFAULT}.
1582          */
changeVideoQuality(int videoQuality)1583         public void changeVideoQuality(int videoQuality) {
1584             if (mVideoCallbacks != null) {
1585                 for (IVideoCallback callback : mVideoCallbacks.values()) {
1586                     try {
1587                         callback.changeVideoQuality(videoQuality);
1588                     } catch (RemoteException ignored) {
1589                         Log.w(this, "changeVideoQuality callback failed", ignored);
1590                     }
1591                 }
1592             }
1593         }
1594 
1595         /**
1596          * Returns a string representation of a call session event.
1597          *
1598          * @param event A call session event passed to {@link #handleCallSessionEvent(int)}.
1599          * @return String representation of the call session event.
1600          * @hide
1601          */
sessionEventToString(int event)1602         public static String sessionEventToString(int event) {
1603             switch (event) {
1604                 case SESSION_EVENT_CAMERA_FAILURE:
1605                     return SESSION_EVENT_CAMERA_FAILURE_STR;
1606                 case SESSION_EVENT_CAMERA_READY:
1607                     return SESSION_EVENT_CAMERA_READY_STR;
1608                 case SESSION_EVENT_RX_PAUSE:
1609                     return SESSION_EVENT_RX_PAUSE_STR;
1610                 case SESSION_EVENT_RX_RESUME:
1611                     return SESSION_EVENT_RX_RESUME_STR;
1612                 case SESSION_EVENT_TX_START:
1613                     return SESSION_EVENT_TX_START_STR;
1614                 case SESSION_EVENT_TX_STOP:
1615                     return SESSION_EVENT_TX_STOP_STR;
1616                 case SESSION_EVENT_CAMERA_PERMISSION_ERROR:
1617                     return SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR;
1618                 default:
1619                     return SESSION_EVENT_UNKNOWN_STR + " " + event;
1620             }
1621         }
1622     }
1623 
1624     private final Listener mConnectionDeathListener = new Listener() {
1625         @Override
1626         public void onDestroyed(Connection c) {
1627             if (mConferenceables.remove(c)) {
1628                 fireOnConferenceableConnectionsChanged();
1629             }
1630         }
1631     };
1632 
1633     private final Conference.Listener mConferenceDeathListener = new Conference.Listener() {
1634         @Override
1635         public void onDestroyed(Conference c) {
1636             if (mConferenceables.remove(c)) {
1637                 fireOnConferenceableConnectionsChanged();
1638             }
1639         }
1640     };
1641 
1642     /**
1643      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
1644      * load factor before resizing, 1 means we only expect a single thread to
1645      * access the map so make only a single shard
1646      */
1647     private final Set<Listener> mListeners = Collections.newSetFromMap(
1648             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
1649     private final List<Conferenceable> mConferenceables = new ArrayList<>();
1650     private final List<Conferenceable> mUnmodifiableConferenceables =
1651             Collections.unmodifiableList(mConferenceables);
1652 
1653     // The internal telecom call ID associated with this connection.
1654     private String mTelecomCallId;
1655     private int mState = STATE_NEW;
1656     private CallAudioState mCallAudioState;
1657     private Uri mAddress;
1658     private int mAddressPresentation;
1659     private String mCallerDisplayName;
1660     private int mCallerDisplayNamePresentation;
1661     private boolean mRingbackRequested = false;
1662     private int mConnectionCapabilities;
1663     private int mConnectionProperties;
1664     private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL;
1665     private VideoProvider mVideoProvider;
1666     private boolean mAudioModeIsVoip;
1667     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
1668     private StatusHints mStatusHints;
1669     private int mVideoState;
1670     private DisconnectCause mDisconnectCause;
1671     private Conference mConference;
1672     private ConnectionService mConnectionService;
1673     private Bundle mExtras;
1674     private final Object mExtrasLock = new Object();
1675 
1676     /**
1677      * Tracks the key set for the extras bundle provided on the last invocation of
1678      * {@link #setExtras(Bundle)}.  Used so that on subsequent invocations we can remove any extras
1679      * keys which were set previously but are no longer present in the replacement Bundle.
1680      */
1681     private Set<String> mPreviousExtraKeys;
1682 
1683     /**
1684      * Create a new Connection.
1685      */
Connection()1686     public Connection() {}
1687 
1688     /**
1689      * Returns the Telecom internal call ID associated with this connection.  Should only be used
1690      * for debugging and tracing purposes.
1691      *
1692      * @return The Telecom call ID.
1693      * @hide
1694      */
getTelecomCallId()1695     public final String getTelecomCallId() {
1696         return mTelecomCallId;
1697     }
1698 
1699     /**
1700      * @return The address (e.g., phone number) to which this Connection is currently communicating.
1701      */
getAddress()1702     public final Uri getAddress() {
1703         return mAddress;
1704     }
1705 
1706     /**
1707      * @return The presentation requirements for the address.
1708      *         See {@link TelecomManager} for valid values.
1709      */
getAddressPresentation()1710     public final int getAddressPresentation() {
1711         return mAddressPresentation;
1712     }
1713 
1714     /**
1715      * @return The caller display name (CNAP).
1716      */
getCallerDisplayName()1717     public final String getCallerDisplayName() {
1718         return mCallerDisplayName;
1719     }
1720 
1721     /**
1722      * @return The presentation requirements for the handle.
1723      *         See {@link TelecomManager} for valid values.
1724      */
getCallerDisplayNamePresentation()1725     public final int getCallerDisplayNamePresentation() {
1726         return mCallerDisplayNamePresentation;
1727     }
1728 
1729     /**
1730      * @return The state of this Connection.
1731      */
getState()1732     public final int getState() {
1733         return mState;
1734     }
1735 
1736     /**
1737      * Returns the video state of the connection.
1738      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1739      * {@link VideoProfile#STATE_BIDIRECTIONAL},
1740      * {@link VideoProfile#STATE_TX_ENABLED},
1741      * {@link VideoProfile#STATE_RX_ENABLED}.
1742      *
1743      * @return The video state of the connection.
1744      * @hide
1745      */
getVideoState()1746     public final int getVideoState() {
1747         return mVideoState;
1748     }
1749 
1750     /**
1751      * @return The audio state of the connection, describing how its audio is currently
1752      *         being routed by the system. This is {@code null} if this Connection
1753      *         does not directly know about its audio state.
1754      * @deprecated Use {@link #getCallAudioState()} instead.
1755      * @hide
1756      */
1757     @SystemApi
1758     @Deprecated
getAudioState()1759     public final AudioState getAudioState() {
1760         if (mCallAudioState == null) {
1761           return null;
1762         }
1763         return new AudioState(mCallAudioState);
1764     }
1765 
1766     /**
1767      * @return The audio state of the connection, describing how its audio is currently
1768      *         being routed by the system. This is {@code null} if this Connection
1769      *         does not directly know about its audio state.
1770      */
getCallAudioState()1771     public final CallAudioState getCallAudioState() {
1772         return mCallAudioState;
1773     }
1774 
1775     /**
1776      * @return The conference that this connection is a part of.  Null if it is not part of any
1777      *         conference.
1778      */
getConference()1779     public final Conference getConference() {
1780         return mConference;
1781     }
1782 
1783     /**
1784      * Returns whether this connection is requesting that the system play a ringback tone
1785      * on its behalf.
1786      */
isRingbackRequested()1787     public final boolean isRingbackRequested() {
1788         return mRingbackRequested;
1789     }
1790 
1791     /**
1792      * @return True if the connection's audio mode is VOIP.
1793      */
getAudioModeIsVoip()1794     public final boolean getAudioModeIsVoip() {
1795         return mAudioModeIsVoip;
1796     }
1797 
1798     /**
1799      * Retrieves the connection start time of the {@code Connnection}, if specified.  A value of
1800      * {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
1801      * start time of the conference.
1802      *
1803      * @return The time at which the {@code Connnection} was connected.
1804      *
1805      * @hide
1806      */
getConnectTimeMillis()1807     public final long getConnectTimeMillis() {
1808         return mConnectTimeMillis;
1809     }
1810 
1811     /**
1812      * @return The status hints for this connection.
1813      */
getStatusHints()1814     public final StatusHints getStatusHints() {
1815         return mStatusHints;
1816     }
1817 
1818     /**
1819      * Returns the extras associated with this connection.
1820      * <p>
1821      * Extras should be updated using {@link #putExtras(Bundle)}.
1822      * <p>
1823      * Telecom or an {@link InCallService} can also update the extras via
1824      * {@link android.telecom.Call#putExtras(Bundle)}, and
1825      * {@link Call#removeExtras(List)}.
1826      * <p>
1827      * The connection is notified of changes to the extras made by Telecom or an
1828      * {@link InCallService} by {@link #onExtrasChanged(Bundle)}.
1829      *
1830      * @return The extras associated with this connection.
1831      */
getExtras()1832     public final Bundle getExtras() {
1833         Bundle extras = null;
1834         synchronized (mExtrasLock) {
1835             if (mExtras != null) {
1836                 extras = new Bundle(mExtras);
1837             }
1838         }
1839         return extras;
1840     }
1841 
1842     /**
1843      * Assign a listener to be notified of state changes.
1844      *
1845      * @param l A listener.
1846      * @return This Connection.
1847      *
1848      * @hide
1849      */
addConnectionListener(Listener l)1850     public final Connection addConnectionListener(Listener l) {
1851         mListeners.add(l);
1852         return this;
1853     }
1854 
1855     /**
1856      * Remove a previously assigned listener that was being notified of state changes.
1857      *
1858      * @param l A Listener.
1859      * @return This Connection.
1860      *
1861      * @hide
1862      */
removeConnectionListener(Listener l)1863     public final Connection removeConnectionListener(Listener l) {
1864         if (l != null) {
1865             mListeners.remove(l);
1866         }
1867         return this;
1868     }
1869 
1870     /**
1871      * @return The {@link DisconnectCause} for this connection.
1872      */
getDisconnectCause()1873     public final DisconnectCause getDisconnectCause() {
1874         return mDisconnectCause;
1875     }
1876 
1877     /**
1878      * Sets the telecom call ID associated with this Connection.  The Telecom Call ID should be used
1879      * ONLY for debugging purposes.
1880      *
1881      * @param callId The telecom call ID.
1882      * @hide
1883      */
setTelecomCallId(String callId)1884     public void setTelecomCallId(String callId) {
1885         mTelecomCallId = callId;
1886     }
1887 
1888     /**
1889      * Inform this Connection that the state of its audio output has been changed externally.
1890      *
1891      * @param state The new audio state.
1892      * @hide
1893      */
setCallAudioState(CallAudioState state)1894     final void setCallAudioState(CallAudioState state) {
1895         checkImmutable();
1896         Log.d(this, "setAudioState %s", state);
1897         mCallAudioState = state;
1898         onAudioStateChanged(getAudioState());
1899         onCallAudioStateChanged(state);
1900     }
1901 
1902     /**
1903      * @param state An integer value of a {@code STATE_*} constant.
1904      * @return A string representation of the value.
1905      */
stateToString(int state)1906     public static String stateToString(int state) {
1907         switch (state) {
1908             case STATE_INITIALIZING:
1909                 return "INITIALIZING";
1910             case STATE_NEW:
1911                 return "NEW";
1912             case STATE_RINGING:
1913                 return "RINGING";
1914             case STATE_DIALING:
1915                 return "DIALING";
1916             case STATE_PULLING_CALL:
1917                 return "PULLING_CALL";
1918             case STATE_ACTIVE:
1919                 return "ACTIVE";
1920             case STATE_HOLDING:
1921                 return "HOLDING";
1922             case STATE_DISCONNECTED:
1923                 return "DISCONNECTED";
1924             default:
1925                 Log.wtf(Connection.class, "Unknown state %d", state);
1926                 return "UNKNOWN";
1927         }
1928     }
1929 
1930     /**
1931      * Returns the connection's capabilities, as a bit mask of the {@code CAPABILITY_*} constants.
1932      */
getConnectionCapabilities()1933     public final int getConnectionCapabilities() {
1934         return mConnectionCapabilities;
1935     }
1936 
1937     /**
1938      * Returns the connection's properties, as a bit mask of the {@code PROPERTY_*} constants.
1939      */
getConnectionProperties()1940     public final int getConnectionProperties() {
1941         return mConnectionProperties;
1942     }
1943 
1944     /**
1945      * Returns the connection's supported audio routes.
1946      *
1947      * @hide
1948      */
getSupportedAudioRoutes()1949     public final int getSupportedAudioRoutes() {
1950         return mSupportedAudioRoutes;
1951     }
1952 
1953     /**
1954      * Sets the value of the {@link #getAddress()} property.
1955      *
1956      * @param address The new address.
1957      * @param presentation The presentation requirements for the address.
1958      *        See {@link TelecomManager} for valid values.
1959      */
setAddress(Uri address, int presentation)1960     public final void setAddress(Uri address, int presentation) {
1961         checkImmutable();
1962         Log.d(this, "setAddress %s", address);
1963         mAddress = address;
1964         mAddressPresentation = presentation;
1965         for (Listener l : mListeners) {
1966             l.onAddressChanged(this, address, presentation);
1967         }
1968     }
1969 
1970     /**
1971      * Sets the caller display name (CNAP).
1972      *
1973      * @param callerDisplayName The new display name.
1974      * @param presentation The presentation requirements for the handle.
1975      *        See {@link TelecomManager} for valid values.
1976      */
setCallerDisplayName(String callerDisplayName, int presentation)1977     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
1978         checkImmutable();
1979         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
1980         mCallerDisplayName = callerDisplayName;
1981         mCallerDisplayNamePresentation = presentation;
1982         for (Listener l : mListeners) {
1983             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
1984         }
1985     }
1986 
1987     /**
1988      * Set the video state for the connection.
1989      * Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
1990      * {@link VideoProfile#STATE_BIDIRECTIONAL},
1991      * {@link VideoProfile#STATE_TX_ENABLED},
1992      * {@link VideoProfile#STATE_RX_ENABLED}.
1993      *
1994      * @param videoState The new video state.
1995      */
setVideoState(int videoState)1996     public final void setVideoState(int videoState) {
1997         checkImmutable();
1998         Log.d(this, "setVideoState %d", videoState);
1999         mVideoState = videoState;
2000         for (Listener l : mListeners) {
2001             l.onVideoStateChanged(this, mVideoState);
2002         }
2003     }
2004 
2005     /**
2006      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
2007      * communicate).
2008      */
setActive()2009     public final void setActive() {
2010         checkImmutable();
2011         setRingbackRequested(false);
2012         setState(STATE_ACTIVE);
2013     }
2014 
2015     /**
2016      * Sets state to ringing (e.g., an inbound ringing connection).
2017      */
setRinging()2018     public final void setRinging() {
2019         checkImmutable();
2020         setState(STATE_RINGING);
2021     }
2022 
2023     /**
2024      * Sets state to initializing (this Connection is not yet ready to be used).
2025      */
setInitializing()2026     public final void setInitializing() {
2027         checkImmutable();
2028         setState(STATE_INITIALIZING);
2029     }
2030 
2031     /**
2032      * Sets state to initialized (the Connection has been set up and is now ready to be used).
2033      */
setInitialized()2034     public final void setInitialized() {
2035         checkImmutable();
2036         setState(STATE_NEW);
2037     }
2038 
2039     /**
2040      * Sets state to dialing (e.g., dialing an outbound connection).
2041      */
setDialing()2042     public final void setDialing() {
2043         checkImmutable();
2044         setState(STATE_DIALING);
2045     }
2046 
2047     /**
2048      * Sets state to pulling (e.g. the connection is being pulled to the local device from another
2049      * device).  Only applicable for {@link Connection}s with
2050      * {@link Connection#PROPERTY_IS_EXTERNAL_CALL} and {@link Connection#CAPABILITY_CAN_PULL_CALL}.
2051      */
setPulling()2052     public final void setPulling() {
2053         checkImmutable();
2054         setState(STATE_PULLING_CALL);
2055     }
2056 
2057     /**
2058      * Sets state to be on hold.
2059      */
setOnHold()2060     public final void setOnHold() {
2061         checkImmutable();
2062         setState(STATE_HOLDING);
2063     }
2064 
2065     /**
2066      * Sets the video connection provider.
2067      * @param videoProvider The video provider.
2068      */
setVideoProvider(VideoProvider videoProvider)2069     public final void setVideoProvider(VideoProvider videoProvider) {
2070         checkImmutable();
2071         mVideoProvider = videoProvider;
2072         for (Listener l : mListeners) {
2073             l.onVideoProviderChanged(this, videoProvider);
2074         }
2075     }
2076 
getVideoProvider()2077     public final VideoProvider getVideoProvider() {
2078         return mVideoProvider;
2079     }
2080 
2081     /**
2082      * Sets state to disconnected.
2083      *
2084      * @param disconnectCause The reason for the disconnection, as specified by
2085      *         {@link DisconnectCause}.
2086      */
setDisconnected(DisconnectCause disconnectCause)2087     public final void setDisconnected(DisconnectCause disconnectCause) {
2088         checkImmutable();
2089         mDisconnectCause = disconnectCause;
2090         setState(STATE_DISCONNECTED);
2091         Log.d(this, "Disconnected with cause %s", disconnectCause);
2092         for (Listener l : mListeners) {
2093             l.onDisconnected(this, disconnectCause);
2094         }
2095     }
2096 
2097     /**
2098      * Informs listeners that this {@code Connection} is in a post-dial wait state. This is done
2099      * when (a) the {@code Connection} is issuing a DTMF sequence; (b) it has encountered a "wait"
2100      * character; and (c) it wishes to inform the In-Call app that it is waiting for the end-user
2101      * to send an {@link #onPostDialContinue(boolean)} signal.
2102      *
2103      * @param remaining The DTMF character sequence remaining to be emitted once the
2104      *         {@link #onPostDialContinue(boolean)} is received, including any "wait" characters
2105      *         that remaining sequence may contain.
2106      */
setPostDialWait(String remaining)2107     public final void setPostDialWait(String remaining) {
2108         checkImmutable();
2109         for (Listener l : mListeners) {
2110             l.onPostDialWait(this, remaining);
2111         }
2112     }
2113 
2114     /**
2115      * Informs listeners that this {@code Connection} has processed a character in the post-dial
2116      * started state. This is done when (a) the {@code Connection} is issuing a DTMF sequence;
2117      * and (b) it wishes to signal Telecom to play the corresponding DTMF tone locally.
2118      *
2119      * @param nextChar The DTMF character that was just processed by the {@code Connection}.
2120      */
setNextPostDialChar(char nextChar)2121     public final void setNextPostDialChar(char nextChar) {
2122         checkImmutable();
2123         for (Listener l : mListeners) {
2124             l.onPostDialChar(this, nextChar);
2125         }
2126     }
2127 
2128     /**
2129      * Requests that the framework play a ringback tone. This is to be invoked by implementations
2130      * that do not play a ringback tone themselves in the connection's audio stream.
2131      *
2132      * @param ringback Whether the ringback tone is to be played.
2133      */
setRingbackRequested(boolean ringback)2134     public final void setRingbackRequested(boolean ringback) {
2135         checkImmutable();
2136         if (mRingbackRequested != ringback) {
2137             mRingbackRequested = ringback;
2138             for (Listener l : mListeners) {
2139                 l.onRingbackRequested(this, ringback);
2140             }
2141         }
2142     }
2143 
2144     /**
2145      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
2146      *
2147      * @param connectionCapabilities The new connection capabilities.
2148      */
setConnectionCapabilities(int connectionCapabilities)2149     public final void setConnectionCapabilities(int connectionCapabilities) {
2150         checkImmutable();
2151         if (mConnectionCapabilities != connectionCapabilities) {
2152             mConnectionCapabilities = connectionCapabilities;
2153             for (Listener l : mListeners) {
2154                 l.onConnectionCapabilitiesChanged(this, mConnectionCapabilities);
2155             }
2156         }
2157     }
2158 
2159     /**
2160      * Sets the connection's properties as a bit mask of the {@code PROPERTY_*} constants.
2161      *
2162      * @param connectionProperties The new connection properties.
2163      */
setConnectionProperties(int connectionProperties)2164     public final void setConnectionProperties(int connectionProperties) {
2165         checkImmutable();
2166         if (mConnectionProperties != connectionProperties) {
2167             mConnectionProperties = connectionProperties;
2168             for (Listener l : mListeners) {
2169                 l.onConnectionPropertiesChanged(this, mConnectionProperties);
2170             }
2171         }
2172     }
2173 
2174     /**
2175      * Sets the supported audio routes.
2176      *
2177      * @param supportedAudioRoutes the supported audio routes as a bitmask.
2178      *                             See {@link CallAudioState}
2179      * @hide
2180      */
setSupportedAudioRoutes(int supportedAudioRoutes)2181     public final void setSupportedAudioRoutes(int supportedAudioRoutes) {
2182         if ((supportedAudioRoutes
2183                 & (CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER)) == 0) {
2184             throw new IllegalArgumentException(
2185                     "supported audio routes must include either speaker or earpiece");
2186         }
2187 
2188         if (mSupportedAudioRoutes != supportedAudioRoutes) {
2189             mSupportedAudioRoutes = supportedAudioRoutes;
2190             for (Listener l : mListeners) {
2191                 l.onSupportedAudioRoutesChanged(this, mSupportedAudioRoutes);
2192             }
2193         }
2194     }
2195 
2196     /**
2197      * Tears down the Connection object.
2198      */
destroy()2199     public final void destroy() {
2200         for (Listener l : mListeners) {
2201             l.onDestroyed(this);
2202         }
2203     }
2204 
2205     /**
2206      * Requests that the framework use VOIP audio mode for this connection.
2207      *
2208      * @param isVoip True if the audio mode is VOIP.
2209      */
setAudioModeIsVoip(boolean isVoip)2210     public final void setAudioModeIsVoip(boolean isVoip) {
2211         checkImmutable();
2212         mAudioModeIsVoip = isVoip;
2213         for (Listener l : mListeners) {
2214             l.onAudioModeIsVoipChanged(this, isVoip);
2215         }
2216     }
2217 
2218     /**
2219      * Sets the time at which a call became active on this Connection. This is set only
2220      * when a conference call becomes active on this connection.
2221      *
2222      * @param connectionTimeMillis The connection time, in milliseconds.
2223      *
2224      * @hide
2225      */
setConnectTimeMillis(long connectTimeMillis)2226     public final void setConnectTimeMillis(long connectTimeMillis) {
2227         mConnectTimeMillis = connectTimeMillis;
2228     }
2229 
2230     /**
2231      * Sets the label and icon status to display in the in-call UI.
2232      *
2233      * @param statusHints The status label and icon to set.
2234      */
setStatusHints(StatusHints statusHints)2235     public final void setStatusHints(StatusHints statusHints) {
2236         checkImmutable();
2237         mStatusHints = statusHints;
2238         for (Listener l : mListeners) {
2239             l.onStatusHintsChanged(this, statusHints);
2240         }
2241     }
2242 
2243     /**
2244      * Sets the connections with which this connection can be conferenced.
2245      *
2246      * @param conferenceableConnections The set of connections this connection can conference with.
2247      */
setConferenceableConnections(List<Connection> conferenceableConnections)2248     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
2249         checkImmutable();
2250         clearConferenceableList();
2251         for (Connection c : conferenceableConnections) {
2252             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
2253             // small amount of items here.
2254             if (!mConferenceables.contains(c)) {
2255                 c.addConnectionListener(mConnectionDeathListener);
2256                 mConferenceables.add(c);
2257             }
2258         }
2259         fireOnConferenceableConnectionsChanged();
2260     }
2261 
2262     /**
2263      * Similar to {@link #setConferenceableConnections(java.util.List)}, sets a list of connections
2264      * or conferences with which this connection can be conferenced.
2265      *
2266      * @param conferenceables The conferenceables.
2267      */
setConferenceables(List<Conferenceable> conferenceables)2268     public final void setConferenceables(List<Conferenceable> conferenceables) {
2269         clearConferenceableList();
2270         for (Conferenceable c : conferenceables) {
2271             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
2272             // small amount of items here.
2273             if (!mConferenceables.contains(c)) {
2274                 if (c instanceof Connection) {
2275                     Connection connection = (Connection) c;
2276                     connection.addConnectionListener(mConnectionDeathListener);
2277                 } else if (c instanceof Conference) {
2278                     Conference conference = (Conference) c;
2279                     conference.addListener(mConferenceDeathListener);
2280                 }
2281                 mConferenceables.add(c);
2282             }
2283         }
2284         fireOnConferenceableConnectionsChanged();
2285     }
2286 
2287     /**
2288      * Returns the connections or conferences with which this connection can be conferenced.
2289      */
getConferenceables()2290     public final List<Conferenceable> getConferenceables() {
2291         return mUnmodifiableConferenceables;
2292     }
2293 
2294     /**
2295      * @hide
2296      */
setConnectionService(ConnectionService connectionService)2297     public final void setConnectionService(ConnectionService connectionService) {
2298         checkImmutable();
2299         if (mConnectionService != null) {
2300             Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
2301                     "which is already associated with another ConnectionService.");
2302         } else {
2303             mConnectionService = connectionService;
2304         }
2305     }
2306 
2307     /**
2308      * @hide
2309      */
unsetConnectionService(ConnectionService connectionService)2310     public final void unsetConnectionService(ConnectionService connectionService) {
2311         if (mConnectionService != connectionService) {
2312             Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
2313                     "that does not belong to the ConnectionService.");
2314         } else {
2315             mConnectionService = null;
2316         }
2317     }
2318 
2319     /**
2320      * @hide
2321      */
getConnectionService()2322     public final ConnectionService getConnectionService() {
2323         return mConnectionService;
2324     }
2325 
2326     /**
2327      * Sets the conference that this connection is a part of. This will fail if the connection is
2328      * already part of a conference. {@link #resetConference} to un-set the conference first.
2329      *
2330      * @param conference The conference.
2331      * @return {@code true} if the conference was successfully set.
2332      * @hide
2333      */
setConference(Conference conference)2334     public final boolean setConference(Conference conference) {
2335         checkImmutable();
2336         // We check to see if it is already part of another conference.
2337         if (mConference == null) {
2338             mConference = conference;
2339             if (mConnectionService != null && mConnectionService.containsConference(conference)) {
2340                 fireConferenceChanged();
2341             }
2342             return true;
2343         }
2344         return false;
2345     }
2346 
2347     /**
2348      * Resets the conference that this connection is a part of.
2349      * @hide
2350      */
resetConference()2351     public final void resetConference() {
2352         if (mConference != null) {
2353             Log.d(this, "Conference reset");
2354             mConference = null;
2355             fireConferenceChanged();
2356         }
2357     }
2358 
2359     /**
2360      * Set some extras that can be associated with this {@code Connection}.
2361      * <p>
2362      * New or existing keys are replaced in the {@code Connection} extras.  Keys which are no longer
2363      * in the new extras, but were present the last time {@code setExtras} was called are removed.
2364      * <p>
2365      * Alternatively you may use the {@link #putExtras(Bundle)}, and
2366      * {@link #removeExtras(String...)} methods to modify the extras.
2367      * <p>
2368      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
2369      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
2370      *
2371      * @param extras The extras associated with this {@code Connection}.
2372      */
setExtras(@ullable Bundle extras)2373     public final void setExtras(@Nullable Bundle extras) {
2374         checkImmutable();
2375 
2376         // Add/replace any new or changed extras values.
2377         putExtras(extras);
2378 
2379         // If we have used "setExtras" in the past, compare the key set from the last invocation to
2380         // the current one and remove any keys that went away.
2381         if (mPreviousExtraKeys != null) {
2382             List<String> toRemove = new ArrayList<String>();
2383             for (String oldKey : mPreviousExtraKeys) {
2384                 if (extras == null || !extras.containsKey(oldKey)) {
2385                     toRemove.add(oldKey);
2386                 }
2387             }
2388             if (!toRemove.isEmpty()) {
2389                 removeExtras(toRemove);
2390             }
2391         }
2392 
2393         // Track the keys the last time set called setExtras.  This way, the next time setExtras is
2394         // called we can see if the caller has removed any extras values.
2395         if (mPreviousExtraKeys == null) {
2396             mPreviousExtraKeys = new ArraySet<String>();
2397         }
2398         mPreviousExtraKeys.clear();
2399         if (extras != null) {
2400             mPreviousExtraKeys.addAll(extras.keySet());
2401         }
2402     }
2403 
2404     /**
2405      * Adds some extras to this {@code Connection}.  Existing keys are replaced and new ones are
2406      * added.
2407      * <p>
2408      * No assumptions should be made as to how an In-Call UI or service will handle these extras.
2409      * Keys should be fully qualified (e.g., com.example.MY_EXTRA) to avoid conflicts.
2410      *
2411      * @param extras The extras to add.
2412      */
putExtras(@onNull Bundle extras)2413     public final void putExtras(@NonNull Bundle extras) {
2414         checkImmutable();
2415         if (extras == null) {
2416             return;
2417         }
2418         // Creating a duplicate bundle so we don't have to synchronize on mExtrasLock while calling
2419         // the listeners.
2420         Bundle listenerExtras;
2421         synchronized (mExtrasLock) {
2422             if (mExtras == null) {
2423                 mExtras = new Bundle();
2424             }
2425             mExtras.putAll(extras);
2426             listenerExtras = new Bundle(mExtras);
2427         }
2428         for (Listener l : mListeners) {
2429             // Create a new clone of the extras for each listener so that they don't clobber
2430             // each other
2431             l.onExtrasChanged(this, new Bundle(listenerExtras));
2432         }
2433     }
2434 
2435     /**
2436      * Adds a boolean extra to this {@code Connection}.
2437      *
2438      * @param key The extra key.
2439      * @param value The value.
2440      * @hide
2441      */
putExtra(String key, boolean value)2442     public final void putExtra(String key, boolean value) {
2443         Bundle newExtras = new Bundle();
2444         newExtras.putBoolean(key, value);
2445         putExtras(newExtras);
2446     }
2447 
2448     /**
2449      * Adds an integer extra to this {@code Connection}.
2450      *
2451      * @param key The extra key.
2452      * @param value The value.
2453      * @hide
2454      */
putExtra(String key, int value)2455     public final void putExtra(String key, int value) {
2456         Bundle newExtras = new Bundle();
2457         newExtras.putInt(key, value);
2458         putExtras(newExtras);
2459     }
2460 
2461     /**
2462      * Adds a string extra to this {@code Connection}.
2463      *
2464      * @param key The extra key.
2465      * @param value The value.
2466      * @hide
2467      */
putExtra(String key, String value)2468     public final void putExtra(String key, String value) {
2469         Bundle newExtras = new Bundle();
2470         newExtras.putString(key, value);
2471         putExtras(newExtras);
2472     }
2473 
2474     /**
2475      * Removes extras from this {@code Connection}.
2476      *
2477      * @param keys The keys of the extras to remove.
2478      */
removeExtras(List<String> keys)2479     public final void removeExtras(List<String> keys) {
2480         synchronized (mExtrasLock) {
2481             if (mExtras != null) {
2482                 for (String key : keys) {
2483                     mExtras.remove(key);
2484                 }
2485             }
2486         }
2487         List<String> unmodifiableKeys = Collections.unmodifiableList(keys);
2488         for (Listener l : mListeners) {
2489             l.onExtrasRemoved(this, unmodifiableKeys);
2490         }
2491     }
2492 
2493     /**
2494      * Removes extras from this {@code Connection}.
2495      *
2496      * @param keys The keys of the extras to remove.
2497      */
removeExtras(String .... keys)2498     public final void removeExtras(String ... keys) {
2499         removeExtras(Arrays.asList(keys));
2500     }
2501 
2502     /**
2503      * Sets the audio route (speaker, bluetooth, etc...).  When this request is honored, there will
2504      * be change to the {@link #getCallAudioState()}.
2505      * <p>
2506      * Used by self-managed {@link ConnectionService}s which wish to change the audio route for a
2507      * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
2508      * <p>
2509      * See also {@link InCallService#setAudioRoute(int)}.
2510      *
2511      * @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
2512      *              {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
2513      *              {@link CallAudioState#ROUTE_WIRED_HEADSET}).
2514      */
setAudioRoute(int route)2515     public final void setAudioRoute(int route) {
2516         for (Listener l : mListeners) {
2517             l.onAudioRouteChanged(this, route);
2518         }
2519     }
2520 
2521     /**
2522      * Informs listeners that a previously requested RTT session via
2523      * {@link ConnectionRequest#isRequestingRtt()} or
2524      * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
2525      * @hide
2526      */
sendRttInitiationSuccess()2527     public final void sendRttInitiationSuccess() {
2528         mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
2529     }
2530 
2531     /**
2532      * Informs listeners that a previously requested RTT session via
2533      * {@link ConnectionRequest#isRequestingRtt()} or
2534      * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
2535      * has failed.
2536      * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
2537      *               exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
2538      * @hide
2539      */
sendRttInitiationFailure(int reason)2540     public final void sendRttInitiationFailure(int reason) {
2541         mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
2542     }
2543 
2544     /**
2545      * Informs listeners that a currently active RTT session has been terminated by the remote
2546      * side of the coll.
2547      * @hide
2548      */
sendRttSessionRemotelyTerminated()2549     public final void sendRttSessionRemotelyTerminated() {
2550         mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
2551     }
2552 
2553     /**
2554      * Informs listeners that the remote side of the call has requested an upgrade to include an
2555      * RTT session in the call.
2556      * @hide
2557      */
sendRemoteRttRequest()2558     public final void sendRemoteRttRequest() {
2559         mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
2560     }
2561 
2562     /**
2563      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
2564      *
2565      * @param state The new connection audio state.
2566      * @deprecated Use {@link #onCallAudioStateChanged(CallAudioState)} instead.
2567      * @hide
2568      */
2569     @SystemApi
2570     @Deprecated
onAudioStateChanged(AudioState state)2571     public void onAudioStateChanged(AudioState state) {}
2572 
2573     /**
2574      * Notifies this Connection that the {@link #getCallAudioState()} property has a new value.
2575      *
2576      * @param state The new connection audio state.
2577      */
onCallAudioStateChanged(CallAudioState state)2578     public void onCallAudioStateChanged(CallAudioState state) {}
2579 
2580     /**
2581      * Notifies this Connection of an internal state change. This method is called after the
2582      * state is changed.
2583      *
2584      * @param state The new state, one of the {@code STATE_*} constants.
2585      */
onStateChanged(int state)2586     public void onStateChanged(int state) {}
2587 
2588     /**
2589      * Notifies this Connection of a request to play a DTMF tone.
2590      *
2591      * @param c A DTMF character.
2592      */
onPlayDtmfTone(char c)2593     public void onPlayDtmfTone(char c) {}
2594 
2595     /**
2596      * Notifies this Connection of a request to stop any currently playing DTMF tones.
2597      */
onStopDtmfTone()2598     public void onStopDtmfTone() {}
2599 
2600     /**
2601      * Notifies this Connection of a request to disconnect.
2602      */
onDisconnect()2603     public void onDisconnect() {}
2604 
2605     /**
2606      * Notifies this Connection of a request to disconnect a participant of the conference managed
2607      * by the connection.
2608      *
2609      * @param endpoint the {@link Uri} of the participant to disconnect.
2610      * @hide
2611      */
onDisconnectConferenceParticipant(Uri endpoint)2612     public void onDisconnectConferenceParticipant(Uri endpoint) {}
2613 
2614     /**
2615      * Notifies this Connection of a request to separate from its parent conference.
2616      */
onSeparate()2617     public void onSeparate() {}
2618 
2619     /**
2620      * Notifies this Connection of a request to abort.
2621      */
onAbort()2622     public void onAbort() {}
2623 
2624     /**
2625      * Notifies this Connection of a request to hold.
2626      */
onHold()2627     public void onHold() {}
2628 
2629     /**
2630      * Notifies this Connection of a request to exit a hold state.
2631      */
onUnhold()2632     public void onUnhold() {}
2633 
2634     /**
2635      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2636      * a request to accept.
2637      *
2638      * @param videoState The video state in which to answer the connection.
2639      */
onAnswer(int videoState)2640     public void onAnswer(int videoState) {}
2641 
2642     /**
2643      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2644      * a request to accept.
2645      */
onAnswer()2646     public void onAnswer() {
2647         onAnswer(VideoProfile.STATE_AUDIO_ONLY);
2648     }
2649 
2650     /**
2651      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2652      * a request to reject.
2653      */
onReject()2654     public void onReject() {}
2655 
2656     /**
2657      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
2658      * a request to reject with a message.
2659      */
onReject(String replyMessage)2660     public void onReject(String replyMessage) {}
2661 
2662     /**
2663      * Notifies the Connection of a request to silence the ringer.
2664      *
2665      * @hide
2666      */
onSilence()2667     public void onSilence() {}
2668 
2669     /**
2670      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
2671      */
onPostDialContinue(boolean proceed)2672     public void onPostDialContinue(boolean proceed) {}
2673 
2674     /**
2675      * Notifies this Connection of a request to pull an external call to the local device.
2676      * <p>
2677      * The {@link InCallService} issues a request to pull an external call to the local device via
2678      * {@link Call#pullExternalCall()}.
2679      * <p>
2680      * For a Connection to be pulled, both the {@link Connection#CAPABILITY_CAN_PULL_CALL}
2681      * capability and {@link Connection#PROPERTY_IS_EXTERNAL_CALL} property bits must be set.
2682      * <p>
2683      * For more information on external calls, see {@link Connection#PROPERTY_IS_EXTERNAL_CALL}.
2684      */
onPullExternalCall()2685     public void onPullExternalCall() {}
2686 
2687     /**
2688      * Notifies this Connection of a {@link Call} event initiated from an {@link InCallService}.
2689      * <p>
2690      * The {@link InCallService} issues a Call event via {@link Call#sendCallEvent(String, Bundle)}.
2691      * <p>
2692      * Where possible, the Connection should make an attempt to handle {@link Call} events which
2693      * are part of the {@code android.telecom.*} namespace.  The Connection should ignore any events
2694      * it does not wish to handle.  Unexpected events should be handled gracefully, as it is
2695      * possible that a {@link InCallService} has defined its own Call events which a Connection is
2696      * not aware of.
2697      * <p>
2698      * See also {@link Call#sendCallEvent(String, Bundle)}.
2699      *
2700      * @param event The call event.
2701      * @param extras Extras associated with the call event.
2702      */
onCallEvent(String event, Bundle extras)2703     public void onCallEvent(String event, Bundle extras) {}
2704 
2705     /**
2706      * Notifies this {@link Connection} of a change to the extras made outside the
2707      * {@link ConnectionService}.
2708      * <p>
2709      * These extras changes can originate from Telecom itself, or from an {@link InCallService} via
2710      * the {@link android.telecom.Call#putExtras(Bundle)} and
2711      * {@link Call#removeExtras(List)}.
2712      *
2713      * @param extras The new extras bundle.
2714      */
onExtrasChanged(Bundle extras)2715     public void onExtrasChanged(Bundle extras) {}
2716 
2717     /**
2718      * Notifies this {@link Connection} that its {@link ConnectionService} is responsible for
2719      * displaying its incoming call user interface for the {@link Connection}.
2720      * <p>
2721      * Will only be called for incoming calls added via a self-managed {@link ConnectionService}
2722      * (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}), where the {@link ConnectionService}
2723      * should show its own incoming call user interface.
2724      * <p>
2725      * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
2726      * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
2727      * user interface to allow the user to choose whether to answer the new incoming call and
2728      * disconnect other ongoing calls, or to reject the new incoming call.
2729      * <p>
2730      * You should trigger the display of the incoming call user interface for your application by
2731      * showing a {@link Notification} with a full-screen {@link Intent} specified.
2732      * For example:
2733      * <pre><code>
2734      *     // Create an intent which triggers your fullscreen incoming call user interface.
2735      *     Intent intent = new Intent(Intent.ACTION_MAIN, null);
2736      *     intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
2737      *     intent.setClass(context, YourIncomingCallActivity.class);
2738      *     PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
2739      *
2740      *     // Build the notification as an ongoing high priority item; this ensures it will show as
2741      *     // a heads up notification which slides down over top of the current content.
2742      *     final Notification.Builder builder = new Notification.Builder(context);
2743      *     builder.setOngoing(true);
2744      *     builder.setPriority(Notification.PRIORITY_HIGH);
2745      *
2746      *     // Set notification content intent to take user to fullscreen UI if user taps on the
2747      *     // notification body.
2748      *     builder.setContentIntent(pendingIntent);
2749      *     // Set full screen intent to trigger display of the fullscreen UI when the notification
2750      *     // manager deems it appropriate.
2751      *     builder.setFullScreenIntent(pendingIntent, true);
2752      *
2753      *     // Setup notification content.
2754      *     builder.setSmallIcon( yourIconResourceId );
2755      *     builder.setContentTitle("Your notification title");
2756      *     builder.setContentText("Your notification content.");
2757      *
2758      *     // Use builder.addAction(..) to add buttons to answer or reject the call.
2759      *
2760      *     NotificationManager notificationManager = mContext.getSystemService(
2761      *         NotificationManager.class);
2762      *     notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
2763      * </code></pre>
2764      */
onShowIncomingCallUi()2765     public void onShowIncomingCallUi() {}
2766 
2767     /**
2768      * Notifies this {@link Connection} that the user has requested an RTT session.
2769      * The connection service should call {@link #sendRttInitiationSuccess} or
2770      * {@link #sendRttInitiationFailure} to inform Telecom of the success or failure of the
2771      * request, respectively.
2772      * @param rttTextStream The object that should be used to send text to or receive text from
2773      *                      the in-call app.
2774      * @hide
2775      */
onStartRtt(@onNull RttTextStream rttTextStream)2776     public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
2777 
2778     /**
2779      * Notifies this {@link Connection} that it should terminate any existing RTT communication
2780      * channel. No response to Telecom is needed for this method.
2781      * @hide
2782      */
onStopRtt()2783     public void onStopRtt() {}
2784 
2785     /**
2786      * Notifies this connection of a response to a previous remotely-initiated RTT upgrade
2787      * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
2788      * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
2789      * indicated by {@code rttTextStream} being {@code null}
2790      * @hide
2791      * @param rttTextStream The object that should be used to send text to or receive text from
2792      *                      the in-call app.
2793      */
handleRttUpgradeResponse(@ullable RttTextStream rttTextStream)2794     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
2795 
toLogSafePhoneNumber(String number)2796     static String toLogSafePhoneNumber(String number) {
2797         // For unknown number, log empty string.
2798         if (number == null) {
2799             return "";
2800         }
2801 
2802         if (PII_DEBUG) {
2803             // When PII_DEBUG is true we emit PII.
2804             return number;
2805         }
2806 
2807         // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
2808         // sanitized phone numbers.
2809         StringBuilder builder = new StringBuilder();
2810         for (int i = 0; i < number.length(); i++) {
2811             char c = number.charAt(i);
2812             if (c == '-' || c == '@' || c == '.') {
2813                 builder.append(c);
2814             } else {
2815                 builder.append('x');
2816             }
2817         }
2818         return builder.toString();
2819     }
2820 
setState(int state)2821     private void setState(int state) {
2822         checkImmutable();
2823         if (mState == STATE_DISCONNECTED && mState != state) {
2824             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
2825             return;
2826         }
2827         if (mState != state) {
2828             Log.d(this, "setState: %s", stateToString(state));
2829             mState = state;
2830             onStateChanged(state);
2831             for (Listener l : mListeners) {
2832                 l.onStateChanged(this, state);
2833             }
2834         }
2835     }
2836 
2837     private static class FailureSignalingConnection extends Connection {
2838         private boolean mImmutable = false;
FailureSignalingConnection(DisconnectCause disconnectCause)2839         public FailureSignalingConnection(DisconnectCause disconnectCause) {
2840             setDisconnected(disconnectCause);
2841             mImmutable = true;
2842         }
2843 
checkImmutable()2844         public void checkImmutable() {
2845             if (mImmutable) {
2846                 throw new UnsupportedOperationException("Connection is immutable");
2847             }
2848         }
2849     }
2850 
2851     /**
2852      * Return a {@code Connection} which represents a failed connection attempt. The returned
2853      * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
2854      * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
2855      * <p>
2856      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
2857      * so users of this method need not maintain a reference to its return value to destroy it.
2858      *
2859      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
2860      * @return A {@code Connection} which indicates failure.
2861      */
createFailedConnection(DisconnectCause disconnectCause)2862     public static Connection createFailedConnection(DisconnectCause disconnectCause) {
2863         return new FailureSignalingConnection(disconnectCause);
2864     }
2865 
2866     /**
2867      * Override to throw an {@link UnsupportedOperationException} if this {@code Connection} is
2868      * not intended to be mutated, e.g., if it is a marker for failure. Only for framework use;
2869      * this should never be un-@hide-den.
2870      *
2871      * @hide
2872      */
checkImmutable()2873     public void checkImmutable() {}
2874 
2875     /**
2876      * Return a {@code Connection} which represents a canceled connection attempt. The returned
2877      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
2878      * that state. This connection should not be used for anything, and no other
2879      * {@code Connection}s should be attempted.
2880      * <p>
2881      * so users of this method need not maintain a reference to its return value to destroy it.
2882      *
2883      * @return A {@code Connection} which indicates that the underlying connection should
2884      * be canceled.
2885      */
createCanceledConnection()2886     public static Connection createCanceledConnection() {
2887         return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
2888     }
2889 
fireOnConferenceableConnectionsChanged()2890     private final void fireOnConferenceableConnectionsChanged() {
2891         for (Listener l : mListeners) {
2892             l.onConferenceablesChanged(this, getConferenceables());
2893         }
2894     }
2895 
fireConferenceChanged()2896     private final void fireConferenceChanged() {
2897         for (Listener l : mListeners) {
2898             l.onConferenceChanged(this, mConference);
2899         }
2900     }
2901 
clearConferenceableList()2902     private final void clearConferenceableList() {
2903         for (Conferenceable c : mConferenceables) {
2904             if (c instanceof Connection) {
2905                 Connection connection = (Connection) c;
2906                 connection.removeConnectionListener(mConnectionDeathListener);
2907             } else if (c instanceof Conference) {
2908                 Conference conference = (Conference) c;
2909                 conference.removeListener(mConferenceDeathListener);
2910             }
2911         }
2912         mConferenceables.clear();
2913     }
2914 
2915     /**
2916      * Handles a change to extras received from Telecom.
2917      *
2918      * @param extras The new extras.
2919      * @hide
2920      */
handleExtrasChanged(Bundle extras)2921     final void handleExtrasChanged(Bundle extras) {
2922         Bundle b = null;
2923         synchronized (mExtrasLock) {
2924             mExtras = extras;
2925             if (mExtras != null) {
2926                 b = new Bundle(mExtras);
2927             }
2928         }
2929         onExtrasChanged(b);
2930     }
2931 
2932     /**
2933      * Notifies listeners that the merge request failed.
2934      *
2935      * @hide
2936      */
notifyConferenceMergeFailed()2937     protected final void notifyConferenceMergeFailed() {
2938         for (Listener l : mListeners) {
2939             l.onConferenceMergeFailed(this);
2940         }
2941     }
2942 
2943     /**
2944      * Notifies listeners of a change to conference participant(s).
2945      *
2946      * @param conferenceParticipants The participants.
2947      * @hide
2948      */
updateConferenceParticipants( List<ConferenceParticipant> conferenceParticipants)2949     protected final void updateConferenceParticipants(
2950             List<ConferenceParticipant> conferenceParticipants) {
2951         for (Listener l : mListeners) {
2952             l.onConferenceParticipantsChanged(this, conferenceParticipants);
2953         }
2954     }
2955 
2956     /**
2957      * Notifies listeners that a conference call has been started.
2958      * @hide
2959      */
notifyConferenceStarted()2960     protected void notifyConferenceStarted() {
2961         for (Listener l : mListeners) {
2962             l.onConferenceStarted();
2963         }
2964     }
2965 
2966     /**
2967      * Notifies listeners when a change has occurred to the Connection which impacts its ability to
2968      * be a part of a conference call.
2969      * @param isConferenceSupported {@code true} if the connection supports being part of a
2970      *      conference call, {@code false} otherwise.
2971      * @hide
2972      */
notifyConferenceSupportedChanged(boolean isConferenceSupported)2973     protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
2974         for (Listener l : mListeners) {
2975             l.onConferenceSupportedChanged(this, isConferenceSupported);
2976         }
2977     }
2978 
2979     /**
2980      * Sends an event associated with this {@code Connection} with associated event extras to the
2981      * {@link InCallService}.
2982      * <p>
2983      * Connection events are used to communicate point in time information from a
2984      * {@link ConnectionService} to a {@link InCallService} implementations.  An example of a
2985      * custom connection event includes notifying the UI when a WIFI call has been handed over to
2986      * LTE, which the InCall UI might use to inform the user that billing charges may apply.  The
2987      * Android Telephony framework will send the {@link #EVENT_CALL_MERGE_FAILED} connection event
2988      * when a call to {@link Call#mergeConference()} has failed to complete successfully.  A
2989      * connection event could also be used to trigger UI in the {@link InCallService} which prompts
2990      * the user to make a choice (e.g. whether they want to incur roaming costs for making a call),
2991      * which is communicated back via {@link Call#sendCallEvent(String, Bundle)}.
2992      * <p>
2993      * Events are exposed to {@link InCallService} implementations via
2994      * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
2995      * <p>
2996      * No assumptions should be made as to how an In-Call UI or service will handle these events.
2997      * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
2998      * some events altogether.
2999      * <p>
3000      * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
3001      * conflicts between {@link ConnectionService} implementations.  Further, custom
3002      * {@link ConnectionService} implementations shall not re-purpose events in the
3003      * {@code android.*} namespace, nor shall they define new event types in this namespace.  When
3004      * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
3005      * defined.  Extra keys for this bundle should be named similar to the event type (e.g.
3006      * {@code com.example.extra.MY_EXTRA}).
3007      * <p>
3008      *  When defining events and the associated extras, it is important to keep their behavior
3009      * consistent when the associated {@link ConnectionService} is updated.  Support for deprecated
3010      * events/extras should me maintained to ensure backwards compatibility with older
3011      * {@link InCallService} implementations which were built to support the older behavior.
3012      *
3013      * @param event The connection event.
3014      * @param extras Optional bundle containing extra information associated with the event.
3015      */
sendConnectionEvent(String event, Bundle extras)3016     public void sendConnectionEvent(String event, Bundle extras) {
3017         for (Listener l : mListeners) {
3018             l.onConnectionEvent(this, event, extras);
3019         }
3020     }
3021 }
3022