Refactor CallScreeningService's internal structure

Use a single AIDL method and perform logic in Telecom instead of slicing
up the call response on the client side.

Also pass through the call response to the connection service.

Bug: 179412110
Test: atest ConnectionServiceTest CallScreeningServiceTest
Change-Id: I878c0ce34142da104dc0e2795487b03a6bdacb5f
This commit is contained in:
Hall Liu
2021-02-04 13:09:45 -08:00
parent 191b3b9db5
commit 5efe997539
8 changed files with 215 additions and 38 deletions

View File

@@ -10664,7 +10664,7 @@ package android.telecom {
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
method public final void resetConnectionTime();
method public void setCallDirection(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10841,7 +10841,7 @@ package android.telecom {
}
public final class RemoteConnection {
method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean, @Nullable android.telecom.CallScreeningService.CallResponse, boolean);
method @Deprecated public void setAudioState(android.telecom.AudioState);
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (C) 2021 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 CallScreeningService.ParcelableCallResponse;

View File

@@ -30,12 +30,16 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
import java.util.Objects;
/**
* This service can be implemented by the default dialer (see
* {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
@@ -132,7 +136,10 @@ public abstract class CallScreeningService extends Service {
.createFromParcelableCall((ParcelableCall) args.arg2);
onScreenCall(callDetails);
if (callDetails.getCallDirection() == Call.Details.DIRECTION_OUTGOING) {
mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
mCallScreeningAdapter.onScreeningResponse(
callDetails.getTelecomCallId(),
new ComponentName(getPackageName(), getClass().getName()),
null);
}
} catch (RemoteException e) {
Log.w(this, "Exception when screening call: " + e);
@@ -157,6 +164,106 @@ public abstract class CallScreeningService extends Service {
private ICallScreeningAdapter mCallScreeningAdapter;
/**
* Parcelable version of {@link CallResponse} used to do IPC.
* @hide
*/
public static class ParcelableCallResponse implements Parcelable {
private final boolean mShouldDisallowCall;
private final boolean mShouldRejectCall;
private final boolean mShouldSilenceCall;
private final boolean mShouldSkipCallLog;
private final boolean mShouldSkipNotification;
private final boolean mShouldScreenCallViaAudioProcessing;
private ParcelableCallResponse(
boolean shouldDisallowCall,
boolean shouldRejectCall,
boolean shouldSilenceCall,
boolean shouldSkipCallLog,
boolean shouldSkipNotification,
boolean shouldScreenCallViaAudioProcessing) {
mShouldDisallowCall = shouldDisallowCall;
mShouldRejectCall = shouldRejectCall;
mShouldSilenceCall = shouldSilenceCall;
mShouldSkipCallLog = shouldSkipCallLog;
mShouldSkipNotification = shouldSkipNotification;
mShouldScreenCallViaAudioProcessing = shouldScreenCallViaAudioProcessing;
}
protected ParcelableCallResponse(Parcel in) {
mShouldDisallowCall = in.readBoolean();
mShouldRejectCall = in.readBoolean();
mShouldSilenceCall = in.readBoolean();
mShouldSkipCallLog = in.readBoolean();
mShouldSkipNotification = in.readBoolean();
mShouldScreenCallViaAudioProcessing = in.readBoolean();
}
public CallResponse toCallResponse() {
return new CallResponse.Builder()
.setDisallowCall(mShouldDisallowCall)
.setRejectCall(mShouldRejectCall)
.setSilenceCall(mShouldSilenceCall)
.setSkipCallLog(mShouldSkipCallLog)
.setSkipNotification(mShouldSkipNotification)
.setShouldScreenCallViaAudioProcessing(mShouldScreenCallViaAudioProcessing)
.build();
}
public boolean shouldDisallowCall() {
return mShouldDisallowCall;
}
public boolean shouldRejectCall() {
return mShouldRejectCall;
}
public boolean shouldSilenceCall() {
return mShouldSilenceCall;
}
public boolean shouldSkipCallLog() {
return mShouldSkipCallLog;
}
public boolean shouldSkipNotification() {
return mShouldSkipNotification;
}
public boolean shouldScreenCallViaAudioProcessing() {
return mShouldScreenCallViaAudioProcessing;
}
public static final Creator<ParcelableCallResponse> CREATOR =
new Creator<ParcelableCallResponse>() {
@Override
public ParcelableCallResponse createFromParcel(Parcel in) {
return new ParcelableCallResponse(in);
}
@Override
public ParcelableCallResponse[] newArray(int size) {
return new ParcelableCallResponse[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeBoolean(mShouldDisallowCall);
dest.writeBoolean(mShouldRejectCall);
dest.writeBoolean(mShouldSilenceCall);
dest.writeBoolean(mShouldSkipCallLog);
dest.writeBoolean(mShouldSkipNotification);
dest.writeBoolean(mShouldScreenCallViaAudioProcessing);
}
}
/*
* Information about how to respond to an incoming call.
*/
@@ -237,6 +344,38 @@ public abstract class CallScreeningService extends Service {
return mShouldScreenCallViaAudioProcessing;
}
/** @hide */
public ParcelableCallResponse toParcelable() {
return new ParcelableCallResponse(
mShouldDisallowCall,
mShouldRejectCall,
mShouldSilenceCall,
mShouldSkipCallLog,
mShouldSkipNotification,
mShouldScreenCallViaAudioProcessing
);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CallResponse that = (CallResponse) o;
return mShouldDisallowCall == that.mShouldDisallowCall &&
mShouldRejectCall == that.mShouldRejectCall &&
mShouldSilenceCall == that.mShouldSilenceCall &&
mShouldSkipCallLog == that.mShouldSkipCallLog &&
mShouldSkipNotification == that.mShouldSkipNotification &&
mShouldScreenCallViaAudioProcessing == that.mShouldScreenCallViaAudioProcessing;
}
@Override
public int hashCode() {
return Objects.hash(mShouldDisallowCall, mShouldRejectCall, mShouldSilenceCall,
mShouldSkipCallLog, mShouldSkipNotification,
mShouldScreenCallViaAudioProcessing);
}
public static class Builder {
private boolean mShouldDisallowCall;
private boolean mShouldRejectCall;
@@ -423,21 +562,12 @@ public abstract class CallScreeningService extends Service {
public final void respondToCall(@NonNull Call.Details callDetails,
@NonNull CallResponse response) {
try {
if (response.getDisallowCall()) {
mCallScreeningAdapter.disallowCall(
callDetails.getTelecomCallId(),
response.getRejectCall(),
!response.getSkipCallLog(),
!response.getSkipNotification(),
new ComponentName(getPackageName(), getClass().getName()));
} else if (response.getSilenceCall()) {
mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId());
} else if (response.getShouldScreenCallViaAudioProcessing()) {
mCallScreeningAdapter.screenCallFurther(callDetails.getTelecomCallId());
} else {
mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId());
}
mCallScreeningAdapter.onScreeningResponse(
callDetails.getTelecomCallId(),
new ComponentName(getPackageName(), getClass().getName()),
response.toParcelable());
} catch (RemoteException e) {
Log.e(this, e, "Got remote exception when returning response");
}
}
}

View File

@@ -3390,11 +3390,20 @@ public abstract class Connection extends Conferenceable {
* {@code true}, {@link #onDisconnect()} will be called soon after
* this is called.
* @param isInContacts Indicates whether the caller is in the user's contacts list.
* @param callScreeningResponse The response that was returned from the
* {@link CallScreeningService} that handled this call. If no
* response was received from a call screening service,
* this will be {@code null}.
* @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
* system dialer. If {@code callScreeningResponse} is
* {@code null}, this will be {@code false}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { }
public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
@Nullable CallScreeningService.CallResponse callScreeningResponse,
boolean isResponseFromSystemDialer) { }
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.

View File

@@ -759,6 +759,8 @@ public abstract class ConnectionService extends Service {
@Override
public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
CallScreeningService.ParcelableCallResponse callScreeningResponse,
boolean isResponseFromSystemDialer,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
try {
@@ -766,7 +768,9 @@ public abstract class ConnectionService extends Service {
args.arg1 = callId;
args.arg2 = isBlocked;
args.arg3 = isInContacts;
args.arg4 = Log.createSubsession();
args.arg4 = callScreeningResponse;
args.arg5 = isResponseFromSystemDialer;
args.arg6 = Log.createSubsession();
mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
} finally {
Log.endSession();
@@ -1437,12 +1441,16 @@ public abstract class ConnectionService extends Service {
case MSG_ON_CALL_FILTERING_COMPLETED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
Log.continueSession((Session) args.arg4,
Log.continueSession((Session) args.arg6,
SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
String callId = (String) args.arg1;
boolean isBlocked = (boolean) args.arg2;
boolean isInContacts = (boolean) args.arg3;
onCallFilteringCompleted(callId, isBlocked, isInContacts);
CallScreeningService.ParcelableCallResponse callScreeningResponse =
(CallScreeningService.ParcelableCallResponse) args.arg4;
boolean isResponseFromSystemDialer = (boolean) args.arg5;
onCallFilteringCompleted(callId, isBlocked, isInContacts,
callScreeningResponse, isResponseFromSystemDialer);
} finally {
args.recycle();
Log.endSession();
@@ -2458,11 +2466,16 @@ public abstract class ConnectionService extends Service {
}
}
private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
Log.i(this, "onCallFilteringCompleted(%b, %b)", isBlocked, isInContacts);
private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
CallScreeningService.ParcelableCallResponse callScreeningResponse,
boolean isResponseFromSystemDialer) {
Log.i(this, "onCallFilteringCompleted(%s, %b, %b, %s, %b)", callId,
isBlocked, isInContacts, callScreeningResponse, isResponseFromSystemDialer);
Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
if (connection != null) {
connection.onCallFilteringCompleted(isBlocked, isInContacts);
connection.onCallFilteringCompleted(isBlocked, isInContacts,
callScreeningResponse == null ? null : callScreeningResponse.toCallResponse(),
isResponseFromSystemDialer);
}
}

View File

@@ -1204,15 +1204,25 @@ public final class RemoteConnection {
* the results of a contacts lookup for the remote party.
* @param isBlocked Whether call filtering indicates that the call should be blocked
* @param isInContacts Whether the remote party is in the user's contacts
* @param callScreeningResponse The response that was returned from the
* {@link CallScreeningService} that handled this call. If no
* response was received from a call screening service,
* this will be {@code null}.
* @param isResponseFromSystemDialer Whether {@code callScreeningResponse} was sent from the
* system dialer. If {@code callScreeningResponse} is
* {@code null}, this will be {@code false}.
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.READ_CONTACTS)
public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts,
@Nullable CallScreeningService.CallResponse callScreeningResponse,
boolean isResponseFromSystemDialer) {
Log.startSession("RC.oCFC", getActiveOwnerInfo());
try {
if (mConnected) {
mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
callScreeningResponse.toParcelable(), isResponseFromSystemDialer,
null /*Session.Info*/);
}
} catch (RemoteException ignored) {

View File

@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.content.ComponentName;
import android.telecom.CallScreeningService;
/**
* Internal remote callback interface for call screening services.
@@ -26,16 +27,6 @@ import android.content.ComponentName;
* {@hide}
*/
oneway interface ICallScreeningAdapter {
void allowCall(String callId);
void silenceCall(String callId);
void screenCallFurther(String callId);
void disallowCall(
String callId,
boolean shouldReject,
boolean shouldAddToCallLog,
boolean shouldShowNotification,
in ComponentName componentName);
void onScreeningResponse(String callId, in ComponentName componentName,
in CallScreeningService.ParcelableCallResponse response);
}

View File

@@ -20,6 +20,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
import android.telecom.CallScreeningService;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
import android.telecom.PhoneAccountHandle;
@@ -119,7 +120,8 @@ oneway interface IConnectionService {
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
in Session.Info sessionInfo);
in CallScreeningService.ParcelableCallResponse callScreeningResponse,
boolean isResponseFromSystemDialer, in Session.Info sessionInfo);
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);