Loading core/api/current.txt +33 −0 Original line number Diff line number Diff line Loading @@ -41089,6 +41089,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 } Loading @@ -41100,6 +41101,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 { Loading Loading @@ -41135,6 +41137,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>); Loading Loading @@ -41200,6 +41204,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); Loading Loading @@ -41665,6 +41681,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 Loading Loading @@ -41854,6 +41871,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(); telecomm/java/android/telecom/CallAudioState.java +12 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; Loading Loading @@ -189,8 +191,12 @@ public final class CallAudioState implements Parcelable { */ @CallAudioRoute public int getSupportedRouteMask() { if (route == ROUTE_STREAMING) { return ROUTE_STREAMING; } else { return supportedRouteMask; } } /** * @return The {@link BluetoothDevice} through which audio is being routed. Loading Loading @@ -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(); } Loading telecomm/java/android/telecom/CallControl.java +32 −0 Original line number Diff line number Diff line Loading @@ -190,6 +190,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 Loading telecomm/java/android/telecom/CallEventCallback.java +18 −0 Original line number Diff line number Diff line Loading @@ -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); } telecomm/java/android/telecom/CallStreamingService.java 0 → 100644 +184 −0 Original line number Diff line number Diff line /* * 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) { } } Loading
core/api/current.txt +33 −0 Original line number Diff line number Diff line Loading @@ -41089,6 +41089,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 } Loading @@ -41100,6 +41101,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 { Loading Loading @@ -41135,6 +41137,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>); Loading Loading @@ -41200,6 +41204,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); Loading Loading @@ -41665,6 +41681,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 Loading Loading @@ -41854,6 +41871,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();
telecomm/java/android/telecom/CallAudioState.java +12 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; Loading Loading @@ -189,8 +191,12 @@ public final class CallAudioState implements Parcelable { */ @CallAudioRoute public int getSupportedRouteMask() { if (route == ROUTE_STREAMING) { return ROUTE_STREAMING; } else { return supportedRouteMask; } } /** * @return The {@link BluetoothDevice} through which audio is being routed. Loading Loading @@ -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(); } Loading
telecomm/java/android/telecom/CallControl.java +32 −0 Original line number Diff line number Diff line Loading @@ -190,6 +190,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 Loading
telecomm/java/android/telecom/CallEventCallback.java +18 −0 Original line number Diff line number Diff line Loading @@ -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); }
telecomm/java/android/telecom/CallStreamingService.java 0 → 100644 +184 −0 Original line number Diff line number Diff line /* * 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) { } }