diff --git a/api/current.txt b/api/current.txt index 77cc1f62a8fc2..897367f230a90 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27850,6 +27850,62 @@ package android.system { package android.telecomm { + public final class Call { + method public void addListener(android.telecomm.Call.Listener); + method public void answer(); + method public void conference(); + method public void disconnect(); + method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider(); + method public java.util.List getCannedTextResponses(); + method public java.util.List getChildren(); + method public android.telecomm.Call.Details getDetails(); + method public android.telecomm.Call getParent(); + method public java.lang.String getRemainingPostDialSequence(); + method public int getState(); + method public void hold(); + method public void phoneAccountClicked(); + method public void playDtmfTone(char); + method public void postDialContinue(boolean); + method public void reject(boolean, java.lang.String); + method public void removeListener(android.telecomm.Call.Listener); + method public void splitFromConference(); + method public void stopDtmfTone(); + method public void swapWithBackgroundCall(); + method public void unhold(); + field public static final int STATE_ACTIVE = 4; // 0x4 + field public static final int STATE_DIALING = 1; // 0x1 + field public static final int STATE_DISCONNECTED = 7; // 0x7 + field public static final int STATE_HOLDING = 3; // 0x3 + field public static final int STATE_NEW = 0; // 0x0 + field public static final int STATE_RINGING = 2; // 0x2 + } + + public static class Call.Details { + method public android.telecomm.PhoneAccount getAccount(); + method public java.lang.String getCallerDisplayName(); + method public int getCallerDisplayNamePresentation(); + method public int getCapabilities(); + method public long getConnectTimeMillis(); + method public int getDisconnectCauseCode(); + method public java.lang.String getDisconnectCauseMsg(); + method public android.telecomm.GatewayInfo getGatewayInfo(); + method public android.net.Uri getHandle(); + method public int getHandlePresentation(); + } + + public static abstract class Call.Listener { + ctor public Call.Listener(); + method public void onCallDestroyed(android.telecomm.Call); + method public void onCallVideoProviderChanged(android.telecomm.Call, android.telecomm.RemoteCallVideoProvider); + method public void onCannedTextResponsesLoaded(android.telecomm.Call, java.util.List); + method public void onChildrenChanged(android.telecomm.Call, java.util.List); + method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details); + method public void onParentChanged(android.telecomm.Call, android.telecomm.Call); + method public void onPostDial(android.telecomm.Call, java.lang.String); + method public void onPostDialWait(android.telecomm.Call, java.lang.String); + method public void onStateChanged(android.telecomm.Call, int); + } + public final class CallAudioState implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -27936,8 +27992,6 @@ package android.telecomm { enum_constant public static final android.telecomm.CallState DISCONNECTED; enum_constant public static final android.telecomm.CallState NEW; enum_constant public static final android.telecomm.CallState ON_HOLD; - enum_constant public static final android.telecomm.CallState POST_DIAL; - enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT; enum_constant public static final android.telecomm.CallState RINGING; } @@ -28112,17 +28166,29 @@ package android.telecomm { field public static final android.os.Parcelable.Creator CREATOR; } - public abstract class InCallService extends android.app.Service { + public abstract class InCallService { ctor protected InCallService(); - method protected abstract void addCall(android.telecomm.InCallCall); - method protected abstract void bringToForeground(boolean); - method protected final android.telecomm.InCallAdapter getAdapter(); - method protected void onAdapterAttached(android.telecomm.InCallAdapter); - method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState); - method public final android.os.IBinder onBind(android.content.Intent); - method protected abstract void setPostDial(java.lang.String, java.lang.String); - method protected abstract void setPostDialWait(java.lang.String, java.lang.String); - method protected abstract void updateCall(android.telecomm.InCallCall); + method public final android.os.IBinder getBinder(); + method public android.telecomm.Phone getPhone(); + method public void onPhoneCreated(android.telecomm.Phone); + method public void onPhoneDestroyed(android.telecomm.Phone); + } + + public final class Phone { + method public final void addListener(android.telecomm.Phone.Listener); + method public final android.telecomm.CallAudioState getAudioState(); + method public final java.util.List getCalls(); + method public final void removeListener(android.telecomm.Phone.Listener); + method public final void setAudioRoute(int); + method public final void setMuted(boolean); + } + + public static abstract class Phone.Listener { + ctor public Phone.Listener(); + method public void onAudioStateChanged(android.telecomm.Phone, android.telecomm.CallAudioState); + method public void onBringToForeground(android.telecomm.Phone, boolean); + method public void onCallAdded(android.telecomm.Phone, android.telecomm.Call); + method public void onCallRemoved(android.telecomm.Phone, android.telecomm.Call); } public final class PhoneAccount implements android.os.Parcelable { @@ -28151,18 +28217,17 @@ package android.telecomm { method public void updatePeerDimensions(int, int) throws android.os.RemoteException; } - public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient { - method public void binderDied(); - method public void requestCallDataUsage() throws android.os.RemoteException; - method public void requestCameraCapabilities() throws android.os.RemoteException; - method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException; + public class RemoteCallVideoProvider { + method public void requestCallDataUsage(); + method public void requestCameraCapabilities(); + method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile); + method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile); + method public void setCallVideoClient(android.telecomm.CallVideoClient); method public void setCamera(java.lang.String) throws android.os.RemoteException; - method public void setDeviceOrientation(int) throws android.os.RemoteException; - method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException; - method public void setPauseImage(java.lang.String) throws android.os.RemoteException; - method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException; + method public void setDeviceOrientation(int); + method public void setDisplaySurface(android.view.Surface); + method public void setPauseImage(java.lang.String); + method public void setPreviewSurface(android.view.Surface); method public void setZoom(float) throws android.os.RemoteException; } diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java new file mode 100644 index 0000000000000..ba7e253fc824f --- /dev/null +++ b/telecomm/java/android/telecomm/Call.java @@ -0,0 +1,710 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.net.Uri; +import android.os.RemoteException; +import android.telephony.DisconnectCause; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Represents an ongoing phone call that the in-call app should present to the user. + */ +public final class Call { + /** + * The state of a {@code Call} when newly created. + */ + public static final int STATE_NEW = 0; + + /** + * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected. + */ + public static final int STATE_DIALING = 1; + + /** + * The state of an incoming {@code Call} when ringing locally, but not yet connected. + */ + public static final int STATE_RINGING = 2; + + /** + * The state of a {@code Call} when in a holding state. + */ + public static final int STATE_HOLDING = 3; + + /** + * The state of a {@code Call} when actively supporting conversation. + */ + public static final int STATE_ACTIVE = 4; + + /** + * The state of a {@code Call} when no further voice or other communication is being + * transmitted, the remote side has been or will inevitably be informed that the {@code Call} + * is no longer active, and the local data transport has or inevitably will release resources + * associated with this {@code Call}. + */ + public static final int STATE_DISCONNECTED = 7; + + public static class Details { + private final Uri mHandle; + private final int mHandlePresentation; + private final String mCallerDisplayName; + private final int mCallerDisplayNamePresentation; + private final PhoneAccount mAccount; + private final int mCapabilities; + private final int mDisconnectCauseCode; + private final String mDisconnectCauseMsg; + private final long mConnectTimeMillis; + private final GatewayInfo mGatewayInfo; + + /** + * @return The handle (e.g., phone number) to which the {@code Call} is currently + * connected. + */ + public Uri getHandle() { + return mHandle; + } + + /** + * @return The presentation requirements for the handle. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getHandlePresentation() { + return mHandlePresentation; + } + + /** + * @return The display name for the caller. + */ + public String getCallerDisplayName() { + return mCallerDisplayName; + } + + /** + * @return The presentation requirements for the caller display name. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getCallerDisplayNamePresentation() { + return mCallerDisplayNamePresentation; + } + + /** + * @return The {@code PhoneAccount} whereby the {@code Call} is currently being routed. + */ + public PhoneAccount getAccount() { + return mAccount; + } + + /** + * @return A bitmask of the capabilities of the {@code Call}, as defined in + * {@link CallCapabilities}. + */ + public int getCapabilities() { + return mCapabilities; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed + * as a code chosen from among those declared in {@link DisconnectCause}. + */ + public int getDisconnectCauseCode() { + return mDisconnectCauseCode; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for + * disconnection expressed as a free text message. + */ + public String getDisconnectCauseMsg() { + return mDisconnectCauseMsg; + } + + /** + * @return The time the {@code Call} has been connected. This information is updated + * periodically, but user interfaces should not rely on this to display any "call time + * clock". + */ + public long getConnectTimeMillis() { + return mConnectTimeMillis; + } + + /** + * @return Information about any calling gateway the {@code Call} may be using. + */ + public GatewayInfo getGatewayInfo() { + return mGatewayInfo; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Details) { + Details d = (Details) o; + return + Objects.equals(mHandle, d.mHandle) && + Objects.equals(mHandlePresentation, d.mHandlePresentation) && + Objects.equals(mCallerDisplayName, d.mCallerDisplayName) && + Objects.equals(mCallerDisplayNamePresentation, + d.mCallerDisplayNamePresentation) && + Objects.equals(mAccount, d.mAccount) && + Objects.equals(mCapabilities, d.mCapabilities) && + Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) && + Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) && + Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && + Objects.equals(mGatewayInfo, d.mGatewayInfo); + } + return false; + } + + @Override + public int hashCode() { + return + Objects.hashCode(mHandle) + + Objects.hashCode(mHandlePresentation) + + Objects.hashCode(mCallerDisplayName) + + Objects.hashCode(mCallerDisplayNamePresentation) + + Objects.hashCode(mAccount) + + Objects.hashCode(mCapabilities) + + Objects.hashCode(mDisconnectCauseCode) + + Objects.hashCode(mDisconnectCauseMsg) + + Objects.hashCode(mConnectTimeMillis) + + Objects.hashCode(mGatewayInfo); + } + + /** {@hide} */ + public Details( + Uri handle, + int handlePresentation, + String callerDisplayName, + int callerDisplayNamePresentation, + PhoneAccount account, + int capabilities, + int disconnectCauseCode, + String disconnectCauseMsg, + long connectTimeMillis, + GatewayInfo gatewayInfo) { + mHandle = handle; + mHandlePresentation = handlePresentation; + mCallerDisplayName = callerDisplayName; + mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mAccount = account; + mCapabilities = capabilities; + mDisconnectCauseCode = disconnectCauseCode; + mDisconnectCauseMsg = disconnectCauseMsg; + mConnectTimeMillis = connectTimeMillis; + mGatewayInfo = gatewayInfo; + } + } + + public static abstract class Listener { + /** + * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. + * + * TODO(ihab): Provide previous state also? + * + * @param call The {@code Call} invoking this method. + * @param state The new state of the {@code Call}. + */ + public void onStateChanged(Call call, int state) {} + + /** + * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}. + * + * @param call The {@code Call} invoking this method. + * @param parent The new parent of the {@code Call}. + */ + public void onParentChanged(Call call, Call parent) {} + + /** + * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}. + * + * @param call The {@code Call} invoking this method. + * @param children The new children of the {@code Call}. + */ + public void onChildrenChanged(Call call, List children) {} + + /** + * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}. + * + * @param call The {@code Call} invoking this method. + * @param details A {@code Details} object describing the {@code Call}. + */ + public void onDetailsChanged(Call call, Details details) {} + + /** + * Invoked when the text messages that can be used as responses to the incoming + * {@code Call} are loaded from the relevant database. + * See {@link #getCannedTextResponses()}. + * + * @param call The {@code Call} invoking this method. + * @param cannedTextResponses The text messages useable as responses. + */ + public void onCannedTextResponsesLoaded(Call call, List cannedTextResponses) {} + + /** + * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals + * that were embedded into the outgoing number. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDial(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause + * character. This causes the post-dial signals to stop pending user confirmation. An + * implementation should present this choice to the user and invoke + * {@link #postDialContinue(boolean)} when the user makes the choice. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDialWait(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the {@code RemoteCallVideoProvider} of the {@code Call} has changed. + * + * @param call The {@code Call} invoking this method. + * @param callVideoProvider The {@code RemoteCallVideoProvider} associated with the + * {@code Call}. + */ + + public void onCallVideoProviderChanged(Call call, + RemoteCallVideoProvider callVideoProvider) {} + + /** + * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning + * up their UI for the {@code Call} in response to state transitions. Specifically, + * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of + * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather, + * clients should wait for this method to be invoked. + * + * @param call The {@code Call} being destroyed. + */ + public void onCallDestroyed(Call call) {} + } + + private final Phone mPhone; + private final String mTelecommCallId; + private final InCallAdapter mInCallAdapter; + private Call mParent = null; + private int mState; + private final List mChildren = new ArrayList<>(); + private final List mUnmodifiableChildren = Collections.unmodifiableList(mChildren); + private List mCannedTextResponses = null; + private String mRemainingPostDialSequence; + private RemoteCallVideoProvider mCallVideoProvider; + private Details mDetails; + private final List mListeners = new ArrayList<>(); + + /** + * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any. + * + * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence + * remaining or this {@code Call} is not in a post-dial state. + */ + public String getRemainingPostDialSequence() { + return mRemainingPostDialSequence; + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to answer. + */ + public void answer() { + mInCallAdapter.answerCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to reject. + * + * @param rejectWithMessage Whether to reject with a text message. + * @param textMessage An optional text message with which to respond. + */ + public void reject(boolean rejectWithMessage, String textMessage) { + mInCallAdapter.rejectCall(mTelecommCallId, rejectWithMessage, textMessage); + } + + /** + * Instructs this {@code Call} to disconnect. + */ + public void disconnect() { + mInCallAdapter.disconnectCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to go on hold. + */ + public void hold() { + mInCallAdapter.holdCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_HOLDING} call to release from hold. + */ + public void unhold() { + mInCallAdapter.unholdCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone. + * + * Any other currently playing DTMF tone in the specified call is immediately stopped. + * + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public void playDtmfTone(char digit) { + mInCallAdapter.playDtmfTone(mTelecommCallId, digit); + } + + /** + * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone + * currently playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is + * currently playing, this method will do nothing. + */ + public void stopDtmfTone() { + mInCallAdapter.stopDtmfTone(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to continue playing a post-dial DTMF string. + * + * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, + * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. + * While these tones are playing, this {@code Call} will notify listeners via + * {@link Listener#onPostDial(Call, String)}. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, this + * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, this + * {@code Call} will pause playing the tones and notify listeners via + * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app + * should display to the user an indication of this state and an affordance to continue + * the postdial sequence. When the user decides to continue the postdial sequence, the in-call + * app should invoke the {@link #postDialContinue(boolean)} method. + * + * @param proceed Whether or not to continue with the post-dial sequence. + */ + public void postDialContinue(boolean proceed) { + mInCallAdapter.postDialContinue(mTelecommCallId, proceed); + } + + /** + * Notifies this {@code Call} that the phone account user interface element was touched. + * + * TODO(ihab): Figure out if and how we can generalize this + */ + public void phoneAccountClicked() { + mInCallAdapter.phoneAccountClicked(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to enter a conference. + */ + public void conference() { + mInCallAdapter.conference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to split from any conference call with which it may be + * connected. + */ + public void splitFromConference() { + mInCallAdapter.splitFromConference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to swap itself with an existing background call, if one + * such call exists. + */ + public void swapWithBackgroundCall() { + mInCallAdapter.swapWithBackgroundCall(mTelecommCallId); + } + + /** + * Obtains the parent of this {@code Call} in a conference, if any. + * + * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a + * child of any conference {@code Call}s. + */ + public Call getParent() { + return mParent; + } + + /** + * Obtains the children of this conference {@code Call}, if any. + * + * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty + * {@code List} otherwise. + */ + public List getChildren() { + return mUnmodifiableChildren; + } + + /** + * Obtains the state of this {@code Call}. + * + * @return A state value, chosen from the {@code STATE_*} constants. + */ + public int getState() { + return mState; + } + + /** + * Obtains a list of canned, pre-configured message responses to present to the user as + * ways of rejecting this {@code Call} using via a text message. + * + * @see #reject(boolean, String) + * + * @return A list of canned text message responses. + */ + public List getCannedTextResponses() { + return mCannedTextResponses; + } + + /** + * Obtains an object that can be used to display video from this {@code Call}. + * + * @return An {@code ICallVideoProvider}. + */ + public RemoteCallVideoProvider getCallVideoProvider() { + return mCallVideoProvider; + } + + /** + * Obtains an object containing call details. + * + * @return A {@link Details} object. Depending on the state of the {@code Call}, the + * result may be {@code null}. + */ + public Details getDetails() { + return mDetails; + } + + /** + * Adds a listener to this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** {@hide} */ + Call(Phone phone, String telecommCallId, InCallAdapter inCallAdapter) { + mPhone = phone; + mTelecommCallId = telecommCallId; + mInCallAdapter = inCallAdapter; + mState = STATE_NEW; + } + + /** {@hide} */ + final String internalGetCallId() { + return mTelecommCallId; + } + + /** {@hide} */ + final void internalUpdate(InCallCall inCallCall) { + // First, we update the internal state as far as possible before firing any updates. + + Details details = new Details( + inCallCall.getHandle(), + inCallCall.getHandlePresentation(), + inCallCall.getCallerDisplayName(), + inCallCall.getCallerDisplayNamePresentation(), + inCallCall.getAccount(), + inCallCall.getCapabilities(), + inCallCall.getDisconnectCauseCode(), + inCallCall.getDisconnectCauseMsg(), + inCallCall.getConnectTimeMillis(), + inCallCall.getGatewayInfo()); + boolean detailsChanged = !Objects.equals(mDetails, details); + if (detailsChanged) { + mDetails = details; + } + + boolean cannedTextResponsesChanged = false; + if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null + && !inCallCall.getCannedSmsResponses().isEmpty()) { + mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses()); + } + + boolean callVideoProviderChanged = false; + try { + callVideoProviderChanged = + !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider()); + if (callVideoProviderChanged) { + mCallVideoProvider = inCallCall.getCallVideoProvider(); + } + } catch (RemoteException e) { + } + + int state = stateFromInCallCallState(inCallCall.getState()); + boolean stateChanged = mState != state; + if (stateChanged) { + mState = state; + } + + if (inCallCall.getParentCallId() != null) { + mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId()); + } + + mChildren.clear(); + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + mChildren.add(mPhone.internalGetCallByTelecommId( + inCallCall.getChildCallIds().get(i))); + } + } + + // Now we fire updates, ensuring that any client who listens to any of these notifications + // gets the most up-to-date state. + + if (stateChanged) { + fireStateChanged(mState); + } + if (detailsChanged) { + fireDetailsChanged(mDetails); + } + if (cannedTextResponsesChanged) { + fireCannedTextResponsesLoaded(mCannedTextResponses); + } + if (callVideoProviderChanged) { + fireCallVideoProviderChanged(mCallVideoProvider); + } + + // If we have transitioned to DISCONNECTED, that means we need to notify clients and + // remove ourselves from the Phone. Note that we do this after completing all state updates + // so a client can cleanly transition all their UI to the state appropriate for a + // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. + if (mState == STATE_DISCONNECTED) { + fireCallDestroyed(); + mPhone.internalRemoveCall(this); + } + } + + /** {@hide} */ + final void internalSetPostDial(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDial(mRemainingPostDialSequence); + } + + /** {@hide} */ + final void internalSetPostDialWait(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDialWait(mRemainingPostDialSequence); + } + + private void fireStateChanged(int newState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onStateChanged(this, newState); + } + } + + private void fireParentChanged(Call newParent) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onParentChanged(this, newParent); + } + } + + private void fireChildrenChanged(List children) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onChildrenChanged(this, children); + } + } + + private void fireDetailsChanged(Details details) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onDetailsChanged(this, details); + } + } + + private void fireCannedTextResponsesLoaded(List cannedTextResponses) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses); + } + } + + private void fireCallVideoProviderChanged(RemoteCallVideoProvider callVideoProvider) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallVideoProviderChanged(this, callVideoProvider); + } + } + + private void firePostDial(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDial(this, remainingPostDialSequence); + } + } + + private void firePostDialWait(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDialWait(this, remainingPostDialSequence); + } + } + + private void fireCallDestroyed() { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallDestroyed(this); + } + } + + private int stateFromInCallCallState(CallState inCallCallState) { + switch (inCallCallState) { + case NEW: + return STATE_NEW; + case DIALING: + return STATE_DIALING; + case RINGING: + return STATE_RINGING; + case ACTIVE: + return STATE_ACTIVE; + case ON_HOLD: + return STATE_HOLDING; + case DISCONNECTED: + return STATE_DISCONNECTED; + case ABORTED: + return STATE_DISCONNECTED; + default: + Log.wtf(this, "Unrecognized CallState %s", inCallCallState); + return STATE_NEW; + } + } +} diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java index 152c2023e7fd0..a464da5631056 100644 --- a/telecomm/java/android/telecomm/CallState.java +++ b/telecomm/java/android/telecomm/CallState.java @@ -47,22 +47,6 @@ public enum CallState { */ RINGING, - /** - * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending - * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal - * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user - * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or - * {@link #DISCONNECTED}. - */ - POST_DIAL, - - /** - * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user - * confirmation before the remaining digits can be sent. Normal transitions are to - * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence. - */ - POST_DIAL_WAIT, - /** * Indicates that a call is currently connected to another party and a communication channel is * open between them. The normal transition to this state is by the user answering a diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java index 76b28fad2d078..fb970dc14a35f 100644 --- a/telecomm/java/android/telecomm/CallVideoClient.java +++ b/telecomm/java/android/telecomm/CallVideoClient.java @@ -241,6 +241,7 @@ public abstract class CallVideoClient { * * @param callCameraCapabilities The changed camera capabilities. */ - public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities); + public abstract void onHandleCameraCapabilitiesChange( + CallCameraCapabilities callCameraCapabilities); } diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index d8293a5cdbe45..66cf1df98b1c6 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -24,7 +24,7 @@ import com.android.internal.telecomm.IInCallAdapter; * Receives commands from {@link InCallService} implementations which should be executed by * Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When - * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the + * the in-call service is notified of new calls, it can use the * given call IDs to execute commands such as {@link #answerCall} for incoming calls or * {@link #disconnectCall} for active calls the user would like to end. Some commands are only * appropriate for calls in certain states; please consult each method for such limitations. @@ -167,16 +167,15 @@ public final class InCallAdapter { * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. * While these tones are playing, Telecomm will notify the {@link InCallService} that the call - * is in the {@link InCallService#setPostDial(String,String)} state. + * is in the post dial state. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm * will temporarily pause playing the tones for a pre-defined period of time. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm * will pause playing the tones and notify the {@link InCallService} that the call is in the - * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue - * the postdial sequence, the {@link InCallService} should invoke the - * {@link #postDialContinue(String,boolean)} method. + * post dial wait state. When the user decides to continue the postdial sequence, the + * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method. * * @param callId The unique ID of the call for which postdial string playing should continue. * @param proceed Whether or not to continue with the post-dial sequence. diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java index 31291fba7ec73..028b6e49e34e4 100644 --- a/telecomm/java/android/telecomm/InCallService.java +++ b/telecomm/java/android/telecomm/InCallService.java @@ -16,8 +16,6 @@ package android.telecomm; -import android.app.Service; -import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -31,11 +29,10 @@ import com.android.internal.telecomm.IInCallService; * This service is implemented by any app that wishes to provide the user-interface for managing * phone calls. Telecomm binds to this service while there exists a live (active or incoming) * call, and uses it to notify the in-call app of any live and and recently disconnected calls. - * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better - * defined. + * * TODO(santoscordon): What happens if two or more apps on a given device implement this interface? */ -public abstract class InCallService extends Service { +public abstract class InCallService { private static final int MSG_SET_IN_CALL_ADAPTER = 1; private static final int MSG_ADD_CALL = 2; private static final int MSG_UPDATE_CALL = 3; @@ -50,21 +47,21 @@ public abstract class InCallService extends Service { public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_IN_CALL_ADAPTER: - mAdapter = new InCallAdapter((IInCallAdapter) msg.obj); - onAdapterAttached(mAdapter); + mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj)); + onPhoneCreated(mPhone); break; case MSG_ADD_CALL: - addCall((InCallCall) msg.obj); + mPhone.internalAddCall((InCallCall) msg.obj); break; case MSG_UPDATE_CALL: - updateCall((InCallCall) msg.obj); + mPhone.internalUpdateCall((InCallCall) msg.obj); break; - case MSG_SET_POST_DIAL: { + case MSG_SET_POST_DIAL: { SomeArgs args = (SomeArgs) msg.obj; try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDial(callId, remaining); + mPhone.internalSetPostDial(callId, remaining); } finally { args.recycle(); } @@ -75,17 +72,17 @@ public abstract class InCallService extends Service { try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDialWait(callId, remaining); + mPhone.internalSetPostDialWait(callId, remaining); } finally { args.recycle(); } break; } case MSG_ON_AUDIO_STATE_CHANGED: - onAudioStateChanged((CallAudioState) msg.obj); + mPhone.internalAudioStateChanged((CallAudioState) msg.obj); break; case MSG_BRING_TO_FOREGROUND: - bringToForeground(msg.arg1 == 1); + mPhone.internalBringToForeground(msg.arg1 == 1); break; default: break; @@ -142,85 +139,41 @@ public abstract class InCallService extends Service { } } - private final InCallServiceBinder mBinder; + private Phone mPhone; - private InCallAdapter mAdapter; + protected InCallService() {} - protected InCallService() { - mBinder = new InCallServiceBinder(); - } - - @Override - public final IBinder onBind(Intent intent) { - return mBinder; + public final IBinder getBinder() { + return new InCallServiceBinder(); } /** - * @return The attached {@link InCallAdapter} if attached, or null otherwise. + * Obtain the {@code Phone} associated with this {@code InCallService}. + * + * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null} + * if the {@code InCallService} is not in a state where it has an associated {@code Phone}. */ - protected final InCallAdapter getAdapter() { - return mAdapter; + public Phone getPhone() { + return mPhone; } /** - * Lifecycle callback which is called when this {@link InCallService} has been attached - * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use. + * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience + * to start displaying in-call information to the user. Each instance of {@code InCallService} + * will have only one {@code Phone}, and this method will be called exactly once in the + * lifetime of the {@code InCallService}. * - * @param adapter The adapter now attached to this in-call service. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected void onAdapterAttached(InCallAdapter adapter) { - } + public void onPhoneCreated(Phone phone) { } /** - * Indicates to the in-call app that a new call has been created and an appropriate - * user-interface should be built and shown to notify the user. + * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience + * to stop displaying in-call information to the user. This method will be called exactly once + * in the lifetime of the {@code InCallService}, and it will always be called after a previous + * call to {@link #onPhoneCreated(Phone)}. * - * @param call Information about the new call. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected abstract void addCall(InCallCall call); - - /** - * Call when information about a call has changed. - * - * @param call Information about the new call. - */ - protected abstract void updateCall(InCallCall call); - - /** - * Indicates to the in-call app that the specified call is active but in a "post-dial" state - * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended - * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when - * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when - * the post-dial tones are completed. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDial(String callId, String remaining); - - /** - * Indicates to the in-call app that the specified call was in the - * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the - * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)} - * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app - * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDialWait(String callId, String remaining); - - /** - * Called when the audio state changes. - * - * @param audioState The new {@link CallAudioState}. - */ - protected abstract void onAudioStateChanged(CallAudioState audioState); - - /** - * Brings the in-call screen to the foreground. - * - * @param showDialpad If true, put up the dialpad when the screen is shown. - */ - protected abstract void bringToForeground(boolean showDialpad); + public void onPhoneDestroyed(Phone phone) { } } diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java new file mode 100644 index 0000000000000..9c97b45891b45 --- /dev/null +++ b/telecomm/java/android/telecomm/Phone.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecomm; + +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A unified virtual device providing a means of voice (and other) communication on a device. + */ +public final class Phone { + + public abstract static class Listener { + /** + * Called when the audio state changes. + * + * @param phone The {@code Phone} calling this method. + * @param audioState The new {@link CallAudioState}. + */ + public void onAudioStateChanged(Phone phone, CallAudioState audioState) { } + + /** + * Called to bring the in-call screen to the foreground. The in-call experience should + * respond immediately by coming to the foreground to inform the user of the state of + * ongoing {@code Call}s. + * + * @param phone The {@code Phone} calling this method. + * @param showDialpad If true, put up the dialpad when the screen is shown. + */ + public void onBringToForeground(Phone phone, boolean showDialpad) { } + + /** + * Called when a {@code Call} has been added to this in-call session. The in-call user + * experience should add necessary state listeners to the specified {@code Call} and + * immediately start to show the user information about the existence + * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will + * include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly added {@code Call}. + */ + public void onCallAdded(Phone phone, Call call) { } + + /** + * Called when a {@code Call} has been removed from this in-call session. The in-call user + * experience should remove any state listeners from the specified {@code Call} and + * immediately stop displaying any information about this {@code Call}. + * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly removed {@code Call}. + */ + public void onCallRemoved(Phone phone, Call call) { } + } + + // A Map allows us to track each Call by its Telecomm-specified call ID + private final Map mCallByTelecommCallId = new ArrayMap<>(); + + // A List allows us to keep the Calls in a stable iteration order so that casually developed + // user interface components do not incur any spurious jank + private final List mCalls = new ArrayList<>(); + + // An unmodifiable view of the above List can be safely shared with subclass implementations + private final List mUnmodifiableCalls = Collections.unmodifiableList(mCalls); + + private final InCallAdapter mInCallAdapter; + + private CallAudioState mAudioState; + + private final List mListeners = new ArrayList<>(); + + /** {@hide} */ + Phone(InCallAdapter adapter) { + mInCallAdapter = adapter; + } + + /** {@hide} */ + final void internalAddCall(InCallCall inCallCall) { + Call call = new Call(this, inCallCall.getId(), mInCallAdapter); + mCallByTelecommCallId.put(inCallCall.getId(), call); + mCalls.add(call); + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + fireCallAdded(call); + } + + /** {@hide} */ + final void internalRemoveCall(Call call) { + mCallByTelecommCallId.remove(call.internalGetCallId()); + mCalls.remove(call); + fireCallRemoved(call); + } + + /** {@hide} */ + final void internalUpdateCall(InCallCall inCallCall) { + Call call = mCallByTelecommCallId.get(inCallCall.getId()); + if (call != null) { + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + } + } + + /** {@hide} */ + final void internalSetPostDial(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDial(remaining); + } + } + + /** {@hide} */ + final void internalSetPostDialWait(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDialWait(remaining); + } + } + + /** {@hide} */ + final void internalAudioStateChanged(CallAudioState callAudioState) { + if (!Objects.equals(mAudioState, callAudioState)) { + mAudioState = callAudioState; + fireAudioStateChanged(callAudioState); + } + } + + /** {@hide} */ + final Call internalGetCallByTelecommId(String telecommId) { + return mCallByTelecommCallId.get(telecommId); + } + + /** {@hide} */ + final void internalBringToForeground(boolean showDialpad) { + fireBringToForeground(showDialpad); + } + + /** + * Adds a listener to this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** + * Obtains the current list of {@code Call}s to be displayed by this in-call experience. + * + * @return A list of the relevant {@code Call}s. + */ + public final List getCalls() { + return mUnmodifiableCalls; + } + + /** + * Sets the microphone mute state. When this request is honored, there will be change to + * the {@link #getAudioState()}. + * + * @param state {@code true} if the microphone should be muted; {@code false} otherwise. + */ + public final void setMuted(boolean state) { + mInCallAdapter.mute(state); + } + + /** + * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will + * be change to the {@link #getAudioState()}. + * + * @param route The audio route to use. + */ + public final void setAudioRoute(int route) { + mInCallAdapter.setAudioRoute(route); + } + + /** + * Obtains the current phone call audio state of the {@code Phone}. + * + * @return An object encapsulating the audio state. + */ + public final CallAudioState getAudioState() { + return mAudioState; + } + + private void fireCallAdded(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallAdded(this, call); + } + } + + private void fireCallRemoved(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallRemoved(this, call); + } + } + + private void fireAudioStateChanged(CallAudioState audioState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onAudioStateChanged(this, audioState); + } + } + + private void fireBringToForeground(boolean showDialpad) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onBringToForeground(this, showDialpad); + } + } + + private void checkCallTree(InCallCall inCallCall) { + if (inCallCall.getParentCallId() != null && + !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) { + Log.wtf(this, "InCallCall %s has nonexistent parent %s", + inCallCall.getId(), inCallCall.getParentCallId()); + } + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) { + Log.wtf(this, "InCallCall %s has nonexistent child %s", + inCallCall.getId(), inCallCall.getChildCallIds().get(i)); + } + } + } + } +} diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java index 856d321dc4084..a49076a2fd0c8 100644 --- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java +++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java @@ -20,63 +20,92 @@ import android.os.IBinder; import android.os.RemoteException; import android.view.Surface; -import com.android.internal.telecomm.ICallVideoClient; import com.android.internal.telecomm.ICallVideoProvider; -public class RemoteCallVideoProvider implements IBinder.DeathRecipient { +public class RemoteCallVideoProvider { private final ICallVideoProvider mCallVideoProvider; + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mCallVideoProvider.asBinder().unlinkToDeath(this, 0); + } + }; + + /** {@hide} */ RemoteCallVideoProvider(ICallVideoProvider callVideoProvider) throws RemoteException { mCallVideoProvider = callVideoProvider; - mCallVideoProvider.asBinder().linkToDeath(this, 0); + mCallVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); } - @Override - public void binderDied() { - mCallVideoProvider.asBinder().unlinkToDeath(this, 0); - } - - public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException { - mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + public void setCallVideoClient(CallVideoClient callVideoClient) { + try { + mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + } catch (RemoteException e) { + } } public void setCamera(String cameraId) throws RemoteException { mCallVideoProvider.setCamera(cameraId); } - public void setPreviewSurface(Surface surface) throws RemoteException { - mCallVideoProvider.setPreviewSurface(surface); + public void setPreviewSurface(Surface surface) { + try { + mCallVideoProvider.setPreviewSurface(surface); + } catch (RemoteException e) { + } } - public void setDisplaySurface(Surface surface) throws RemoteException { - mCallVideoProvider.setDisplaySurface(surface); + public void setDisplaySurface(Surface surface) { + try { + mCallVideoProvider.setDisplaySurface(surface); + } catch (RemoteException e) { + } } - public void setDeviceOrientation(int rotation) throws RemoteException { - mCallVideoProvider.setDeviceOrientation(rotation); + public void setDeviceOrientation(int rotation) { + try { + mCallVideoProvider.setDeviceOrientation(rotation); + } catch (RemoteException e) { + } } public void setZoom(float value) throws RemoteException { mCallVideoProvider.setZoom(value); } - public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyRequest(requestProfile); + public void sendSessionModifyRequest(VideoCallProfile requestProfile) { + try { + mCallVideoProvider.sendSessionModifyRequest(requestProfile); + } catch (RemoteException e) { + } } - public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyResponse(responseProfile); + public void sendSessionModifyResponse(VideoCallProfile responseProfile) { + try { + mCallVideoProvider.sendSessionModifyResponse(responseProfile); + } catch (RemoteException e) { + } } - public void requestCameraCapabilities() throws RemoteException { - mCallVideoProvider.requestCameraCapabilities(); + public void requestCameraCapabilities() { + try { + mCallVideoProvider.requestCameraCapabilities(); + } catch (RemoteException e) { + } } - public void requestCallDataUsage() throws RemoteException { - mCallVideoProvider.requestCallDataUsage(); + public void requestCallDataUsage() { + try { + mCallVideoProvider.requestCallDataUsage(); + } catch (RemoteException e) { + } } - public void setPauseImage(String uri) throws RemoteException { - mCallVideoProvider.setPauseImage(uri); + public void setPauseImage(String uri) { + try { + mCallVideoProvider.setPauseImage(uri); + } catch (RemoteException e) { + } } } \ No newline at end of file