Connection.java revision 3b4b1dcb607b8932374a4bc8c9ab349b9bb68660
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
17package android.telecom;
18
19import com.android.internal.telecom.IVideoCallback;
20import com.android.internal.telecom.IVideoProvider;
21
22import android.net.Uri;
23import android.os.Handler;
24import android.os.IBinder;
25import android.os.Message;
26import android.os.RemoteException;
27import android.view.Surface;
28
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.List;
32import java.util.Set;
33import java.util.concurrent.ConcurrentHashMap;
34
35/**
36 * Represents a connection to a remote endpoint that carries voice traffic.
37 * <p>
38 * Implementations create a custom subclass of {@code Connection} and return it to the framework
39 * as the return value of
40 * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}
41 * or
42 * {@link ConnectionService#onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
43 * Implementations are then responsible for updating the state of the {@code Connection}, and
44 * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
45 * longer used and associated resources may be recovered.
46 */
47public abstract class Connection {
48
49    public static final int STATE_INITIALIZING = 0;
50
51    public static final int STATE_NEW = 1;
52
53    public static final int STATE_RINGING = 2;
54
55    public static final int STATE_DIALING = 3;
56
57    public static final int STATE_ACTIVE = 4;
58
59    public static final int STATE_HOLDING = 5;
60
61    public static final int STATE_DISCONNECTED = 6;
62
63    // Flag controlling whether PII is emitted into the logs
64    private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
65
66    /** @hide */
67    public abstract static class Listener {
68        public void onStateChanged(Connection c, int state) {}
69        public void onAddressChanged(Connection c, Uri newAddress, int presentation) {}
70        public void onCallerDisplayNameChanged(
71                Connection c, String callerDisplayName, int presentation) {}
72        public void onVideoStateChanged(Connection c, int videoState) {}
73        public void onDisconnected(Connection c, DisconnectCause disconnectCause) {}
74        public void onPostDialWait(Connection c, String remaining) {}
75        public void onRingbackRequested(Connection c, boolean ringback) {}
76        public void onDestroyed(Connection c) {}
77        public void onCallCapabilitiesChanged(Connection c, int callCapabilities) {}
78        public void onVideoProviderChanged(
79                Connection c, VideoProvider videoProvider) {}
80        public void onAudioModeIsVoipChanged(Connection c, boolean isVoip) {}
81        public void onStatusHintsChanged(Connection c, StatusHints statusHints) {}
82        public void onConferenceableConnectionsChanged(
83                Connection c, List<Connection> conferenceableConnections) {}
84        public void onConferenceChanged(Connection c, Conference conference) {}
85        /** @hide */
86        public void onConferenceParticipantChanged(Connection c, ConferenceParticipant participant)
87        {}
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
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 {
210            public void setVideoCallback(IBinder videoCallbackBinder) {
211                mMessageHandler.obtainMessage(
212                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
213            }
214
215            public void setCamera(String cameraId) {
216                mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget();
217            }
218
219            public void setPreviewSurface(Surface surface) {
220                mMessageHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
221            }
222
223            public void setDisplaySurface(Surface surface) {
224                mMessageHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
225            }
226
227            public void setDeviceOrientation(int rotation) {
228                mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
229            }
230
231            public void setZoom(float value) {
232                mMessageHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
233            }
234
235            public void sendSessionModifyRequest(VideoProfile requestProfile) {
236                mMessageHandler.obtainMessage(
237                        MSG_SEND_SESSION_MODIFY_REQUEST, requestProfile).sendToTarget();
238            }
239
240            public void sendSessionModifyResponse(VideoProfile responseProfile) {
241                mMessageHandler.obtainMessage(
242                        MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
243            }
244
245            public void requestCameraCapabilities() {
246                mMessageHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
247            }
248
249            public void requestCallDataUsage() {
250                mMessageHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
251            }
252
253            public void setPauseImage(String uri) {
254                mMessageHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
255            }
256        }
257
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         */
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         */
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         */
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         */
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         */
299        public abstract void onSetDeviceOrientation(int rotation);
300
301        /**
302         * Sets camera zoom ratio.
303         *
304         * @param value The camera zoom ratio.
305         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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         */
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     */
485    public Connection() {}
486
487    /**
488     * @return The address (e.g., phone number) to which this Connection is currently communicating.
489     */
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     */
498    public final int getAddressPresentation() {
499        return mAddressPresentation;
500    }
501
502    /**
503     * @return The caller display name (CNAP).
504     */
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     */
513    public final int getCallerDisplayNamePresentation() {
514        return mCallerDisplayNamePresentation;
515    }
516
517    /**
518     * @return The state of this Connection.
519     */
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     */
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     */
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     */
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     */
559    public final boolean isRingbackRequested() {
560        return mRingbackRequested;
561    }
562
563    /**
564     * @return True if the connection's audio mode is VOIP.
565     */
566    public final boolean getAudioModeIsVoip() {
567        return mAudioModeIsVoip;
568    }
569
570    /**
571     * @return The status hints for this connection.
572     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
733    public final void setInitialized() {
734        setState(STATE_NEW);
735    }
736
737    /**
738     * Sets state to dialing (e.g., dialing an outbound call).
739     */
740    public final void setDialing() {
741        setState(STATE_DIALING);
742    }
743
744    /**
745     * Sets state to be on hold.
746     */
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     */
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 */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
875    public final List<Connection> getConferenceableConnections() {
876        return mUnmodifiableConferenceableConnections;
877    }
878
879    /*
880     * @hide
881     */
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     */
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     */
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     */
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                onConferenceChanged();
925            }
926            return true;
927        }
928        return false;
929    }
930
931    /**
932     * Resets the conference that this connection is a part of.
933     * @hide
934     */
935    public final void resetConference() {
936        if (mConference != null) {
937            Log.d(this, "Conference reset");
938            mConference = null;
939            fireConferenceChanged();
940            onConferenceChanged();
941        }
942    }
943
944    /**
945     * Notifies this Connection that the {@link #getAudioState()} property has a new value.
946     *
947     * @param state The new call audio state.
948     */
949    public void onAudioStateChanged(AudioState state) {}
950
951    /**
952     * Notifies this Connection of an internal state change. This method is called after the
953     * state is changed.
954     *
955     * @param state The new state, one of the {@code STATE_*} constants.
956     */
957    public void onStateChanged(int state) {}
958
959    /**
960     * Notifies this Connection of a request to play a DTMF tone.
961     *
962     * @param c A DTMF character.
963     */
964    public void onPlayDtmfTone(char c) {}
965
966    /**
967     * Notifies this Connection of a request to stop any currently playing DTMF tones.
968     */
969    public void onStopDtmfTone() {}
970
971    /**
972     * Notifies this Connection of a request to disconnect.
973     */
974    public void onDisconnect() {}
975
976    /**
977     * Notifies this Connection of a request to disconnect a participant of the conference managed
978     * by the connection.
979     *
980     * @param endpoint the {@link Uri} of the participant to disconnect.
981     * @hide
982     */
983    public void onDisconnectConferenceParticipant(Uri endpoint) {}
984
985    /**
986     * Notifies this Connection of a request to separate from its parent conference.
987     */
988    public void onSeparate() {}
989
990    /**
991     * Notifies this Connection of a request to abort.
992     */
993    public void onAbort() {}
994
995    /**
996     * Notifies this Connection of a request to hold.
997     */
998    public void onHold() {}
999
1000    /**
1001     * Notifies this Connection of a request to exit a hold state.
1002     */
1003    public void onUnhold() {}
1004
1005    /**
1006     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1007     * a request to accept.
1008     *
1009     * @param videoState The video state in which to answer the call.
1010     * @hide
1011     */
1012    public void onAnswer(int videoState) {}
1013
1014    /**
1015     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1016     * a request to accept.
1017     */
1018    public void onAnswer() {
1019        onAnswer(VideoProfile.VideoState.AUDIO_ONLY);
1020    }
1021
1022    /**
1023     * Notifies this Connection, which is in {@link #STATE_RINGING}, of
1024     * a request to reject.
1025     */
1026    public void onReject() {}
1027
1028    /**
1029     * Notifies this Connection whether the user wishes to proceed with the post-dial DTMF codes.
1030     */
1031    public void onPostDialContinue(boolean proceed) {}
1032
1033    /**
1034     * Notifies this Connection that the conference which is set on it has changed.
1035     */
1036    public void onConferenceChanged() {}
1037
1038    static String toLogSafePhoneNumber(String number) {
1039        // For unknown number, log empty string.
1040        if (number == null) {
1041            return "";
1042        }
1043
1044        if (PII_DEBUG) {
1045            // When PII_DEBUG is true we emit PII.
1046            return number;
1047        }
1048
1049        // Do exactly same thing as Uri#toSafeString() does, which will enable us to compare
1050        // sanitized phone numbers.
1051        StringBuilder builder = new StringBuilder();
1052        for (int i = 0; i < number.length(); i++) {
1053            char c = number.charAt(i);
1054            if (c == '-' || c == '@' || c == '.') {
1055                builder.append(c);
1056            } else {
1057                builder.append('x');
1058            }
1059        }
1060        return builder.toString();
1061    }
1062
1063    private void setState(int state) {
1064        if (mState == STATE_DISCONNECTED && mState != state) {
1065            Log.d(this, "Connection already DISCONNECTED; cannot transition out of this state.");
1066            return;
1067        }
1068        if (mState != state) {
1069            Log.d(this, "setState: %s", stateToString(state));
1070            mState = state;
1071            onStateChanged(state);
1072            for (Listener l : mListeners) {
1073                l.onStateChanged(this, state);
1074            }
1075        }
1076    }
1077
1078    private static class FailureSignalingConnection extends Connection {
1079        public FailureSignalingConnection(DisconnectCause disconnectCause) {
1080            setDisconnected(disconnectCause);
1081        }
1082    }
1083
1084    /**
1085     * Return a {@code Connection} which represents a failed connection attempt. The returned
1086     * {@code Connection} will have a {@link android.telecom.DisconnectCause} and as specified,
1087     * and a {@link #getState()} of {@link #STATE_DISCONNECTED}.
1088     * <p>
1089     * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
1090     * so users of this method need not maintain a reference to its return value to destroy it.
1091     *
1092     * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
1093     * @return A {@code Connection} which indicates failure.
1094     */
1095    public static Connection createFailedConnection(DisconnectCause disconnectCause) {
1096        return new FailureSignalingConnection(disconnectCause);
1097    }
1098
1099    /**
1100     * Return a {@code Connection} which represents a canceled connection attempt. The returned
1101     * {@code Connection} will have state {@link #STATE_DISCONNECTED}, and cannot be moved out of
1102     * that state. This connection should not be used for anything, and no other
1103     * {@code Connection}s should be attempted.
1104     * <p>
1105     * The returned {@code Connection} can be assumed to {@link #destroy()} itself when appropriate,
1106     * so users of this method need not maintain a reference to its return value to destroy it.
1107     *
1108     * @return A {@code Connection} which indicates that the underlying call should be canceled.
1109     */
1110    public static Connection createCanceledConnection() {
1111        return new FailureSignalingConnection(new DisconnectCause(DisconnectCause.CANCELED));
1112    }
1113
1114    private final void  fireOnConferenceableConnectionsChanged() {
1115        for (Listener l : mListeners) {
1116            l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
1117        }
1118    }
1119
1120    private final void fireConferenceChanged() {
1121        for (Listener l : mListeners) {
1122            l.onConferenceChanged(this, mConference);
1123        }
1124    }
1125
1126    private final void clearConferenceableList() {
1127        for (Connection c : mConferenceableConnections) {
1128            c.removeConnectionListener(mConnectionDeathListener);
1129        }
1130        mConferenceableConnections.clear();
1131    }
1132
1133    /**
1134     * Notifies listeners of a change to a conference participant.
1135     *
1136     * @param conferenceParticipant The participant.
1137     * @hide
1138     */
1139    protected final void updateConferenceParticipant(ConferenceParticipant conferenceParticipant) {
1140        for (Listener l : mListeners) {
1141            l.onConferenceParticipantChanged(this, conferenceParticipant);
1142        }
1143    }
1144}
1145