RemoteConnection.java revision 55b97525f0f50857f1ab0acd2608053fd0f3416c
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.IConnectionService;
20import com.android.internal.telecom.IVideoCallback;
21import com.android.internal.telecom.IVideoProvider;
22
23import android.net.Uri;
24import android.os.IBinder;
25import android.os.RemoteException;
26import android.view.Surface;
27
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31import java.util.Set;
32import java.util.concurrent.ConcurrentHashMap;
33
34/**
35 * A connection provided to a {@link ConnectionService} by another {@code ConnectionService}
36 * running in a different process.
37 *
38 * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
39 * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
40 */
41public final class RemoteConnection {
42
43    public static abstract class Callback {
44        /**
45         * Invoked when the state of this {@code RemoteConnection} has changed. See
46         * {@link #getState()}.
47         *
48         * @param connection The {@code RemoteConnection} invoking this method.
49         * @param state The new state of the {@code RemoteConnection}.
50         */
51        public void onStateChanged(RemoteConnection connection, int state) {}
52
53        /**
54         * Invoked when this {@code RemoteConnection} is disconnected.
55         *
56         * @param connection The {@code RemoteConnection} invoking this method.
57         * @param disconnectCause The ({@see DisconnectCause}) associated with this failed
58         *     connection.
59         */
60        public void onDisconnected(
61                RemoteConnection connection,
62                DisconnectCause disconnectCause) {}
63
64        /**
65         * Invoked when this {@code RemoteConnection} is requesting ringback. See
66         * {@link #isRingbackRequested()}.
67         *
68         * @param connection The {@code RemoteConnection} invoking this method.
69         * @param ringback Whether the {@code RemoteConnection} is requesting ringback.
70         */
71        public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
72
73        /**
74         * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
75         * See {@link #getConnectionCapabilities()}.
76         *
77         * @param connection The {@code RemoteConnection} invoking this method.
78         * @param connectionCapabilities The new capabilities of the {@code RemoteConnection}.
79         */
80        public void onConnectionCapabilitiesChanged(
81                RemoteConnection connection,
82                int connectionCapabilities) {}
83
84        /**
85         * Invoked when the post-dial sequence in the outgoing {@code Connection} has reached a
86         * pause character. This causes the post-dial signals to stop pending user confirmation. An
87         * implementation should present this choice to the user and invoke
88         * {@link RemoteConnection#postDialContinue(boolean)} when the user makes the choice.
89         *
90         * @param connection The {@code RemoteConnection} invoking this method.
91         * @param remainingPostDialSequence The post-dial characters that remain to be sent.
92         */
93        public void onPostDialWait(RemoteConnection connection, String remainingPostDialSequence) {}
94
95        /**
96         * Invoked when the post-dial sequence in the outgoing {@code Connection} has processed
97         * a character.
98         *
99         * @param connection The {@code RemoteConnection} invoking this method.
100         * @param nextChar The character being processed.
101         */
102        public void onPostDialChar(RemoteConnection connection, char nextChar) {}
103
104        /**
105         * Indicates that the VOIP audio status of this {@code RemoteConnection} has changed.
106         * See {@link #isVoipAudioMode()}.
107         *
108         * @param connection The {@code RemoteConnection} invoking this method.
109         * @param isVoip Whether the new audio state of the {@code RemoteConnection} is VOIP.
110         */
111        public void onVoipAudioChanged(RemoteConnection connection, boolean isVoip) {}
112
113        /**
114         * Indicates that the status hints of this {@code RemoteConnection} have changed. See
115         * {@link #getStatusHints()} ()}.
116         *
117         * @param connection The {@code RemoteConnection} invoking this method.
118         * @param statusHints The new status hints of the {@code RemoteConnection}.
119         */
120        public void onStatusHintsChanged(RemoteConnection connection, StatusHints statusHints) {}
121
122        /**
123         * Indicates that the address (e.g., phone number) of this {@code RemoteConnection} has
124         * changed. See {@link #getAddress()} and {@link #getAddressPresentation()}.
125         *
126         * @param connection The {@code RemoteConnection} invoking this method.
127         * @param address The new address of the {@code RemoteConnection}.
128         * @param presentation The presentation requirements for the address.
129         *        See {@link TelecomManager} for valid values.
130         */
131        public void onAddressChanged(RemoteConnection connection, Uri address, int presentation) {}
132
133        /**
134         * Indicates that the caller display name of this {@code RemoteConnection} has changed.
135         * See {@link #getCallerDisplayName()} and {@link #getCallerDisplayNamePresentation()}.
136         *
137         * @param connection The {@code RemoteConnection} invoking this method.
138         * @param callerDisplayName The new caller display name of the {@code RemoteConnection}.
139         * @param presentation The presentation requirements for the handle.
140         *        See {@link TelecomManager} for valid values.
141         */
142        public void onCallerDisplayNameChanged(
143                RemoteConnection connection, String callerDisplayName, int presentation) {}
144
145        /**
146         * Indicates that the video state of this {@code RemoteConnection} has changed.
147         * See {@link #getVideoState()}.
148         *
149         * @param connection The {@code RemoteConnection} invoking this method.
150         * @param videoState The new video state of the {@code RemoteConnection}.
151         * @hide
152         */
153        public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
154
155        /**
156         * Indicates that the call substate of this {@code RemoteConnection} has changed.
157         * See {@link #getCallSubstate()}.
158         *
159         * @param connection The {@code RemoteConnection} invoking this method.
160         * @param callSubstate The new call substate of the {@code RemoteConnection}.
161         * @hide
162         */
163        public void onCallSubstateChanged(RemoteConnection connection, int callSubstate) {}
164
165        /**
166         * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
167         * should be made to the {@code RemoteConnection}, and references to it should be cleared.
168         *
169         * @param connection The {@code RemoteConnection} invoking this method.
170         */
171        public void onDestroyed(RemoteConnection connection) {}
172
173        /**
174         * Indicates that the {@code RemoteConnection}s with which this {@code RemoteConnection}
175         * may be asked to create a conference has changed.
176         *
177         * @param connection The {@code RemoteConnection} invoking this method.
178         * @param conferenceableConnections The {@code RemoteConnection}s with which this
179         *         {@code RemoteConnection} may be asked to create a conference.
180         */
181        public void onConferenceableConnectionsChanged(
182                RemoteConnection connection,
183                List<RemoteConnection> conferenceableConnections) {}
184
185        /**
186         * Indicates that the {@code VideoProvider} associated with this {@code RemoteConnection}
187         * has changed.
188         *
189         * @param connection The {@code RemoteConnection} invoking this method.
190         * @param videoProvider The new {@code VideoProvider} associated with this
191         *         {@code RemoteConnection}.
192         * @hide
193         */
194        public void onVideoProviderChanged(
195                RemoteConnection connection, VideoProvider videoProvider) {}
196
197        /**
198         * Indicates that the {@code RemoteConference} that this {@code RemoteConnection} is a part
199         * of has changed.
200         *
201         * @param connection The {@code RemoteConnection} invoking this method.
202         * @param conference The {@code RemoteConference} of which this {@code RemoteConnection} is
203         *         a part, which may be {@code null}.
204         */
205        public void onConferenceChanged(
206                RemoteConnection connection,
207                RemoteConference conference) {}
208    }
209
210    /** {@hide} */
211    public static class VideoProvider {
212
213        public abstract static class Listener {
214            public void onReceiveSessionModifyRequest(
215                    VideoProvider videoProvider,
216                    VideoProfile videoProfile) {}
217
218            public void onReceiveSessionModifyResponse(
219                    VideoProvider videoProvider,
220                    int status,
221                    VideoProfile requestedProfile,
222                    VideoProfile responseProfile) {}
223
224            public void onHandleCallSessionEvent(VideoProvider videoProvider, int event) {}
225
226            public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
227
228            public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
229
230            public void onCameraCapabilitiesChanged(
231                    VideoProvider videoProvider,
232                    CameraCapabilities cameraCapabilities) {}
233
234            public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
235        }
236
237        private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
238            @Override
239            public void receiveSessionModifyRequest(VideoProfile videoProfile) {
240                for (Listener l : mListeners) {
241                    l.onReceiveSessionModifyRequest(VideoProvider.this, videoProfile);
242                }
243            }
244
245            @Override
246            public void receiveSessionModifyResponse(int status, VideoProfile requestedProfile,
247                    VideoProfile responseProfile) {
248                for (Listener l : mListeners) {
249                    l.onReceiveSessionModifyResponse(
250                            VideoProvider.this,
251                            status,
252                            requestedProfile,
253                            responseProfile);
254                }
255            }
256
257            @Override
258            public void handleCallSessionEvent(int event) {
259                for (Listener l : mListeners) {
260                    l.onHandleCallSessionEvent(VideoProvider.this, event);
261                }
262            }
263
264            @Override
265            public void changePeerDimensions(int width, int height) {
266                for (Listener l : mListeners) {
267                    l.onPeerDimensionsChanged(VideoProvider.this, width, height);
268                }
269            }
270
271            @Override
272            public void changeCallDataUsage(long dataUsage) {
273                for (Listener l : mListeners) {
274                    l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
275                }
276            }
277
278            @Override
279            public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
280                for (Listener l : mListeners) {
281                    l.onCameraCapabilitiesChanged(VideoProvider.this, cameraCapabilities);
282                }
283            }
284
285            @Override
286            public void changeVideoQuality(int videoQuality) {
287                for (Listener l : mListeners) {
288                    l.onVideoQualityChanged(VideoProvider.this, videoQuality);
289                }
290            }
291
292            @Override
293            public IBinder asBinder() {
294                return null;
295            }
296        };
297
298        private final VideoCallbackServant mVideoCallbackServant =
299                new VideoCallbackServant(mVideoCallbackDelegate);
300
301        private final IVideoProvider mVideoProviderBinder;
302
303        /**
304         * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
305         * load factor before resizing, 1 means we only expect a single thread to
306         * access the map so make only a single shard
307         */
308        private final Set<Listener> mListeners = Collections.newSetFromMap(
309                new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1));
310
311        public VideoProvider(IVideoProvider videoProviderBinder) {
312            mVideoProviderBinder = videoProviderBinder;
313            try {
314                mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
315            } catch (RemoteException e) {
316            }
317        }
318
319        public void addListener(Listener l) {
320            mListeners.add(l);
321        }
322
323        public void removeListener(Listener l) {
324            mListeners.remove(l);
325        }
326
327        public void setCamera(String cameraId) {
328            try {
329                mVideoProviderBinder.setCamera(cameraId);
330            } catch (RemoteException e) {
331            }
332        }
333
334        public void setPreviewSurface(Surface surface) {
335            try {
336                mVideoProviderBinder.setPreviewSurface(surface);
337            } catch (RemoteException e) {
338            }
339        }
340
341        public void setDisplaySurface(Surface surface) {
342            try {
343                mVideoProviderBinder.setDisplaySurface(surface);
344            } catch (RemoteException e) {
345            }
346        }
347
348        public void setDeviceOrientation(int rotation) {
349            try {
350                mVideoProviderBinder.setDeviceOrientation(rotation);
351            } catch (RemoteException e) {
352            }
353        }
354
355        public void setZoom(float value) {
356            try {
357                mVideoProviderBinder.setZoom(value);
358            } catch (RemoteException e) {
359            }
360        }
361
362        public void sendSessionModifyRequest(VideoProfile reqProfile) {
363            try {
364                mVideoProviderBinder.sendSessionModifyRequest(reqProfile);
365            } catch (RemoteException e) {
366            }
367        }
368
369        public void sendSessionModifyResponse(VideoProfile responseProfile) {
370            try {
371                mVideoProviderBinder.sendSessionModifyResponse(responseProfile);
372            } catch (RemoteException e) {
373            }
374        }
375
376        public void requestCameraCapabilities() {
377            try {
378                mVideoProviderBinder.requestCameraCapabilities();
379            } catch (RemoteException e) {
380            }
381        }
382
383        public void requestCallDataUsage() {
384            try {
385                mVideoProviderBinder.requestCallDataUsage();
386            } catch (RemoteException e) {
387            }
388        }
389
390        public void setPauseImage(String uri) {
391            try {
392                mVideoProviderBinder.setPauseImage(uri);
393            } catch (RemoteException e) {
394            }
395        }
396    }
397
398    private IConnectionService mConnectionService;
399    private final String mConnectionId;
400    /**
401     * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
402     * load factor before resizing, 1 means we only expect a single thread to
403     * access the map so make only a single shard
404     */
405    private final Set<Callback> mCallbacks = Collections.newSetFromMap(
406            new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
407    private final List<RemoteConnection> mConferenceableConnections = new ArrayList<>();
408    private final List<RemoteConnection> mUnmodifiableconferenceableConnections =
409            Collections.unmodifiableList(mConferenceableConnections);
410
411    private int mState = Connection.STATE_NEW;
412    private DisconnectCause mDisconnectCause;
413    private boolean mRingbackRequested;
414    private boolean mConnected;
415    private int mConnectionCapabilities;
416    private int mVideoState;
417    private int mCallSubstate;
418    private VideoProvider mVideoProvider;
419    private boolean mIsVoipAudioMode;
420    private StatusHints mStatusHints;
421    private Uri mAddress;
422    private int mAddressPresentation;
423    private String mCallerDisplayName;
424    private int mCallerDisplayNamePresentation;
425    private RemoteConference mConference;
426
427    /**
428     * @hide
429     */
430    RemoteConnection(
431            String id,
432            IConnectionService connectionService,
433            ConnectionRequest request) {
434        mConnectionId = id;
435        mConnectionService = connectionService;
436        mConnected = true;
437        mState = Connection.STATE_INITIALIZING;
438    }
439
440    /**
441     * @hide
442     */
443    RemoteConnection(String callId, IConnectionService connectionService,
444            ParcelableConnection connection) {
445        mConnectionId = callId;
446        mConnectionService = connectionService;
447        mConnected = true;
448        mState = connection.getState();
449        mDisconnectCause = connection.getDisconnectCause();
450        mRingbackRequested = connection.isRingbackRequested();
451        mConnectionCapabilities = connection.getConnectionCapabilities();
452        mVideoState = connection.getVideoState();
453        mVideoProvider = new RemoteConnection.VideoProvider(connection.getVideoProvider());
454        mIsVoipAudioMode = connection.getIsVoipAudioMode();
455        mStatusHints = connection.getStatusHints();
456        mAddress = connection.getHandle();
457        mAddressPresentation = connection.getHandlePresentation();
458        mCallerDisplayName = connection.getCallerDisplayName();
459        mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation();
460        mConference = null;
461    }
462
463    /**
464     * Create a RemoteConnection which is used for failed connections. Note that using it for any
465     * "real" purpose will almost certainly fail. Callers should note the failure and act
466     * accordingly (moving on to another RemoteConnection, for example)
467     *
468     * @param disconnectCause The reason for the failed connection.
469     * @hide
470     */
471    RemoteConnection(DisconnectCause disconnectCause) {
472        mConnectionId = "NULL";
473        mConnected = false;
474        mState = Connection.STATE_DISCONNECTED;
475        mDisconnectCause = disconnectCause;
476    }
477
478    /**
479     * Adds a callback to this {@code RemoteConnection}.
480     *
481     * @param callback A {@code Callback}.
482     */
483    public void registerCallback(Callback callback) {
484        mCallbacks.add(callback);
485    }
486
487    /**
488     * Removes a callback from this {@code RemoteConnection}.
489     *
490     * @param callback A {@code Callback}.
491     */
492    public void unregisterCallback(Callback callback) {
493        if (callback != null) {
494            mCallbacks.remove(callback);
495        }
496    }
497
498    /**
499     * Obtains the state of this {@code RemoteConnection}.
500     *
501     * @return A state value, chosen from the {@code STATE_*} constants.
502     */
503    public int getState() {
504        return mState;
505    }
506
507    /**
508     * Obtains the reason why this {@code RemoteConnection} may have been disconnected.
509     *
510     * @return For a {@link Connection#STATE_DISCONNECTED} {@code RemoteConnection}, the
511     *         disconnect cause expressed as a code chosen from among those declared in
512     *         {@link DisconnectCause}.
513     */
514    public DisconnectCause getDisconnectCause() {
515        return mDisconnectCause;
516    }
517
518    /**
519     * Obtains the capabilities of this {@code RemoteConnection}.
520     *
521     * @return A bitmask of the capabilities of the {@code RemoteConnection}, as defined in
522     *         the {@code CAPABILITY_*} constants in class {@link Connection}.
523     */
524    public int getConnectionCapabilities() {
525        return mConnectionCapabilities;
526    }
527
528    /**
529     * Determines if the audio mode of this {@code RemoteConnection} is VOIP.
530     *
531     * @return {@code true} if the {@code RemoteConnection}'s current audio mode is VOIP.
532     */
533    public boolean isVoipAudioMode() {
534        return mIsVoipAudioMode;
535    }
536
537    /**
538     * Obtains status hints pertaining to this {@code RemoteConnection}.
539     *
540     * @return The current {@link StatusHints} of this {@code RemoteConnection},
541     *         or {@code null} if none have been set.
542     */
543    public StatusHints getStatusHints() {
544        return mStatusHints;
545    }
546
547    /**
548     * Obtains the address of this {@code RemoteConnection}.
549     *
550     * @return The address (e.g., phone number) to which the {@code RemoteConnection}
551     *         is currently connected.
552     */
553    public Uri getAddress() {
554        return mAddress;
555    }
556
557    /**
558     * Obtains the presentation requirements for the address of this {@code RemoteConnection}.
559     *
560     * @return The presentation requirements for the address. See
561     *         {@link TelecomManager} for valid values.
562     */
563    public int getAddressPresentation() {
564        return mAddressPresentation;
565    }
566
567    /**
568     * Obtains the display name for this {@code RemoteConnection}'s caller.
569     *
570     * @return The display name for the caller.
571     */
572    public CharSequence getCallerDisplayName() {
573        return mCallerDisplayName;
574    }
575
576    /**
577     * Obtains the presentation requirements for this {@code RemoteConnection}'s
578     * caller's display name.
579     *
580     * @return The presentation requirements for the caller display name. See
581     *         {@link TelecomManager} for valid values.
582     */
583    public int getCallerDisplayNamePresentation() {
584        return mCallerDisplayNamePresentation;
585    }
586
587    /**
588     * Obtains the video state of this {@code RemoteConnection}.
589     *
590     * @return The video state of the {@code RemoteConnection}. See {@link VideoProfile.VideoState}.
591     * @hide
592     */
593    public int getVideoState() {
594        return mVideoState;
595    }
596
597    /**
598     *
599     * @return The call substate of the {@code RemoteConnection}. See
600     * @hide
601     */
602    public int getCallSubstate() {
603        return mCallSubstate;
604    }
605
606    /**
607     * Obtains the video provider of this {@code RemoteConnection}.
608     * @return The video provider associated with this {@code RemoteConnection}.
609     * @hide
610     */
611    public final VideoProvider getVideoProvider() {
612        return mVideoProvider;
613    }
614
615    /**
616     * Determines whether this {@code RemoteConnection} is requesting ringback.
617     *
618     * @return Whether the {@code RemoteConnection} is requesting that the framework play a
619     *         ringback tone on its behalf.
620     */
621    public boolean isRingbackRequested() {
622        return false;
623    }
624
625    /**
626     * Instructs this {@code RemoteConnection} to abort.
627     */
628    public void abort() {
629        try {
630            if (mConnected) {
631                mConnectionService.abort(mConnectionId);
632            }
633        } catch (RemoteException ignored) {
634        }
635    }
636
637    /**
638     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
639     */
640    public void answer() {
641        try {
642            if (mConnected) {
643                mConnectionService.answer(mConnectionId);
644            }
645        } catch (RemoteException ignored) {
646        }
647    }
648
649    /**
650     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to answer.
651     * @param videoState The video state in which to answer the call.
652     * @hide
653     */
654    public void answer(int videoState) {
655        try {
656            if (mConnected) {
657                mConnectionService.answerVideo(mConnectionId, videoState);
658            }
659        } catch (RemoteException ignored) {
660        }
661    }
662
663    /**
664     * Instructs this {@link Connection#STATE_RINGING} {@code RemoteConnection} to reject.
665     */
666    public void reject() {
667        try {
668            if (mConnected) {
669                mConnectionService.reject(mConnectionId);
670            }
671        } catch (RemoteException ignored) {
672        }
673    }
674
675    /**
676     * Instructs this {@code RemoteConnection} to go on hold.
677     */
678    public void hold() {
679        try {
680            if (mConnected) {
681                mConnectionService.hold(mConnectionId);
682            }
683        } catch (RemoteException ignored) {
684        }
685    }
686
687    /**
688     * Instructs this {@link Connection#STATE_HOLDING} call to release from hold.
689     */
690    public void unhold() {
691        try {
692            if (mConnected) {
693                mConnectionService.unhold(mConnectionId);
694            }
695        } catch (RemoteException ignored) {
696        }
697    }
698
699    /**
700     * Instructs this {@code RemoteConnection} to disconnect.
701     */
702    public void disconnect() {
703        try {
704            if (mConnected) {
705                mConnectionService.disconnect(mConnectionId);
706            }
707        } catch (RemoteException ignored) {
708        }
709    }
710
711    /**
712     * Instructs this {@code RemoteConnection} to play a dual-tone multi-frequency signaling
713     * (DTMF) tone.
714     *
715     * Any other currently playing DTMF tone in the specified call is immediately stopped.
716     *
717     * @param digit A character representing the DTMF digit for which to play the tone. This
718     *         value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}.
719     */
720    public void playDtmfTone(char digit) {
721        try {
722            if (mConnected) {
723                mConnectionService.playDtmfTone(mConnectionId, digit);
724            }
725        } catch (RemoteException ignored) {
726        }
727    }
728
729    /**
730     * Instructs this {@code RemoteConnection} to stop any dual-tone multi-frequency signaling
731     * (DTMF) tone currently playing.
732     *
733     * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is
734     * currently playing, this method will do nothing.
735     */
736    public void stopDtmfTone() {
737        try {
738            if (mConnected) {
739                mConnectionService.stopDtmfTone(mConnectionId);
740            }
741        } catch (RemoteException ignored) {
742        }
743    }
744
745    /**
746     * Instructs this {@code RemoteConnection} to continue playing a post-dial DTMF string.
747     *
748     * A post-dial DTMF string is a string of digits following the first instance of either
749     * {@link TelecomManager#DTMF_CHARACTER_WAIT} or {@link TelecomManager#DTMF_CHARACTER_PAUSE}.
750     * These digits are immediately sent as DTMF tones to the recipient as soon as the
751     * connection is made.
752     *
753     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_PAUSE} symbol, this
754     * {@code RemoteConnection} will temporarily pause playing the tones for a pre-defined period
755     * of time.
756     *
757     * If the DTMF string contains a {@link TelecomManager#DTMF_CHARACTER_WAIT} symbol, this
758     * {@code RemoteConnection} will pause playing the tones and notify callbacks via
759     * {@link Callback#onPostDialWait(RemoteConnection, String)}. At this point, the in-call app
760     * should display to the user an indication of this state and an affordance to continue
761     * the postdial sequence. When the user decides to continue the postdial sequence, the in-call
762     * app should invoke the {@link #postDialContinue(boolean)} method.
763     *
764     * @param proceed Whether or not to continue with the post-dial sequence.
765     */
766    public void postDialContinue(boolean proceed) {
767        try {
768            if (mConnected) {
769                mConnectionService.onPostDialContinue(mConnectionId, proceed);
770            }
771        } catch (RemoteException ignored) {
772        }
773    }
774
775    /**
776     * Set the audio state of this {@code RemoteConnection}.
777     *
778     * @param state The audio state of this {@code RemoteConnection}.
779     */
780    public void setAudioState(AudioState state) {
781        try {
782            if (mConnected) {
783                mConnectionService.onAudioStateChanged(mConnectionId, state);
784            }
785        } catch (RemoteException ignored) {
786        }
787    }
788
789    /**
790     * Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
791     * successfully asked to create a conference with.
792     *
793     * @return The {@code RemoteConnection}s with which this {@code RemoteConnection} may be
794     *         merged into a {@link RemoteConference}.
795     */
796    public List<RemoteConnection> getConferenceableConnections() {
797        return mUnmodifiableconferenceableConnections;
798    }
799
800    /**
801     * Obtain the {@code RemoteConference} that this {@code RemoteConnection} may be a part
802     * of, or {@code null} if there is no such {@code RemoteConference}.
803     *
804     * @return A {@code RemoteConference} or {@code null};
805     */
806    public RemoteConference getConference() {
807        return mConference;
808    }
809
810    /** {@hide} */
811    String getId() {
812        return mConnectionId;
813    }
814
815    /** {@hide} */
816    IConnectionService getConnectionService() {
817        return mConnectionService;
818    }
819
820    /**
821     * @hide
822     */
823    void setState(int state) {
824        if (mState != state) {
825            mState = state;
826            for (Callback c: mCallbacks) {
827                c.onStateChanged(this, state);
828            }
829        }
830    }
831
832    /**
833     * @hide
834     */
835    void setDisconnected(DisconnectCause disconnectCause) {
836        if (mState != Connection.STATE_DISCONNECTED) {
837            mState = Connection.STATE_DISCONNECTED;
838            mDisconnectCause = disconnectCause;
839
840            for (Callback c : mCallbacks) {
841                c.onDisconnected(this, mDisconnectCause);
842            }
843        }
844    }
845
846    /**
847     * @hide
848     */
849    void setRingbackRequested(boolean ringback) {
850        if (mRingbackRequested != ringback) {
851            mRingbackRequested = ringback;
852            for (Callback c : mCallbacks) {
853                c.onRingbackRequested(this, ringback);
854            }
855        }
856    }
857
858    /**
859     * @hide
860     */
861    void setConnectionCapabilities(int connectionCapabilities) {
862        mConnectionCapabilities = connectionCapabilities;
863        for (Callback c : mCallbacks) {
864            c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
865        }
866    }
867
868    /**
869     * @hide
870     */
871    void setDestroyed() {
872        if (!mCallbacks.isEmpty()) {
873            // Make sure that the callbacks are notified that the call is destroyed first.
874            if (mState != Connection.STATE_DISCONNECTED) {
875                setDisconnected(
876                        new DisconnectCause(DisconnectCause.ERROR, "Connection destroyed."));
877            }
878
879            for (Callback c : mCallbacks) {
880                c.onDestroyed(this);
881            }
882            mCallbacks.clear();
883
884            mConnected = false;
885        }
886    }
887
888    /**
889     * @hide
890     */
891    void setPostDialWait(String remainingDigits) {
892        for (Callback c : mCallbacks) {
893            c.onPostDialWait(this, remainingDigits);
894        }
895    }
896
897    /**
898     * @hide
899     */
900    void onPostDialChar(char nextChar) {
901        for (Callback c : mCallbacks) {
902            c.onPostDialChar(this, nextChar);
903        }
904    }
905
906    /**
907     * @hide
908     */
909    void setVideoState(int videoState) {
910        mVideoState = videoState;
911        for (Callback c : mCallbacks) {
912            c.onVideoStateChanged(this, videoState);
913        }
914    }
915
916    /**
917     * @hide
918     */
919    void setCallSubstate(int callSubstate) {
920        mCallSubstate = callSubstate;
921        for (Callback c : mCallbacks) {
922            c.onCallSubstateChanged(this, callSubstate);
923        }
924    }
925
926    /**
927     * @hide
928     */
929    void setVideoProvider(VideoProvider videoProvider) {
930        mVideoProvider = videoProvider;
931        for (Callback c : mCallbacks) {
932            c.onVideoProviderChanged(this, videoProvider);
933        }
934    }
935
936    /** @hide */
937    void setIsVoipAudioMode(boolean isVoip) {
938        mIsVoipAudioMode = isVoip;
939        for (Callback c : mCallbacks) {
940            c.onVoipAudioChanged(this, isVoip);
941        }
942    }
943
944    /** @hide */
945    void setStatusHints(StatusHints statusHints) {
946        mStatusHints = statusHints;
947        for (Callback c : mCallbacks) {
948            c.onStatusHintsChanged(this, statusHints);
949        }
950    }
951
952    /** @hide */
953    void setAddress(Uri address, int presentation) {
954        mAddress = address;
955        mAddressPresentation = presentation;
956        for (Callback c : mCallbacks) {
957            c.onAddressChanged(this, address, presentation);
958        }
959    }
960
961    /** @hide */
962    void setCallerDisplayName(String callerDisplayName, int presentation) {
963        mCallerDisplayName = callerDisplayName;
964        mCallerDisplayNamePresentation = presentation;
965        for (Callback c : mCallbacks) {
966            c.onCallerDisplayNameChanged(this, callerDisplayName, presentation);
967        }
968    }
969
970    /** @hide */
971    void setConferenceableConnections(List<RemoteConnection> conferenceableConnections) {
972        mConferenceableConnections.clear();
973        mConferenceableConnections.addAll(conferenceableConnections);
974        for (Callback c : mCallbacks) {
975            c.onConferenceableConnectionsChanged(this, mUnmodifiableconferenceableConnections);
976        }
977    }
978
979    /** @hide */
980    void setConference(RemoteConference conference) {
981        if (mConference != conference) {
982            mConference = conference;
983            for (Callback c : mCallbacks) {
984                c.onConferenceChanged(this, conference);
985            }
986        }
987    }
988
989    /**
990     * Create a RemoteConnection represents a failure, and which will be in
991     * {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
992     * certainly result in bad things happening. Do not do this.
993     *
994     * @return a failed {@link RemoteConnection}
995     *
996     * @hide
997     */
998    public static RemoteConnection failure(DisconnectCause disconnectCause) {
999        return new RemoteConnection(disconnectCause);
1000    }
1001}
1002