• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.telecom;
18 
19 import com.android.internal.telecom.IVideoCallback;
20 import com.android.internal.telecom.IVideoProvider;
21 
22 import android.annotation.SystemApi;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.IBinder;
26 import android.os.Message;
27 import android.os.RemoteException;
28 import android.view.Surface;
29 
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Set;
34 import java.util.concurrent.ConcurrentHashMap;
35 
36 /**
37  * Represents a connection to a remote endpoint that carries voice traffic.
38  * <p>
39  * Implementations create a custom subclass of {@code Connection} and return it to the framework
40  * as the return value of
41  * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
42  * or
43  * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
44  * Implementations are then responsible for updating the state of the {@code Connection}, and
45  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
46  * longer used and associated resources may be recovered.
47  * @hide
48  */
49 @SystemApi
50 public abstract class Connection {
51 
52     public static final int STATE_INITIALIZING = 0;
53 
54     public static final int STATE_NEW = 1;
55 
56     public static final int STATE_RINGING = 2;
57 
58     public static final int STATE_DIALING = 3;
59 
60     public static final int STATE_ACTIVE = 4;
61 
62     public static final int STATE_HOLDING = 5;
63 
64     public static final int STATE_DISCONNECTED = 6;
65 
66     // Flag controlling whether PII is emitted into the logs
67     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
68 
69     /** @hide */
70     public abstract static class Listener {
onStateChanged(Connection c, int state)71         public void onStateChanged(Connection c, int state) {}
onAddressChanged(Connection c, Uri newAddress, int presentation)72         public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
onCallerDisplayNameChanged( Connection c, String callerDisplayName, int presentation)73         public void onCallerDisplayNameChanged(
74                 Connection c, String callerDisplayName, int presentation) {}
onVideoStateChanged(Connection c, int videoState)75         public void onVideoStateChanged(Connection c, int videoState) {}
onDisconnected(Connection c, DisconnectCause disconnectCause)76         public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
onPostDialWait(Connection c, String remaining)77         public void onPostDialWait(Connection c, String remaining) {}
onRingbackRequested(Connection c, boolean ringback)78         public void onRingbackRequested(Connection c, boolean ringback) {}
onDestroyed(Connection c)79         public void onDestroyed(Connection c) {}
onCallCapabilitiesChanged(Connection c, int callCapabilities)80         public void onCallCapabilitiesChanged(Connection c, int callCapabilities) {}
onVideoProviderChanged( Connection c, VideoProvider videoProvider)81         public void onVideoProviderChanged(
82                 Connection c, VideoProvider videoProvider) {}
onAudioModeIsVoipChanged(Connection c, boolean isVoip)83         public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
onStatusHintsChanged(Connection c, StatusHints statusHints)84         public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
onConferenceableConnectionsChanged( Connection c, List<Connection> conferenceableConnections)85         public void onConferenceableConnectionsChanged(
86                 Connection c, List<Connection> conferenceableConnections) {}
onConferenceChanged(Connection c, Conference conference)87         public void onConferenceChanged(Connection c, Conference conference) {}
88     }
89 
90     /** @hide */
91     public static abstract class VideoProvider {
92 
93         /**
94          * Video is not being received (no protocol pause was issued).
95          */
96         public static final int SESSION_EVENT_RX_PAUSE = 1;
97 
98         /**
99          * Video reception has resumed after a SESSION_EVENT_RX_PAUSE.
100          */
101         public static final int SESSION_EVENT_RX_RESUME = 2;
102 
103         /**
104          * Video transmission has begun. This occurs after a negotiated start of video transmission
105          * when the underlying protocol has actually begun transmitting video to the remote party.
106          */
107         public static final int SESSION_EVENT_TX_START = 3;
108 
109         /**
110          * Video transmission has stopped. This occurs after a negotiated stop of video transmission
111          * when the underlying protocol has actually stopped transmitting video to the remote party.
112          */
113         public static final int SESSION_EVENT_TX_STOP = 4;
114 
115         /**
116          * A camera failure has occurred for the selected camera.  The In-Call UI can use this as a
117          * cue to inform the user the camera is not available.
118          */
119         public static final int SESSION_EVENT_CAMERA_FAILURE = 5;
120 
121         /**
122          * Issued after {@code SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready for
123          * operation.  The In-Call UI can use this as a cue to inform the user that the camera has
124          * become available again.
125          */
126         public static final int SESSION_EVENT_CAMERA_READY = 6;
127 
128         /**
129          * Session modify request was successful.
130          */
131         public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
132 
133         /**
134          * Session modify request failed.
135          */
136         public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
137 
138         /**
139          * Session modify request ignored due to invalid parameters.
140          */
141         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
142 
143         private static final int MSG_SET_VIDEO_CALLBACK = 1;
144         private static final int MSG_SET_CAMERA = 2;
145         private static final int MSG_SET_PREVIEW_SURFACE = 3;
146         private static final int MSG_SET_DISPLAY_SURFACE = 4;
147         private static final int MSG_SET_DEVICE_ORIENTATION = 5;
148         private static final int MSG_SET_ZOOM = 6;
149         private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
150         private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
151         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
152         private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
153         private static final int MSG_SET_PAUSE_IMAGE = 11;
154 
155         private final VideoProvider.VideoProviderHandler
156                 mMessageHandler = new VideoProvider.VideoProviderHandler();
157         private final VideoProvider.VideoProviderBinder mBinder;
158         private IVideoCallback mVideoCallback;
159 
160         /**
161          * Default handler used to consolidate binder method calls onto a single thread.
162          */
163         private final class VideoProviderHandler extends Handler {
164             @Override
handleMessage(Message msg)165             public void handleMessage(Message msg) {
166                 switch (msg.what) {
167                     case MSG_SET_VIDEO_CALLBACK:
168                         mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
169                         break;
170                     case MSG_SET_CAMERA:
171                         onSetCamera((String) msg.obj);
172                         break;
173                     case MSG_SET_PREVIEW_SURFACE:
174                         onSetPreviewSurface((Surface) msg.obj);
175                         break;
176                     case MSG_SET_DISPLAY_SURFACE:
177                         onSetDisplaySurface((Surface) msg.obj);
178                         break;
179                     case MSG_SET_DEVICE_ORIENTATION:
180                         onSetDeviceOrientation(msg.arg1);
181                         break;
182                     case MSG_SET_ZOOM:
183                         onSetZoom((Float) msg.obj);
184                         break;
185                     case MSG_SEND_SESSION_MODIFY_REQUEST:
186                         onSendSessionModifyRequest((VideoProfile) msg.obj);
187                         break;
188                     case MSG_SEND_SESSION_MODIFY_RESPONSE:
189                         onSendSessionModifyResponse((VideoProfile) msg.obj);
190                         break;
191                     case MSG_REQUEST_CAMERA_CAPABILITIES:
192                         onRequestCameraCapabilities();
193                         break;
194                     case MSG_REQUEST_CALL_DATA_USAGE:
195                         onRequestCallDataUsage();
196                         break;
197                     case MSG_SET_PAUSE_IMAGE:
198                         onSetPauseImage((String) msg.obj);
199                         break;
200                     default:
201                         break;
202                 }
203             }
204         }
205 
206         /**
207          * IVideoProvider stub implementation.
208          */
209         private final class VideoProviderBinder extends IVideoProvider.Stub {
setVideoCallback(IBinder videoCallbackBinder)210             public void setVideoCallback(IBinder videoCallbackBinder) {
211                 mMessageHandler.obtainMessage(
212                         MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
213             }
214 
setCamera(String cameraId)215             public void setCamera(String cameraId) {
216                 mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
217             }
218 
setPreviewSurface(Surface surface)219             public void setPreviewSurface(Surface surface) {
220                 mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
221             }
222 
setDisplaySurface(Surface surface)223             public void setDisplaySurface(Surface surface) {
224                 mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
225             }
226 
setDeviceOrientation(int rotation)227             public void setDeviceOrientation(int rotation) {
228                 mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
229             }
230 
setZoom(float value)231             public void setZoom(float value) {
232                 mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
233             }
234 
sendSessionModifyRequest(VideoProfile requestProfile)235             public void sendSessionModifyRequest(VideoProfile requestProfile) {
236                 mMessageHandler.obtainMessage(
237                         MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
238             }
239 
sendSessionModifyResponse(VideoProfile responseProfile)240             public void sendSessionModifyResponse(VideoProfile responseProfile) {
241                 mMessageHandler.obtainMessage(
242                         MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
243             }
244 
requestCameraCapabilities()245             public void requestCameraCapabilities() {
246                 mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
247             }
248 
requestCallDataUsage()249             public void requestCallDataUsage() {
250                 mMessageHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
251             }
252 
setPauseImage(String uri)253             public void setPauseImage(String uri) {
254                 mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
255             }
256         }
257 
VideoProvider()258         public VideoProvider() {
259             mBinder = new VideoProvider.VideoProviderBinder();
260         }
261 
262         /**
263          * Returns binder object which can be used across IPC methods.
264          * @hide
265          */
getInterface()266         public final IVideoProvider getInterface() {
267             return mBinder;
268         }
269 
270         /**
271          * Sets the camera to be used for video recording in a video call.
272          *
273          * @param cameraId The id of the camera.
274          */
onSetCamera(String cameraId)275         public abstract void onSetCamera(String cameraId);
276 
277         /**
278          * Sets the surface to be used for displaying a preview of what the user's camera is
279          * currently capturing.  When video transmission is enabled, this is the video signal which
280          * is sent to the remote device.
281          *
282          * @param surface The surface.
283          */
onSetPreviewSurface(Surface surface)284         public abstract void onSetPreviewSurface(Surface surface);
285 
286         /**
287          * Sets the surface to be used for displaying the video received from the remote device.
288          *
289          * @param surface The surface.
290          */
onSetDisplaySurface(Surface surface)291         public abstract void onSetDisplaySurface(Surface surface);
292 
293         /**
294          * Sets the device orientation, in degrees.  Assumes that a standard portrait orientation of
295          * the device is 0 degrees.
296          *
297          * @param rotation The device orientation, in degrees.
298          */
onSetDeviceOrientation(int rotation)299         public abstract void onSetDeviceOrientation(int rotation);
300 
301         /**
302          * Sets camera zoom ratio.
303          *
304          * @param value The camera zoom ratio.
305          */
onSetZoom(float value)306         public abstract void onSetZoom(float value);
307 
308         /**
309          * Issues a request to modify the properties of the current session.  The request is
310          * sent to the remote device where it it handled by the In-Call UI.
311          * Some examples of session modification requests: upgrade call from audio to video,
312          * downgrade call from video to audio, pause video.
313          *
314          * @param requestProfile The requested call video properties.
315          */
onSendSessionModifyRequest(VideoProfile requestProfile)316         public abstract void onSendSessionModifyRequest(VideoProfile requestProfile);
317 
318         /**te
319          * Provides a response to a request to change the current call session video
320          * properties.
321          * This is in response to a request the InCall UI has received via the InCall UI.
322          *
323          * @param responseProfile The response call video properties.
324          */
onSendSessionModifyResponse(VideoProfile responseProfile)325         public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
326 
327         /**
328          * Issues a request to the video provider to retrieve the camera capabilities.
329          * Camera capabilities are reported back to the caller via the In-Call UI.
330          */
onRequestCameraCapabilities()331         public abstract void onRequestCameraCapabilities();
332 
333         /**
334          * Issues a request to the video telephony framework to retrieve the cumulative data usage
335          * for the current call.  Data usage is reported back to the caller via the
336          * InCall UI.
337          */
onRequestCallDataUsage()338         public abstract void onRequestCallDataUsage();
339 
340         /**
341          * Provides the video telephony framework with the URI of an image to be displayed to remote
342          * devices when the video signal is paused.
343          *
344          * @param uri URI of image to display.
345          */
onSetPauseImage(String uri)346         public abstract void onSetPauseImage(String uri);
347 
348         /**
349          * Invokes callback method defined in In-Call UI.
350          *
351          * @param videoProfile The requested video call profile.
352          */
receiveSessionModifyRequest(VideoProfile videoProfile)353         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
354             if (mVideoCallback != null) {
355                 try {
356                     mVideoCallback.receiveSessionModifyRequest(videoProfile);
357                 } catch (RemoteException ignored) {
358                 }
359             }
360         }
361 
362         /**
363          * Invokes callback method defined in In-Call UI.
364          *
365          * @param status Status of the session modify request.  Valid values are
366          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
367          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_FAIL},
368          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_INVALID}
369          * @param requestedProfile The original request which was sent to the remote device.
370          * @param responseProfile The actual profile changes made by the remote device.
371          */
receiveSessionModifyResponse(int status, VideoProfile requestedProfile, VideoProfile responseProfile)372         public void receiveSessionModifyResponse(int status,
373                 VideoProfile requestedProfile, VideoProfile responseProfile) {
374             if (mVideoCallback != null) {
375                 try {
376                     mVideoCallback.receiveSessionModifyResponse(
377                             status, requestedProfile, responseProfile);
378                 } catch (RemoteException ignored) {
379                 }
380             }
381         }
382 
383         /**
384          * Invokes callback method defined in In-Call UI.
385          *
386          * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
387          * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
388          * {@link VideoProvider#SESSION_EVENT_TX_START},
389          * {@link VideoProvider#SESSION_EVENT_TX_STOP}
390          *
391          * @param event The event.
392          */
handleCallSessionEvent(int event)393         public void handleCallSessionEvent(int event) {
394             if (mVideoCallback != null) {
395                 try {
396                     mVideoCallback.handleCallSessionEvent(event);
397                 } catch (RemoteException ignored) {
398                 }
399             }
400         }
401 
402         /**
403          * Invokes callback method defined in In-Call UI.
404          *
405          * @param width  The updated peer video width.
406          * @param height The updated peer video height.
407          */
changePeerDimensions(int width, int height)408         public void changePeerDimensions(int width, int height) {
409             if (mVideoCallback != null) {
410                 try {
411                     mVideoCallback.changePeerDimensions(width, height);
412                 } catch (RemoteException ignored) {
413                 }
414             }
415         }
416 
417         /**
418          * Invokes callback method defined in In-Call UI.
419          *
420          * @param dataUsage The updated data usage.
421          */
changeCallDataUsage(int dataUsage)422         public void changeCallDataUsage(int dataUsage) {
423             if (mVideoCallback != null) {
424                 try {
425                     mVideoCallback.changeCallDataUsage(dataUsage);
426                 } catch (RemoteException ignored) {
427                 }
428             }
429         }
430 
431         /**
432          * Invokes callback method defined in In-Call UI.
433          *
434          * @param cameraCapabilities The changed camera capabilities.
435          */
changeCameraCapabilities(CameraCapabilities cameraCapabilities)436         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
437             if (mVideoCallback != null) {
438                 try {
439                     mVideoCallback.changeCameraCapabilities(cameraCapabilities);
440                 } catch (RemoteException ignored) {
441                 }
442             }
443         }
444     }
445 
446     private final Listener mConnectionDeathListener = new Listener() {
447         @Override
448         public void onDestroyed(Connection c) {
449             if (mConferenceableConnections.remove(c)) {
450                 fireOnConferenceableConnectionsChanged();
451             }
452         }
453     };
454 
455     /**
456      * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
457      * load factor before resizing, 1 means we only expect a single thread to
458      * access the map so make only a single shard
459      */
460     private final Set<Listener> mListeners = Collections.newSetFromMap(
461             new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
462     private final List<Connection> mConferenceableConnections = new ArrayList<>();
463     private final List<Connection> mUnmodifiableConferenceableConnections =
464             Collections.unmodifiableList(mConferenceableConnections);
465 
466     private int mState = STATE_NEW;
467     private AudioState mAudioState;
468     private Uri mAddress;
469     private int mAddressPresentation;
470     private String mCallerDisplayName;
471     private int mCallerDisplayNamePresentation;
472     private boolean mRingbackRequested = false;
473     private int mCallCapabilities;
474     private VideoProvider mVideoProvider;
475     private boolean mAudioModeIsVoip;
476     private StatusHints mStatusHints;
477     private int mVideoState;
478     private DisconnectCause mDisconnectCause;
479     private Conference mConference;
480     private ConnectionService mConnectionService;
481 
482     /**
483      * Create a new Connection.
484      */
Connection()485     public Connection() {}
486 
487     /**
488      * @return The address (e.g., phone number) to which this Connection is currently communicating.
489      */
getAddress()490     public final Uri getAddress() {
491         return mAddress;
492     }
493 
494     /**
495      * @return The presentation requirements for the address.
496      *         See {@link TelecomManager} for valid values.
497      */
getAddressPresentation()498     public final int getAddressPresentation() {
499         return mAddressPresentation;
500     }
501 
502     /**
503      * @return The caller display name (CNAP).
504      */
getCallerDisplayName()505     public final String getCallerDisplayName() {
506         return mCallerDisplayName;
507     }
508 
509     /**
510      * @return The presentation requirements for the handle.
511      *         See {@link TelecomManager} for valid values.
512      */
getCallerDisplayNamePresentation()513     public final int getCallerDisplayNamePresentation() {
514         return mCallerDisplayNamePresentation;
515     }
516 
517     /**
518      * @return The state of this Connection.
519      */
getState()520     public final int getState() {
521         return mState;
522     }
523 
524     /**
525      * Returns the video state of the call.
526      * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
527      * {@link VideoProfile.VideoState#BIDIRECTIONAL},
528      * {@link VideoProfile.VideoState#TX_ENABLED},
529      * {@link VideoProfile.VideoState#RX_ENABLED}.
530      *
531      * @return The video state of the call.
532      * @hide
533      */
getVideoState()534     public final int getVideoState() {
535         return mVideoState;
536     }
537 
538     /**
539      * @return The audio state of the call, describing how its audio is currently
540      *         being routed by the system. This is {@code null} if this Connection
541      *         does not directly know about its audio state.
542      */
getAudioState()543     public final AudioState getAudioState() {
544         return mAudioState;
545     }
546 
547     /**
548      * @return The conference that this connection is a part of.  Null if it is not part of any
549      *         conference.
550      */
getConference()551     public final Conference getConference() {
552         return mConference;
553     }
554 
555     /**
556      * Returns whether this connection is requesting that the system play a ringback tone
557      * on its behalf.
558      */
isRingbackRequested()559     public final boolean isRingbackRequested() {
560         return mRingbackRequested;
561     }
562 
563     /**
564      * @return True if the connection's audio mode is VOIP.
565      */
getAudioModeIsVoip()566     public final boolean getAudioModeIsVoip() {
567         return mAudioModeIsVoip;
568     }
569 
570     /**
571      * @return The status hints for this connection.
572      */
getStatusHints()573     public final StatusHints getStatusHints() {
574         return mStatusHints;
575     }
576 
577     /**
578      * Assign a listener to be notified of state changes.
579      *
580      * @param l A listener.
581      * @return This Connection.
582      *
583      * @hide
584      */
addConnectionListener(Listener l)585     public final Connection addConnectionListener(Listener l) {
586         mListeners.add(l);
587         return this;
588     }
589 
590     /**
591      * Remove a previously assigned listener that was being notified of state changes.
592      *
593      * @param l A Listener.
594      * @return This Connection.
595      *
596      * @hide
597      */
removeConnectionListener(Listener l)598     public final Connection removeConnectionListener(Listener l) {
599         if (l != null) {
600             mListeners.remove(l);
601         }
602         return this;
603     }
604 
605     /**
606      * @return The {@link DisconnectCause} for this connection.
607      */
getDisconnectCause()608     public final DisconnectCause getDisconnectCause() {
609         return mDisconnectCause;
610     }
611 
612     /**
613      * Inform this Connection that the state of its audio output has been changed externally.
614      *
615      * @param state The new audio state.
616      * @hide
617      */
setAudioState(AudioState state)618     final void setAudioState(AudioState state) {
619         Log.d(this, "setAudioState %s", state);
620         mAudioState = state;
621         onAudioStateChanged(state);
622     }
623 
624     /**
625      * @param state An integer value of a {@code STATE_*} constant.
626      * @return A string representation of the value.
627      */
stateToString(int state)628     public static String stateToString(int state) {
629         switch (state) {
630             case STATE_INITIALIZING:
631                 return "STATE_INITIALIZING";
632             case STATE_NEW:
633                 return "STATE_NEW";
634             case STATE_RINGING:
635                 return "STATE_RINGING";
636             case STATE_DIALING:
637                 return "STATE_DIALING";
638             case STATE_ACTIVE:
639                 return "STATE_ACTIVE";
640             case STATE_HOLDING:
641                 return "STATE_HOLDING";
642             case STATE_DISCONNECTED:
643                 return "DISCONNECTED";
644             default:
645                 Log.wtf(Connection.class, "Unknown state %d", state);
646                 return "UNKNOWN";
647         }
648     }
649 
650     /**
651      * Returns the connection's {@link PhoneCapabilities}
652      */
getCallCapabilities()653     public final int getCallCapabilities() {
654         return mCallCapabilities;
655     }
656 
657     /**
658      * Sets the value of the {@link #getAddress()} property.
659      *
660      * @param address The new address.
661      * @param presentation The presentation requirements for the address.
662      *        See {@link TelecomManager} for valid values.
663      */
setAddress(Uri address, int presentation)664     public final void setAddress(Uri address, int presentation) {
665         Log.d(this, "setAddress %s", address);
666         mAddress = address;
667         mAddressPresentation = presentation;
668         for (Listener l : mListeners) {
669             l.onAddressChanged(this, address, presentation);
670         }
671     }
672 
673     /**
674      * Sets the caller display name (CNAP).
675      *
676      * @param callerDisplayName The new display name.
677      * @param presentation The presentation requirements for the handle.
678      *        See {@link TelecomManager} for valid values.
679      */
setCallerDisplayName(String callerDisplayName, int presentation)680     public final void setCallerDisplayName(String callerDisplayName, int presentation) {
681         Log.d(this, "setCallerDisplayName %s", callerDisplayName);
682         mCallerDisplayName = callerDisplayName;
683         mCallerDisplayNamePresentation = presentation;
684         for (Listener l : mListeners) {
685             l.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
686         }
687     }
688 
689     /**
690      * Set the video state for the connection.
691      * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
692      * {@link VideoProfile.VideoState#BIDIRECTIONAL},
693      * {@link VideoProfile.VideoState#TX_ENABLED},
694      * {@link VideoProfile.VideoState#RX_ENABLED}.
695      *
696      * @param videoState The new video state.
697      * @hide
698      */
setVideoState(int videoState)699     public final void setVideoState(int videoState) {
700         Log.d(this, "setVideoState %d", videoState);
701         mVideoState = videoState;
702         for (Listener l : mListeners) {
703             l.onVideoStateChanged(this, mVideoState);
704         }
705     }
706 
707     /**
708      * Sets state to active (e.g., an ongoing call where two or more parties can actively
709      * communicate).
710      */
setActive()711     public final void setActive() {
712         setRingbackRequested(false);
713         setState(STATE_ACTIVE);
714     }
715 
716     /**
717      * Sets state to ringing (e.g., an inbound ringing call).
718      */
setRinging()719     public final void setRinging() {
720         setState(STATE_RINGING);
721     }
722 
723     /**
724      * Sets state to initializing (this Connection is not yet ready to be used).
725      */
setInitializing()726     public final void setInitializing() {
727         setState(STATE_INITIALIZING);
728     }
729 
730     /**
731      * Sets state to initialized (the Connection has been set up and is now ready to be used).
732      */
setInitialized()733     public final void setInitialized() {
734         setState(STATE_NEW);
735     }
736 
737     /**
738      * Sets state to dialing (e.g., dialing an outbound call).
739      */
setDialing()740     public final void setDialing() {
741         setState(STATE_DIALING);
742     }
743 
744     /**
745      * Sets state to be on hold.
746      */
setOnHold()747     public final void setOnHold() {
748         setState(STATE_HOLDING);
749     }
750 
751     /**
752      * Sets the video call provider.
753      * @param videoProvider The video provider.
754      * @hide
755      */
setVideoProvider(VideoProvider videoProvider)756     public final void setVideoProvider(VideoProvider videoProvider) {
757         mVideoProvider = videoProvider;
758         for (Listener l : mListeners) {
759             l.onVideoProviderChanged(this, videoProvider);
760         }
761     }
762 
763     /** @hide */
getVideoProvider()764     public final VideoProvider getVideoProvider() {
765         return mVideoProvider;
766     }
767 
768     /**
769      * Sets state to disconnected.
770      *
771      * @param disconnectCause The reason for the disconnection, as specified by
772      *         {@link DisconnectCause}.
773      */
setDisconnected(DisconnectCause disconnectCause)774     public final void setDisconnected(DisconnectCause disconnectCause) {
775         mDisconnectCause = disconnectCause;
776         setState(STATE_DISCONNECTED);
777         Log.d(this, "Disconnected with cause %s", disconnectCause);
778         for (Listener l : mListeners) {
779             l.onDisconnected(this, disconnectCause);
780         }
781     }
782 
783     /**
784      * TODO: Needs documentation.
785      */
setPostDialWait(String remaining)786     public final void setPostDialWait(String remaining) {
787         for (Listener l : mListeners) {
788             l.onPostDialWait(this, remaining);
789         }
790     }
791 
792     /**
793      * Requests that the framework play a ringback tone. This is to be invoked by implementations
794      * that do not play a ringback tone themselves in the call's audio stream.
795      *
796      * @param ringback Whether the ringback tone is to be played.
797      */
setRingbackRequested(boolean ringback)798     public final void setRingbackRequested(boolean ringback) {
799         if (mRingbackRequested != ringback) {
800             mRingbackRequested = ringback;
801             for (Listener l : mListeners) {
802                 l.onRingbackRequested(this, ringback);
803             }
804         }
805     }
806 
807     /**
808      * Sets the connection's {@link PhoneCapabilities}.
809      *
810      * @param callCapabilities The new call capabilities.
811      */
setCallCapabilities(int callCapabilities)812     public final void setCallCapabilities(int callCapabilities) {
813         if (mCallCapabilities != callCapabilities) {
814             mCallCapabilities = callCapabilities;
815             for (Listener l : mListeners) {
816                 l.onCallCapabilitiesChanged(this, mCallCapabilities);
817             }
818         }
819     }
820 
821     /**
822      * Tears down the Connection object.
823      */
destroy()824     public final void destroy() {
825         for (Listener l : mListeners) {
826             l.onDestroyed(this);
827         }
828     }
829 
830     /**
831      * Requests that the framework use VOIP audio mode for this connection.
832      *
833      * @param isVoip True if the audio mode is VOIP.
834      */
setAudioModeIsVoip(boolean isVoip)835     public final void setAudioModeIsVoip(boolean isVoip) {
836         mAudioModeIsVoip = isVoip;
837         for (Listener l : mListeners) {
838             l.onAudioModeIsVoipChanged(this, isVoip);
839         }
840     }
841 
842     /**
843      * Sets the label and icon status to display in the in-call UI.
844      *
845      * @param statusHints The status label and icon to set.
846      */
setStatusHints(StatusHints statusHints)847     public final void setStatusHints(StatusHints statusHints) {
848         mStatusHints = statusHints;
849         for (Listener l : mListeners) {
850             l.onStatusHintsChanged(this, statusHints);
851         }
852     }
853 
854     /**
855      * Sets the connections with which this connection can be conferenced.
856      *
857      * @param conferenceableConnections The set of connections this connection can conference with.
858      */
setConferenceableConnections(List<Connection> conferenceableConnections)859     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
860         clearConferenceableList();
861         for (Connection c : conferenceableConnections) {
862             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
863             // small amount of items here.
864             if (!mConferenceableConnections.contains(c)) {
865                 c.addConnectionListener(mConnectionDeathListener);
866                 mConferenceableConnections.add(c);
867             }
868         }
869         fireOnConferenceableConnectionsChanged();
870     }
871 
872     /**
873      * Returns the connections with which this connection can be conferenced.
874      */
getConferenceableConnections()875     public final List<Connection> getConferenceableConnections() {
876         return mUnmodifiableConferenceableConnections;
877     }
878 
879     /**
880      * @hide
881      */
setConnectionService(ConnectionService connectionService)882     public final void setConnectionService(ConnectionService connectionService) {
883         if (mConnectionService != null) {
884             Log.e(this, new Exception(), "Trying to set ConnectionService on a connection " +
885                     "which is already associated with another ConnectionService.");
886         } else {
887             mConnectionService = connectionService;
888         }
889     }
890 
891     /**
892      * @hide
893      */
unsetConnectionService(ConnectionService connectionService)894     public final void unsetConnectionService(ConnectionService connectionService) {
895         if (mConnectionService != connectionService) {
896             Log.e(this, new Exception(), "Trying to remove ConnectionService from a Connection " +
897                     "that does not belong to the ConnectionService.");
898         } else {
899             mConnectionService = null;
900         }
901     }
902 
903     /**
904      * @hide
905      */
getConnectionService()906     public final ConnectionService getConnectionService() {
907         return mConnectionService;
908     }
909 
910     /**
911      * Sets the conference that this connection is a part of. This will fail if the connection is
912      * already part of a conference call. {@link #resetConference} to un-set the conference first.
913      *
914      * @param conference The conference.
915      * @return {@code true} if the conference was successfully set.
916      * @hide
917      */
setConference(Conference conference)918     public final boolean setConference(Conference conference) {
919         // We check to see if it is already part of another conference.
920         if (mConference == null) {
921             mConference = conference;
922             if (mConnectionService != null && mConnectionService.containsConference(conference)) {
923                 fireConferenceChanged();
924             }
925             return true;
926         }
927         return false;
928     }
929 
930     /**
931      * Resets the conference that this connection is a part of.
932      * @hide
933      */
resetConference()934     public final void resetConference() {
935         if (mConference != null) {
936             Log.d(this, "Conference reset");
937             mConference = null;
938             fireConferenceChanged();
939         }
940     }
941 
942     /**
943      * Notifies this Connection that the {@link #getAudioState()} property has a new value.
944      *
945      * @param state The new call audio state.
946      */
onAudioStateChanged(AudioState state)947     public void onAudioStateChanged(AudioState state) {}
948 
949     /**
950      * Notifies this Connection of an internal state change. This method is called after the
951      * state is changed.
952      *
953      * @param state The new state, one of the {@code STATE_*} constants.
954      */
onStateChanged(int state)955     public void onStateChanged(int state) {}
956 
957     /**
958      * Notifies this Connection of a request to play a DTMF tone.
959      *
960      * @param c A DTMF character.
961      */
onPlayDtmfTone(char c)962     public void onPlayDtmfTone(char c) {}
963 
964     /**
965      * Notifies this Connection of a request to stop any currently playing DTMF tones.
966      */
onStopDtmfTone()967     public void onStopDtmfTone() {}
968 
969     /**
970      * Notifies this Connection of a request to disconnect.
971      */
onDisconnect()972     public void onDisconnect() {}
973 
974     /**
975      * Notifies this Connection of a request to separate from its parent conference.
976      */
onSeparate()977     public void onSeparate() {}
978 
979     /**
980      * Notifies this Connection of a request to abort.
981      */
onAbort()982     public void onAbort() {}
983 
984     /**
985      * Notifies this Connection of a request to hold.
986      */
onHold()987     public void onHold() {}
988 
989     /**
990      * Notifies this Connection of a request to exit a hold state.
991      */
onUnhold()992     public void onUnhold() {}
993 
994     /**
995      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
996      * a request to accept.
997      *
998      * @param videoState The video state in which to answer the call.
999      * @hide
1000      */
onAnswer(int videoState)1001     public void onAnswer(int videoState) {}
1002 
1003     /**
1004      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1005      * a request to accept.
1006      */
onAnswer()1007     public void onAnswer() {
1008         onAnswer(VideoProfile.VideoState.AUDIO_ONLY);
1009     }
1010 
1011     /**
1012      * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1013      * a request to reject.
1014      */
onReject()1015     public void onReject() {}
1016 
1017     /**
1018      * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
1019      */
onPostDialContinue(boolean proceed)1020     public void onPostDialContinue(boolean proceed) {}
1021 
1022     /**
1023      * Merge this connection and the specified connection into a conference call.  Once the
1024      * connections are merged, the calls should be added to the an existing or new
1025      * {@code Conference} instance. For new {@code Conference} instances, use
1026      * {@code ConnectionService#addConference}.
1027      *
1028      * @param otherConnection The connection with which this connection should be conferenced.
1029      */
onConferenceWith(Connection otherConnection)1030     public void onConferenceWith(Connection otherConnection) {}
1031 
toLogSafePhoneNumber(String number)1032     static String toLogSafePhoneNumber(String number) {
1033         // For unknown number, log empty string.
1034         if (number == null) {
1035             return "";
1036         }
1037 
1038         if (PII_DEBUG) {
1039             // When PII_DEBUG is true we emit PII.
1040             return number;
1041         }
1042 
1043         // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
1044         // sanitized phone numbers.
1045         StringBuilder builder = new StringBuilder();
1046         for (int i = 0; i < number.length(); i++) {
1047             char c = number.charAt(i);
1048             if (c == '-' || c == '@' || c == '.') {
1049                 builder.append(c);
1050             } else {
1051                 builder.append('x');
1052             }
1053         }
1054         return builder.toString();
1055     }
1056 
setState(int state)1057     private void setState(int state) {
1058         if (mState == STATE_DISCONNECTED && mState != state) {
1059             Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
1060             return;
1061         }
1062         if (mState != state) {
1063             Log.d(this, "setState: %s", stateToString(state));
1064             mState = state;
1065             onStateChanged(state);
1066             for (Listener l : mListeners) {
1067                 l.onStateChanged(this, state);
1068             }
1069         }
1070     }
1071 
1072     private static class FailureSignalingConnection extends Connection {
FailureSignalingConnection(DisconnectCause disconnectCause)1073         public FailureSignalingConnection(DisconnectCause disconnectCause) {
1074             setDisconnected(disconnectCause);
1075         }
1076     }
1077 
1078     /**
1079      * Return a {@code Connection} which represents a failed connection attempt. The returned
1080      * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
1081      * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
1082      * <p>
1083      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
1084      * so users of this method need not maintain a reference to its return value to destroy it.
1085      *
1086      * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
1087      * @return A {@code Connection} which indicates failure.
1088      */
createFailedConnection(DisconnectCause disconnectCause)1089     public static Connection createFailedConnection(DisconnectCause disconnectCause) {
1090         return new FailureSignalingConnection(disconnectCause);
1091     }
1092 
1093     /**
1094      * Return a {@code Connection} which represents a canceled connection attempt. The returned
1095      * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
1096      * that state. This connection should not be used for anything, and no other
1097      * {@code Connection}s should be attempted.
1098      * <p>
1099      * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
1100      * so users of this method need not maintain a reference to its return value to destroy it.
1101      *
1102      * @return A {@code Connection} which indicates that the underlying call should be canceled.
1103      */
createCanceledConnection()1104     public static Connection createCanceledConnection() {
1105         return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
1106     }
1107 
fireOnConferenceableConnectionsChanged()1108     private final void  fireOnConferenceableConnectionsChanged() {
1109         for (Listener l : mListeners) {
1110             l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
1111         }
1112     }
1113 
fireConferenceChanged()1114     private final void fireConferenceChanged() {
1115         for (Listener l : mListeners) {
1116             l.onConferenceChanged(this, mConference);
1117         }
1118     }
1119 
clearConferenceableList()1120     private final void clearConferenceableList() {
1121         for (Connection c : mConferenceableConnections) {
1122             c.removeConnectionListener(mConnectionDeathListener);
1123         }
1124         mConferenceableConnections.clear();
1125     }
1126 }
1127