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