Add APIs to support background call screening
Add Telecom APIs and AIDL code.
Test: CTS
Bug: 140317205
Change-Id: I99970f5ce12442f307a656db76cb26f5101e6c7f
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 1822cee..5e71416 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.net.Uri;
import android.os.Build;
@@ -119,6 +120,20 @@
public static final int STATE_PULLING_CALL = 11;
/**
+ * The state of a call that is active with the network, but the audio from the call is
+ * being intercepted by an app on the local device. Telecom does not hold audio focus in this
+ * state, and the call will be invisible to the user except for a persistent notification.
+ */
+ public static final int STATE_AUDIO_PROCESSING = 12;
+
+ /**
+ * The state of a call that is being presented to the user after being in
+ * {@link #STATE_AUDIO_PROCESSING}. The call is still active with the network in this case, and
+ * Telecom will hold audio focus and play a ringtone if appropriate.
+ */
+ public static final int STATE_SIMULATED_RINGING = 13;
+
+ /**
* The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
* extras. Used to pass the phone accounts to display on the front end to the user in order to
* select phone accounts to (for example) place a call.
@@ -1479,6 +1494,49 @@
}
/**
+ * Instructs Telecom to put the call into the background audio processing state.
+ *
+ * This method can be called either when the call is in {@link #STATE_RINGING} or
+ * {@link #STATE_ACTIVE}. After Telecom acknowledges the request by setting the call's state to
+ * {@link #STATE_AUDIO_PROCESSING}, your app may setup the audio paths with the audio stack in
+ * order to capture and play audio on the call stream.
+ *
+ * This method can only be called by the default dialer app.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //@RequiresPermission(android.Manifest.permission.BACKGROUND_CALL_AUDIO)
+ public void enterBackgroundAudioProcessing() {
+ if (mState != STATE_ACTIVE && mState != STATE_RINGING) {
+ throw new IllegalStateException("Call must be active or ringing");
+ }
+ mInCallAdapter.enterBackgroundAudioProcessing(mTelecomCallId);
+ }
+
+ /**
+ * Instructs Telecom to come out of the background audio processing state requested by
+ * {@link #enterBackgroundAudioProcessing()} or from the call screening service.
+ *
+ * This method can only be called when the call is in {@link #STATE_AUDIO_PROCESSING}.
+ *
+ * @param shouldRing If true, Telecom will put the call into the
+ * {@link #STATE_SIMULATED_RINGING} state and notify other apps that there is
+ * a ringing call. Otherwise, the call will go into {@link #STATE_ACTIVE}
+ * immediately.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ //@RequiresPermission(android.Manifest.permission.BACKGROUND_CALL_AUDIO)
+ public void exitBackgroundAudioProcessing(boolean shouldRing) {
+ if (mState != STATE_AUDIO_PROCESSING) {
+ throw new IllegalStateException("Call must in the audio processing state");
+ }
+ mInCallAdapter.exitBackgroundAudioProcessing(mTelecomCallId, shouldRing);
+ }
+
+ /**
* 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.
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index 0e0406d..9016b3e8 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -19,6 +19,8 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -140,23 +142,30 @@
private final boolean mShouldSilenceCall;
private final boolean mShouldSkipCallLog;
private final boolean mShouldSkipNotification;
+ private final boolean mShouldScreenCallFurther;
private CallResponse(
boolean shouldDisallowCall,
boolean shouldRejectCall,
boolean shouldSilenceCall,
boolean shouldSkipCallLog,
- boolean shouldSkipNotification) {
+ boolean shouldSkipNotification,
+ boolean shouldScreenCallFurther) {
if (!shouldDisallowCall
&& (shouldRejectCall || shouldSkipCallLog || shouldSkipNotification)) {
throw new IllegalStateException("Invalid response state for allowed call.");
}
+ if (shouldDisallowCall && shouldScreenCallFurther) {
+ throw new IllegalStateException("Invalid response state for allowed call.");
+ }
+
mShouldDisallowCall = shouldDisallowCall;
mShouldRejectCall = shouldRejectCall;
mShouldSkipCallLog = shouldSkipCallLog;
mShouldSkipNotification = shouldSkipNotification;
mShouldSilenceCall = shouldSilenceCall;
+ mShouldScreenCallFurther = shouldScreenCallFurther;
}
/*
@@ -195,12 +204,22 @@
return mShouldSkipNotification;
}
+ /**
+ * @return Whether we should enter the {@link Call#STATE_AUDIO_PROCESSING} state to allow
+ * for further screening of the call.
+ * @hide
+ */
+ public boolean getShouldScreenCallFurther() {
+ return mShouldScreenCallFurther;
+ }
+
public static class Builder {
private boolean mShouldDisallowCall;
private boolean mShouldRejectCall;
private boolean mShouldSilenceCall;
private boolean mShouldSkipCallLog;
private boolean mShouldSkipNotification;
+ private boolean mShouldScreenCallFurther;
/**
* Sets whether the incoming call should be blocked.
@@ -256,13 +275,32 @@
return this;
}
+ /**
+ * Sets whether to request background audio processing so that the in-call service can
+ * screen the call further. If set to {@code true}, {@link #setDisallowCall} should be
+ * called with {@code false}, and all other parameters in this builder will be ignored.
+ *
+ * This request will only be honored if the {@link CallScreeningService} shares the same
+ * uid as the default dialer app. Otherwise, the call will go through as usual.
+ *
+ * @param shouldScreenCallFurther Whether to request further call screening.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public Builder setShouldScreenCallFurther(boolean shouldScreenCallFurther) {
+ mShouldScreenCallFurther = shouldScreenCallFurther;
+ return this;
+ }
+
public CallResponse build() {
return new CallResponse(
mShouldDisallowCall,
mShouldRejectCall,
mShouldSilenceCall,
mShouldSkipCallLog,
- mShouldSkipNotification);
+ mShouldSkipNotification,
+ mShouldScreenCallFurther);
}
}
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 8678e33..2612468 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,8 +16,8 @@
package android.telecom;
-import android.net.Uri;
import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
@@ -149,6 +149,26 @@
}
/**
+ * @see Call#enterBackgroundAudioProcessing()
+ */
+ public void enterBackgroundAudioProcessing(String callId) {
+ try {
+ mAdapter.enterBackgroundAudioProcessing(callId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * @see Call#exitBackgroundAudioProcessing(boolean)
+ */
+ public void exitBackgroundAudioProcessing(String callId, boolean shouldRing) {
+ try {
+ mAdapter.exitBackgroundAudioProcessing(callId, shouldRing);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Request audio routing to a specific bluetooth device. Calling this method may result in
* the device routing audio to a different bluetooth device than the one specified. A list of
* available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}