diff options
author | 2022-12-13 11:08:55 -0800 | |
---|---|---|
committer | 2022-12-21 21:19:09 -0800 | |
commit | ef5a4cce8daf39417c48f2de1c709c01b4e75704 (patch) | |
tree | 6d9f377eb71a4ce35d7872edf3fe40d2638009e4 | |
parent | b377c388729121b73f990cfe01d318dc9e901541 (diff) |
Add call streaming related API.
Bug: 262412844
Test: build, cts test
Change-Id: Ib9013291ba5bac4f94fb2919fb2eccb8aa25acb8
14 files changed, 632 insertions, 7 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 5bb6652aa9e9..775ac0fddbfc 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -41086,6 +41086,7 @@ package android.telecom { field public static final int ROUTE_BLUETOOTH = 2; // 0x2 field public static final int ROUTE_EARPIECE = 1; // 0x1 field public static final int ROUTE_SPEAKER = 8; // 0x8 + field public static final int ROUTE_STREAMING = 16; // 0x10 field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4 field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5 } @@ -41097,6 +41098,7 @@ package android.telecom { method public void rejectCall(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void setActive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); method public void setInactive(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); + method public void startCallStreaming(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telecom.CallException>); } public final class CallEndpoint implements android.os.Parcelable { @@ -41132,6 +41134,8 @@ package android.telecom { public interface CallEventCallback { method public void onAnswer(int, @NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState); + method public void onCallStreamingFailed(int); + method public void onCallStreamingStarted(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onDisconnect(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onReject(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onSetActive(@NonNull java.util.function.Consumer<java.lang.Boolean>); @@ -41197,6 +41201,18 @@ package android.telecom { method public android.telecom.CallScreeningService.CallResponse.Builder setSkipNotification(boolean); } + public abstract class CallStreamingService extends android.app.Service { + ctor public CallStreamingService(); + method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); + method public void onCallStreamingStarted(@NonNull android.telecom.StreamingCall); + method public void onCallStreamingStateChanged(int); + method public void onCallStreamingStopped(); + field public static final String SERVICE_INTERFACE = "android.telecom.CallStreamingService"; + field public static final int STREAMING_FAILED_ALREADY_STREAMING = 1; // 0x1 + field public static final int STREAMING_FAILED_NO_SENDER = 2; // 0x2 + field public static final int STREAMING_FAILED_SENDER_BINDING_ERROR = 3; // 0x3 + } + public abstract class Conference extends android.telecom.Conferenceable { ctor public Conference(android.telecom.PhoneAccountHandle); method public final boolean addConnection(android.telecom.Connection); @@ -41662,6 +41678,7 @@ package android.telecom { field public static final int CAPABILITY_RTT = 4096; // 0x1000 field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800 field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4 + field public static final int CAPABILITY_SUPPORTS_CALL_STREAMING = 524288; // 0x80000 field public static final int CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS = 262144; // 0x40000 field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400 field public static final int CAPABILITY_SUPPORTS_VOICE_CALLING_INDICATIONS = 65536; // 0x10000 @@ -41851,6 +41868,22 @@ package android.telecom { field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR; } + public final class StreamingCall implements android.os.Parcelable { + ctor public StreamingCall(@NonNull android.content.ComponentName, @NonNull String, @NonNull android.net.Uri, @NonNull android.os.Bundle); + method public int describeContents(); + method @NonNull public android.net.Uri getAddress(); + method @NonNull public android.content.ComponentName getComponentName(); + method @NonNull public String getDisplayName(); + method @NonNull public android.os.Bundle getExtras(); + method public int getState(); + method public void setStreamingState(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StreamingCall> CREATOR; + field public static final int STATE_DISCONNECTED = 3; // 0x3 + field public static final int STATE_HOLDING = 2; // 0x2 + field public static final int STATE_STREAMING = 1; // 0x1 + } + public class TelecomManager { method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(); diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java index fccdf76372dd..c7cc1bd88bdf 100644 --- a/telecomm/java/android/telecom/CallAudioState.java +++ b/telecomm/java/android/telecom/CallAudioState.java @@ -27,7 +27,6 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -58,6 +57,9 @@ public final class CallAudioState implements Parcelable { /** Direct the audio stream through the device's speakerphone. */ public static final int ROUTE_SPEAKER = 0x00000008; + /** Direct the audio stream through another device. */ + public static final int ROUTE_STREAMING = 0x00000010; + /** * Direct the audio stream through the device's earpiece or wired headset if one is * connected. @@ -70,7 +72,7 @@ public final class CallAudioState implements Parcelable { * @hide **/ public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET | - ROUTE_SPEAKER; + ROUTE_SPEAKER | ROUTE_STREAMING; private final boolean isMuted; private final int route; @@ -189,7 +191,11 @@ public final class CallAudioState implements Parcelable { */ @CallAudioRoute public int getSupportedRouteMask() { - return supportedRouteMask; + if (route == ROUTE_STREAMING) { + return ROUTE_STREAMING; + } else { + return supportedRouteMask; + } } /** @@ -232,6 +238,9 @@ public final class CallAudioState implements Parcelable { if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) { listAppend(buffer, "SPEAKER"); } + if ((route & ROUTE_STREAMING) == ROUTE_STREAMING) { + listAppend(buffer, "STREAMING"); + } return buffer.toString(); } diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java index 3bda6f4db394..867bcc7f1e5a 100644 --- a/telecomm/java/android/telecom/CallControl.java +++ b/telecomm/java/android/telecom/CallControl.java @@ -191,6 +191,38 @@ public final class CallControl implements AutoCloseable { } /** + * Request start a call streaming session. On receiving valid request, telecom will bind to + * the {@link CallStreamingService} implemented by a general call streaming sender. So that the + * call streaming sender can perform streaming local device audio to another remote device and + * control the call during streaming. + * + * @param executor The {@link Executor} on which the {@link OutcomeReceiver} callback + * will be called on. + * @param callback that will be completed on the Telecom side that details success or failure + * of the requested operation. + * + * {@link OutcomeReceiver#onResult} will be called if Telecom has successfully + * rejected the incoming call. + * + * {@link OutcomeReceiver#onError} will be called if Telecom has failed to + * reject the incoming call. A {@link CallException} will be passed that + * details why the operation failed. + */ + public void startCallStreaming(@CallbackExecutor @NonNull Executor executor, + @NonNull OutcomeReceiver<Void, CallException> callback) { + if (mServerInterface != null) { + try { + mServerInterface.startCallStreaming(mCallId, + new CallControlResultReceiver("startCallStreaming", executor, callback)); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } else { + throw new IllegalStateException(INTERFACE_ERROR_MSG); + } + } + + /** * This method should be called after * {@link CallControl#disconnect(DisconnectCause, Executor, OutcomeReceiver)} or * {@link CallControl#rejectCall(Executor, OutcomeReceiver)} diff --git a/telecomm/java/android/telecom/CallEventCallback.java b/telecomm/java/android/telecom/CallEventCallback.java index a26291f7d105..fd7e10104dd6 100644 --- a/telecomm/java/android/telecom/CallEventCallback.java +++ b/telecomm/java/android/telecom/CallEventCallback.java @@ -100,4 +100,22 @@ public interface CallEventCallback { * @param callAudioState that is currently being used */ void onCallAudioStateChanged(@NonNull CallAudioState callAudioState); + + /** + * Telecom is informing the client to set the call in streaming. + * + * @param wasCompleted The {@link Consumer} to be completed. If the client can stream the + * call on their end, {@link Consumer#accept(Object)} should be called with + * {@link Boolean#TRUE}. Otherwise, {@link Consumer#accept(Object)} + * should be called with {@link Boolean#FALSE}. + */ + void onCallStreamingStarted(@NonNull Consumer<Boolean> wasCompleted); + + /** + * Telecom is informing the client user requested call streaming but the stream can't be + * started. + * + * @param reason Code to indicate the reason of this failure + */ + void onCallStreamingFailed(@CallStreamingService.StreamingFailedReason int reason); } diff --git a/telecomm/java/android/telecom/CallStreamingService.java b/telecomm/java/android/telecom/CallStreamingService.java new file mode 100644 index 000000000000..affa6b654723 --- /dev/null +++ b/telecomm/java/android/telecom/CallStreamingService.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2022 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.telecom; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SdkConstant; +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; + +import androidx.annotation.Nullable; + +import com.android.internal.telecom.ICallStreamingService; +import com.android.internal.telecom.IStreamingCallAdapter; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This service is implemented by an app that wishes to provide functionality for a general call + * streaming sender for voip calls. + * + * TODO: add doc of how to be the general streaming sender + * + */ +public abstract class CallStreamingService extends Service { + /** + * The {@link android.content.Intent} that must be declared as handled by the service. + */ + @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) + public static final String SERVICE_INTERFACE = "android.telecom.CallStreamingService"; + + private static final int MSG_SET_STREAMING_CALL_ADAPTER = 1; + private static final int MSG_CALL_STREAMING_STARTED = 2; + private static final int MSG_CALL_STREAMING_STOPPED = 3; + private static final int MSG_CALL_STREAMING_CHANGED_CHANGED = 4; + + /** Default Handler used to consolidate binder method calls onto a single thread. */ + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (mStreamingCallAdapter == null && msg.what != MSG_SET_STREAMING_CALL_ADAPTER) { + return; + } + + switch (msg.what) { + case MSG_SET_STREAMING_CALL_ADAPTER: + mStreamingCallAdapter = new StreamingCallAdapter( + (IStreamingCallAdapter) msg.obj); + break; + case MSG_CALL_STREAMING_STARTED: + mCall = (StreamingCall) msg.obj; + mCall.setAdapter(mStreamingCallAdapter); + CallStreamingService.this.onCallStreamingStarted(mCall); + break; + case MSG_CALL_STREAMING_STOPPED: + mCall = null; + mStreamingCallAdapter = null; + CallStreamingService.this.onCallStreamingStopped(); + break; + case MSG_CALL_STREAMING_CHANGED_CHANGED: + int state = (int) msg.obj; + mCall.setStreamingState(state); + CallStreamingService.this.onCallStreamingStateChanged(state); + break; + default: + break; + } + } + }; + + @Nullable + @Override + public IBinder onBind(@NonNull Intent intent) { + return new CallStreamingServiceBinder(); + } + + /** Manages the binder calls so that the implementor does not need to deal with it. */ + private final class CallStreamingServiceBinder extends ICallStreamingService.Stub { + @Override + public void setStreamingCallAdapter(IStreamingCallAdapter streamingCallAdapter) + throws RemoteException { + mHandler.obtainMessage(MSG_SET_STREAMING_CALL_ADAPTER, mStreamingCallAdapter) + .sendToTarget(); + } + + @Override + public void onCallStreamingStarted(StreamingCall call) throws RemoteException { + mHandler.obtainMessage(MSG_CALL_STREAMING_STARTED, call).sendToTarget(); + } + + @Override + public void onCallStreamingStopped() throws RemoteException { + mHandler.obtainMessage(MSG_CALL_STREAMING_STOPPED).sendToTarget(); + } + + @Override + public void onCallStreamingStateChanged(int state) throws RemoteException { + mHandler.obtainMessage(MSG_CALL_STREAMING_CHANGED_CHANGED, state).sendToTarget(); + } + } + + /** + * Call streaming request reject reason used with + * {@link CallEventCallback#onCallStreamingFailed(int)} to indicate that telecom is rejecting a + * call streaming request because there's an ongoing streaming call on this device. + */ + public static final int STREAMING_FAILED_ALREADY_STREAMING = 1; + + /** + * Call streaming request reject reason used with + * {@link CallEventCallback#onCallStreamingFailed(int)} to indicate that telecom is rejecting a + * call streaming request because telecom can't find existing general streaming sender on this + * device. + */ + public static final int STREAMING_FAILED_NO_SENDER = 2; + + /** + * Call streaming request reject reason used with + * {@link CallEventCallback#onCallStreamingFailed(int)} to indicate that telecom is rejecting a + * call streaming request because telecom can't bind to the general streaming sender app. + */ + public static final int STREAMING_FAILED_SENDER_BINDING_ERROR = 3; + + private StreamingCallAdapter mStreamingCallAdapter; + private StreamingCall mCall; + + /** + * @hide + */ + @IntDef(prefix = {"STREAMING_FAILED"}, + value = { + STREAMING_FAILED_ALREADY_STREAMING, + STREAMING_FAILED_NO_SENDER, + STREAMING_FAILED_SENDER_BINDING_ERROR + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StreamingFailedReason {}; + + /** + * Called when a {@code StreamingCall} has been added to this call streaming session. The call + * streaming sender should start to intercept the device audio using audio records and audio + * tracks from Audio frameworks. + * + * @param call a newly added {@code StreamingCall}. + */ + public void onCallStreamingStarted(@NonNull StreamingCall call) { + } + + /** + * Called when a current {@code StreamingCall} has been removed from this call streaming + * session. The call streaming sender should notify the streaming receiver that the call is + * stopped streaming and stop the device audio interception. + */ + public void onCallStreamingStopped() { + } + + /** + * Called when the streaming state of current {@code StreamingCall} changed. General streaming + * sender usually get notified of the holding/unholding from the original owner voip app of the + * call. + */ + public void onCallStreamingStateChanged(@StreamingCall.StreamingCallState int state) { + } +} diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 047ab3ae0454..b8c056e4bb10 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -437,7 +437,15 @@ public final class PhoneAccount implements Parcelable { */ public static final int CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS = 0x40000; - /* NEXT CAPABILITY: [0x80000, 0x100000, 0x200000] */ + /** + * Flag indicating that this voip app {@link PhoneAccount} supports the call streaming session + * to stream call audio to another remote device via streaming app. + * + * @see #getCapabilities + */ + public static final int CAPABILITY_SUPPORTS_CALL_STREAMING = 0x80000; + + /* NEXT CAPABILITY: [0x100000, 0x200000, 0x400000] */ /** * URI scheme for telephone number URIs. diff --git a/telecomm/java/android/telecom/StreamingCall.aidl b/telecomm/java/android/telecom/StreamingCall.aidl new file mode 100644 index 000000000000..d2866589a72a --- /dev/null +++ b/telecomm/java/android/telecom/StreamingCall.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2022, 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.telecom; + +/** + * {@hide} + */ +parcelable StreamingCall;
\ No newline at end of file diff --git a/telecomm/java/android/telecom/StreamingCall.java b/telecomm/java/android/telecom/StreamingCall.java new file mode 100644 index 000000000000..985cccc0e758 --- /dev/null +++ b/telecomm/java/android/telecom/StreamingCall.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2022 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.telecom; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.ComponentName; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Represents a voip call requested to stream to another device that the general streaming sender + * app should present to the receiver. + */ +public final class StreamingCall implements Parcelable { + /** + * The state of a {@code StreamingCall} when newly created. General streaming sender should + * continuously stream call audio to the sender device as long as the {@code StreamingCall} is + * in this state. + */ + public static final int STATE_STREAMING = 1; + + /** + * The state of a {@code StreamingCall} when in a holding state. + */ + public static final int STATE_HOLDING = 2; + + /** + * The state of a {@code StreamingCall} when it's either disconnected or pulled back to the + * original device. + */ + public static final int STATE_DISCONNECTED = 3; + + private StreamingCall(@NonNull Parcel in) { + mComponentName = in.readParcelable(ComponentName.class.getClassLoader()); + mDisplayName = in.readString16NoHelper(); + mAddress = in.readParcelable(Uri.class.getClassLoader()); + mExtras = in.readBundle(); + mState = in.readInt(); + } + + @NonNull + public static final Creator<StreamingCall> CREATOR = new Creator<>() { + @Override + public StreamingCall createFromParcel(@NonNull Parcel in) { + return new StreamingCall(in); + } + + @Override + public StreamingCall[] newArray(int size) { + return new StreamingCall[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { + dest.writeParcelable(mComponentName, flags); + dest.writeString16NoHelper(mDisplayName); + dest.writeParcelable(mAddress, flags); + dest.writeBundle(mExtras); + dest.writeInt(mState); + } + + /** + * @hide + */ + @IntDef(prefix = { "STATE_" }, + value = { + STATE_STREAMING, + STATE_HOLDING, + STATE_DISCONNECTED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface StreamingCallState {} + + private final ComponentName mComponentName; + private final String mDisplayName; + private final Uri mAddress; + private final Bundle mExtras; + @StreamingCallState + private int mState; + private StreamingCallAdapter mAdapter = null; + + public StreamingCall(@NonNull ComponentName componentName, @NonNull String displayName, + @NonNull Uri address, @NonNull Bundle extras) { + mComponentName = componentName; + mDisplayName = displayName; + mAddress = address; + mExtras = extras; + mState = STATE_STREAMING; + } + + /** + * @hide + */ + public void setAdapter(StreamingCallAdapter adapter) { + mAdapter = adapter; + } + + /** + * @return The {@link ComponentName} to identify the original voip app of this + * {@code StreamingCall}. General streaming sender app can use this to query necessary + * information (app icon etc.) in order to present notification of the streaming call on the + * receiver side. + */ + @NonNull + public ComponentName getComponentName() { + return mComponentName; + } + + /** + * @return The display name that the general streaming sender app can use this to present the + * {@code StreamingCall} to the receiver side. + */ + @NonNull + public String getDisplayName() { + return mDisplayName; + } + + /** + * @return The address (e.g., phone number) to which the {@code StreamingCall} is currently + * connected. + */ + @NonNull + public Uri getAddress() { + return mAddress; + } + + /** + * @return The state of this {@code StreamingCall}. + */ + @StreamingCallState + public int getState() { + return mState; + } + + /** + * @return The extra info the general streaming app need to stream the call from voip app or + * D2DI sdk. + */ + @NonNull + public Bundle getExtras() { + return mExtras; + } + + /** + * Sets the state of this {@code StreamingCall}. The general streaming sender app can use this + * to request holding, unholding and disconnecting this {@code StreamingCall}. + * @param state The current streaming state of the call. + */ + public void setStreamingState(@StreamingCallState int state) { + mAdapter.setStreamingState(state); + } +} diff --git a/telecomm/java/android/telecom/StreamingCallAdapter.java b/telecomm/java/android/telecom/StreamingCallAdapter.java new file mode 100644 index 000000000000..bd8727d69354 --- /dev/null +++ b/telecomm/java/android/telecom/StreamingCallAdapter.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.telecom; + +import android.os.RemoteException; + +import com.android.internal.telecom.IStreamingCallAdapter; + +/** + * Receives commands from {@link CallStreamingService} implementations which should be executed by + * Telecom. When Telecom binds to a {@link CallStreamingService}, an instance of this class is given + * to the general streaming app through which it can manipulate the streaming calls. Whe the general + * streaming app is notified of new ongoing streaming calls, it can execute + * {@link StreamingCall#setStreamingState(int)} for the ongoing streaming calls the user on the + * receiver side would like to hold, unhold and disconnect. + * + * @hide + */ +public final class StreamingCallAdapter { + private final IStreamingCallAdapter mAdapter; + + /** + * {@hide} + */ + public StreamingCallAdapter(IStreamingCallAdapter adapter) { + mAdapter = adapter; + } + + /** + * Instruct telecom to change the state of the streaming call. + * + * @param state The streaming state to set + */ + public void setStreamingState(@StreamingCall.StreamingCallState int state) { + try { + mAdapter.setStreamingState(state); + } catch (RemoteException e) { + } + } +} diff --git a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java index 682dba16dca8..16816ff0950b 100644 --- a/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java +++ b/telecomm/java/com/android/internal/telecom/ClientTransactionalServiceWrapper.java @@ -139,6 +139,7 @@ public class ClientTransactionalServiceWrapper { private static final String ON_ANSWER = "onAnswer"; private static final String ON_REJECT = "onReject"; private static final String ON_DISCONNECT = "onDisconnect"; + private static final String ON_STREAMING_STARTED = "onStreamingStarted"; private void handleCallEventCallback(String action, String callId, int code, ResultReceiver ackResultReceiver) { @@ -174,6 +175,9 @@ public class ClientTransactionalServiceWrapper { case ON_ANSWER: callback.onAnswer(code, outcomeReceiverWrapper); break; + case ON_STREAMING_STARTED: + callback.onCallStreamingStarted(outcomeReceiverWrapper); + break; } }); } catch (Exception e) { @@ -249,9 +253,7 @@ public class ClientTransactionalServiceWrapper { if (call != null) { CallEventCallback callback = call.getCallEventCallback(); Executor executor = call.getExecutor(); - executor.execute(() -> { - callback.onCallAudioStateChanged(callAudioState); - }); + executor.execute(() -> callback.onCallAudioStateChanged(callAudioState)); } } @@ -259,5 +261,23 @@ public class ClientTransactionalServiceWrapper { public void removeCallFromTransactionalServiceWrapper(String callId) { untrackCall(callId); } + + @Override + public void onCallStreamingStarted(String callId, ResultReceiver resultReceiver) { + handleCallEventCallback(ON_STREAMING_STARTED, callId, 0, resultReceiver); + } + + @Override + public void onCallStreamingFailed(String callId, int reason) { + Log.i(TAG, TextUtils.formatSimple("onCallAudioStateChanged: callId=[%s], reason=[%s]", + callId, reason)); + // lookup the callEventCallback associated with the particular call + TransactionalCall call = mCallIdToTransactionalCall.get(callId); + if (call != null) { + CallEventCallback callback = call.getCallEventCallback(); + Executor executor = call.getExecutor(); + executor.execute(() -> callback.onCallStreamingFailed(reason)); + } + } }; } diff --git a/telecomm/java/com/android/internal/telecom/ICallControl.aidl b/telecomm/java/com/android/internal/telecom/ICallControl.aidl index bf68c5e3b98a..dc0aeace0655 100644 --- a/telecomm/java/com/android/internal/telecom/ICallControl.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallControl.aidl @@ -28,4 +28,5 @@ oneway interface ICallControl { void setInactive(String callId, in ResultReceiver callback); void disconnect(String callId, in DisconnectCause disconnectCause, in ResultReceiver callback); void rejectCall(String callId, in ResultReceiver callback); + void startCallStreaming(String callId, in ResultReceiver callback); }
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl index 7f5825aba8aa..c45ef97fa004 100644 --- a/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallEventCallback.aidl @@ -35,6 +35,9 @@ oneway interface ICallEventCallback { void onReject(String callId, in ResultReceiver callback); void onDisconnect(String callId, in ResultReceiver callback); void onCallAudioStateChanged(String callId, in CallAudioState callAudioState); + // Streaming related. Client registered call streaming capabilities should override + void onCallStreamingStarted(String callId, in ResultReceiver callback); + void onCallStreamingFailed(String callId, int reason); // hidden methods that help with cleanup void removeCallFromTransactionalServiceWrapper(String callId); }
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/ICallStreamingService.aidl b/telecomm/java/com/android/internal/telecom/ICallStreamingService.aidl new file mode 100644 index 000000000000..6d53fd25bb45 --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/ICallStreamingService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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 com.android.internal.telecom; + +import android.telecom.StreamingCall; + +import com.android.internal.telecom.IStreamingCallAdapter; + +/** + * Internal remote interface for call streaming services. + * + * @see android.telecom.CallStreamingService + * + * {@hide} + */ +oneway interface ICallStreamingService { + void setStreamingCallAdapter(in IStreamingCallAdapter streamingCallAdapter); + void onCallStreamingStarted(in StreamingCall call); + void onCallStreamingStopped(); + void onCallStreamingStateChanged(int state); +}
\ No newline at end of file diff --git a/telecomm/java/com/android/internal/telecom/IStreamingCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IStreamingCallAdapter.aidl new file mode 100644 index 000000000000..51424a66d0df --- /dev/null +++ b/telecomm/java/com/android/internal/telecom/IStreamingCallAdapter.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 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 com.android.internal.telecom; + +/** + * Internal remote callback interface for call streaming services. + * + * @see android.telecom.StreamingCallAdapter + * + * {@hide} + */ +oneway interface IStreamingCallAdapter { + void setStreamingState(int state); +}
\ No newline at end of file |