Call Screening and Caller ID changes
- Fix bug where call screening service doesn't log calls.
Ensure dialer and 3p app screened calls are logged.
- Add ability to bind to a call screening service for outgoing
calls in order for it to provide outgoing caller id.
- Update test call screening service to support incoming and
outgoing calls.
- Refactor some call screening service filter code to make better
reuse of binding logic.
Bug: 63966743
Test: Manual, CTS
Change-Id: I86d440fb45c08da01ce2159441d2b9efc53eb27c
diff --git a/src/com/android/server/telecom/CallScreeningServiceHelper.java b/src/com/android/server/telecom/CallScreeningServiceHelper.java
new file mode 100644
index 0000000..b2f76c4
--- /dev/null
+++ b/src/com/android/server/telecom/CallScreeningServiceHelper.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2018 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.server.telecom;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telecom.CallIdentification;
+import android.telecom.CallScreeningService;
+import android.telecom.Log;
+import android.telecom.Logging.Session;
+import android.text.TextUtils;
+
+import com.android.internal.telecom.ICallScreeningAdapter;
+import com.android.internal.telecom.ICallScreeningService;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Helper class for performing operations with {@link CallScreeningService}s.
+ */
+public class CallScreeningServiceHelper {
+ private static final String TAG = CallScreeningServiceHelper.class.getSimpleName();
+
+ /**
+ * Abstracts away dependency on the {@link PackageManager} required to fetch the label for an
+ * app.
+ */
+ public interface AppLabelProxy {
+ String getAppLabel(String packageName);
+ }
+
+ /**
+ * Implementation of {@link CallScreeningService} adapter AIDL; provides a means for responses
+ * from the call screening service to be handled.
+ */
+ private class CallScreeningAdapter extends ICallScreeningAdapter.Stub {
+ @Override
+ public void allowCall(String s) throws RemoteException {
+ // no-op; we don't allow this on outgoing calls.
+ }
+
+ @Override
+ public void disallowCall(String s, boolean b, boolean b1, boolean b2,
+ ComponentName componentName) throws RemoteException {
+ // no-op; we don't allow this on outgoing calls.
+ }
+
+ @Override
+ public void provideCallIdentification(String callId, CallIdentification callIdentification)
+ throws RemoteException {
+ Log.startSession("CSA.pCI");
+ long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mTelecomLock) {
+ if (mCall != null && mCall.getId().equals(callId)) {
+ Log.i(TAG, "provideCallIdentification - got call ID");
+ callIdentification.setCallScreeningAppName(mAppLabelProxy.getAppLabel(
+ mPackageName));
+ callIdentification.setCallScreeningPackageName(mPackageName);
+ mFuture.complete(callIdentification);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ Log.endSession();
+ }
+ mFuture.complete(null);
+ }
+ }
+
+ private final ParcelableCallUtils.Converter mParcelableCallUtilsConverter;
+ private final TelecomSystem.SyncRoot mTelecomLock;
+ private final Call mCall;
+ private final UserHandle mUserHandle;
+ private final Context mContext;
+ private final AppLabelProxy mAppLabelProxy;
+ private final Session mLoggingSession;
+ private CompletableFuture<CallIdentification> mFuture;
+ private String mPackageName;
+
+ public CallScreeningServiceHelper(Context context, TelecomSystem.SyncRoot telecomLock,
+ String packageName, ParcelableCallUtils.Converter converter,
+ UserHandle userHandle, Call call, AppLabelProxy appLabelProxy) {
+ mContext = context;
+ mTelecomLock = telecomLock;
+ mParcelableCallUtilsConverter = converter;
+ mCall = call;
+ mUserHandle = userHandle;
+ mPackageName = packageName;
+ mAppLabelProxy = appLabelProxy;
+ mLoggingSession = Log.createSubsession();
+ }
+
+ /**
+ * Builds a {@link CompletableFuture} which performs a bind to a {@link CallScreeningService}
+ * @return
+ */
+ public CompletableFuture<CallIdentification> process() {
+ Log.d(this, "process");
+ return bindAndGetCallIdentification();
+ }
+
+ public CompletableFuture<CallIdentification> bindAndGetCallIdentification() {
+ Log.d(this, "bindAndGetCallIdentification");
+ if (mPackageName == null) {
+ return CompletableFuture.completedFuture(null);
+ }
+
+ mFuture = new CompletableFuture<>();
+
+ ServiceConnection serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ ICallScreeningService screeningService =
+ ICallScreeningService.Stub.asInterface(service);
+ Log.continueSession(mLoggingSession, "CSSH.oSC");
+ try {
+ try {
+ screeningService.screenCall(new CallScreeningAdapter(),
+ mParcelableCallUtilsConverter.toParcelableCallForScreening(mCall));
+ } catch (RemoteException e) {
+ Log.w(CallScreeningServiceHelper.this,
+ "Cancelling call id due to remote exception");
+ mFuture.complete(null);
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // No locking needed -- CompletableFuture only lets one thread call complete.
+ Log.continueSession(mLoggingSession, "CSSH.oSD");
+ try {
+ if (!mFuture.isDone()) {
+ Log.w(CallScreeningServiceHelper.this,
+ "Cancelling outgoing call screen due to service disconnect.");
+ }
+ mFuture.complete(null);
+ } finally {
+ Log.endSession();
+ }
+ }
+ };
+
+ if (!bindCallScreeningService(mContext, mUserHandle, mPackageName, serviceConnection)) {
+ Log.i(this, "bindAndGetCallIdentification - bind failed");
+ Log.addEvent(mCall, LogUtils.Events.BIND_SCREENING, mPackageName);
+ mFuture.complete(null);
+ }
+
+ // Set up a timeout so that we're not waiting forever for the caller ID information.
+ Handler handler = new Handler();
+ handler.postDelayed(() -> {
+ // No locking needed -- CompletableFuture only lets one thread call complete.
+ Log.continueSession(mLoggingSession, "CSSH.timeout");
+ try {
+ if (!mFuture.isDone()) {
+ Log.w(TAG, "Cancelling call id process due to timeout");
+ }
+ mFuture.complete(null);
+ } finally {
+ Log.endSession();
+ }
+ },
+ Timeouts.getCallScreeningTimeoutMillis(mContext.getContentResolver()));
+ return mFuture;
+ }
+
+ /**
+ * Binds to a {@link CallScreeningService}.
+ * @param context The current context.
+ * @param userHandle User to bind as.
+ * @param packageName Package name of the {@link CallScreeningService}.
+ * @param serviceConnection The {@link ServiceConnection} to be notified of binding.
+ * @return {@code true} if binding succeeds, {@code false} otherwise.
+ */
+ public static boolean bindCallScreeningService(Context context, UserHandle userHandle,
+ String packageName, ServiceConnection serviceConnection) {
+ if (TextUtils.isEmpty(packageName)) {
+ Log.i(TAG, "PackageName is empty. Not performing call screening.");
+ return false;
+ }
+
+ Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
+ .setPackage(packageName);
+ List<ResolveInfo> entries = context.getPackageManager().queryIntentServicesAsUser(
+ intent, 0, userHandle.getIdentifier());
+ if (entries.isEmpty()) {
+ Log.i(TAG, packageName + " has no call screening service defined.");
+ return false;
+ }
+
+ ResolveInfo entry = entries.get(0);
+ if (entry.serviceInfo == null) {
+ Log.w(TAG, packageName + " call screening service has invalid service info");
+ return false;
+ }
+
+ if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
+ Manifest.permission.BIND_SCREENING_SERVICE)) {
+ Log.w(TAG, "CallScreeningService must require BIND_SCREENING_SERVICE permission: " +
+ entry.serviceInfo.packageName);
+ return false;
+ }
+
+ ComponentName componentName =
+ new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
+ intent.setComponent(componentName);
+ if (context.bindServiceAsUser(
+ intent,
+ serviceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ UserHandle.CURRENT)) {
+ Log.d(TAG, "bindService, found service, waiting for it to connect");
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/com/android/server/telecom/CallerInfoLookupHelper.java b/src/com/android/server/telecom/CallerInfoLookupHelper.java
index 1e0745f..a919921 100644
--- a/src/com/android/server/telecom/CallerInfoLookupHelper.java
+++ b/src/com/android/server/telecom/CallerInfoLookupHelper.java
@@ -27,6 +27,7 @@
import android.telecom.Logging.Runnable;
import android.telecom.Logging.Session;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CallerInfo;
@@ -37,6 +38,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
public class CallerInfoLookupHelper {
public interface OnQueryCompleteListener {
@@ -77,6 +79,47 @@
mLock = lock;
}
+ /**
+ * Generates a CompletableFuture which performs a contacts lookup asynchronously. The future
+ * returns a {@link Pair} containing the original handle which is being looked up and any
+ * {@link CallerInfo} which was found.
+ * @param handle
+ * @return {@link CompletableFuture} to perform the contacts lookup.
+ */
+ public CompletableFuture<Pair<Uri, CallerInfo>> startLookup(final Uri handle) {
+ // Create the returned future and
+ final CompletableFuture<Pair<Uri, CallerInfo>> callerInfoFuture = new CompletableFuture<>();
+
+ final String number = handle.getSchemeSpecificPart();
+ if (TextUtils.isEmpty(number)) {
+ // Nothing to do here, just finish.
+ Log.d(CallerInfoLookupHelper.this, "onCallerInfoQueryComplete - no number; end early");
+ callerInfoFuture.complete(new Pair<>(handle, null));
+ return callerInfoFuture;
+ }
+
+ // Setup a query complete listener which will get the results of the contacts lookup.
+ OnQueryCompleteListener listener = new OnQueryCompleteListener() {
+ @Override
+ public void onCallerInfoQueryComplete(Uri handle, CallerInfo info) {
+ Log.d(CallerInfoLookupHelper.this, "onCallerInfoQueryComplete - found info for %s",
+ Log.piiHandle(handle));
+ // Got something, so complete the future.
+ callerInfoFuture.complete(new Pair<>(handle, info));
+ }
+
+ @Override
+ public void onContactPhotoQueryComplete(Uri handle, CallerInfo info) {
+ // No-op for now; not something this future cares about.
+ }
+ };
+
+ // Start async lookup.
+ startLookup(handle, listener);
+
+ return callerInfoFuture;
+ }
+
public void startLookup(final Uri handle, OnQueryCompleteListener listener) {
if (handle == null) {
listener.onCallerInfoQueryComplete(handle, null);
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index be9b1dd..aa4cf08 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -46,6 +46,7 @@
import android.provider.CallLog.Calls;
import android.provider.Settings;
import android.telecom.CallAudioState;
+import android.telecom.CallIdentification;
import android.telecom.Conference;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
@@ -612,7 +613,7 @@
filters.add(new CallScreeningServiceController(mContext, this, mPhoneAccountRegistrar,
new ParcelableCallUtils.Converter(), mLock,
new TelecomServiceImpl.SettingsSecureAdapterImpl(), mCallerInfoLookupHelper,
- new CallScreeningServiceController.AppLabelProxy() {
+ new CallScreeningServiceHelper.AppLabelProxy() {
@Override
public String getAppLabel(String packageName) {
PackageManager pm = mContext.getPackageManager();
@@ -1436,6 +1437,34 @@
return mPendingAccountSelection;
}, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA"));
+ // Potentially perform call identification for dialed TEL scheme numbers.
+ if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) {
+ // Perform an asynchronous contacts lookup in this stage; ensure post-dial digits are
+ // not included.
+ CompletableFuture<Pair<Uri, CallerInfo>> contactLookupFuture =
+ mCallerInfoLookupHelper.startLookup(Uri.fromParts(handle.getScheme(),
+ PhoneNumberUtils.extractNetworkPortion(handle.getSchemeSpecificPart()),
+ null));
+
+ // Once the phone account selection stage has completed, we can handle the results from
+ // that with the contacts lookup in order to determine if we should lookup bind to the
+ // CallScreeningService in order for it to potentially provide caller ID.
+ dialerSelectPhoneAccountFuture.thenAcceptBothAsync(contactLookupFuture,
+ (callPhoneAccountHandlePair, uriCallerInfoPair) -> {
+ Call theCall = callPhoneAccountHandlePair.first;
+ boolean isInContacts = uriCallerInfoPair.second != null
+ && uriCallerInfoPair.second.contactExists;
+ Log.d(CallsManager.this, "outgoingCallIdStage: isInContacts=%s",
+ isInContacts);
+
+ // We only want to provide a CallScreeningService with a call if its not in
+ // contacts.
+ if (!isInContacts) {
+ performCallIdentification(theCall);
+ }
+ }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB"));
+ }
+
// Finally, after all user interaction is complete, we execute this code to finish setting
// up the outgoing call. The inner method always returns a completed future containing the
// call that we've finished setting up.
@@ -1498,6 +1527,50 @@
}
/**
+ * Performs call identification for an outgoing phone call.
+ * @param theCall The outgoing call to perform identification.
+ */
+ private void performCallIdentification(Call theCall) {
+ // Find the user chosen call screening app.
+ String callScreeningApp =
+ mRoleManagerAdapter.getDefaultCallScreeningApp();
+
+ CompletableFuture<CallIdentification> future =
+ new CallScreeningServiceHelper(mContext,
+ mLock,
+ callScreeningApp,
+ new ParcelableCallUtils.Converter(),
+ mCurrentUserHandle,
+ theCall,
+ new CallScreeningServiceHelper.AppLabelProxy() {
+ @Override
+ public String getAppLabel(String packageName) {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(
+ packageName, 0);
+ return (String) pm.getApplicationLabel(info);
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ Log.w(this, "Could not determine package name.");
+ }
+
+ return null;
+ }
+ }).process();
+
+ // When we are done, apply call identification to the call.
+ future.thenApply(v -> {
+ Log.i(CallsManager.this, "setting caller ID: %s", v);
+ if (v != null) {
+ synchronized (mLock) {
+ theCall.setCallIdentification(v);
+ }
+ }
+ return null;
+ });
+ }
+
+ /**
* Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing
* call. Takes into account the following:
* 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the
diff --git a/src/com/android/server/telecom/ParcelableCallUtils.java b/src/com/android/server/telecom/ParcelableCallUtils.java
index fe52f4b..8dab6a6 100644
--- a/src/com/android/server/telecom/ParcelableCallUtils.java
+++ b/src/com/android/server/telecom/ParcelableCallUtils.java
@@ -16,6 +16,10 @@
package com.android.server.telecom;
+import static android.telecom.Call.Details.DIRECTION_INCOMING;
+import static android.telecom.Call.Details.DIRECTION_OUTGOING;
+import static android.telecom.Call.Details.DIRECTION_UNKNOWN;
+
import android.net.Uri;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
@@ -167,6 +171,14 @@
}
ParcelableRttCall rttCall = includeRttCall ? getParcelableRttCall(call) : null;
+ int callDirection;
+ if (call.isIncoming()) {
+ callDirection = DIRECTION_INCOMING;
+ } else if (call.isUnknown()) {
+ callDirection = DIRECTION_UNKNOWN;
+ } else {
+ callDirection = DIRECTION_OUTGOING;
+ }
return new ParcelableCall(
call.getId(),
@@ -195,7 +207,8 @@
call.getIntentExtras(),
call.getExtras(),
call.getCreationTimeMillis(),
- call.getCallIdentification());
+ call.getCallIdentification(),
+ callDirection);
}
/**
@@ -203,7 +216,7 @@
* {@link android.telecom.CallScreeningService}. We ONLY expose the following:
* <ul>
* <li>Call Id (not exposed to public, but needed to associated calls)</li>
- * <li>Call state</li>
+ * <li>Call directoin</li>
* <li>Creation time</li>
* <li>Connection time</li>
* <li>Handle (phone number)</li>
@@ -216,6 +229,14 @@
public static ParcelableCall toParcelableCallForScreening(Call call) {
Uri handle = call.getHandlePresentation() == TelecomManager.PRESENTATION_ALLOWED ?
call.getHandle() : null;
+ int callDirection;
+ if (call.isIncoming()) {
+ callDirection = DIRECTION_INCOMING;
+ } else if (call.isUnknown()) {
+ callDirection = DIRECTION_UNKNOWN;
+ } else {
+ callDirection = DIRECTION_OUTGOING;
+ }
return new ParcelableCall(
call.getId(),
getParcelableState(call, false /* supportsExternalCalls */),
@@ -243,7 +264,8 @@
null, /* intentExtras */
null, /* callExtras */
call.getCreationTimeMillis(),
- null /* callIdentification */);
+ null /* callIdentification */,
+ callDirection);
}
private static int getParcelableState(Call call, boolean supportsExternalCalls) {
diff --git a/src/com/android/server/telecom/RoleManagerAdapterImpl.java b/src/com/android/server/telecom/RoleManagerAdapterImpl.java
index 33055a8..51fe9b4 100644
--- a/src/com/android/server/telecom/RoleManagerAdapterImpl.java
+++ b/src/com/android/server/telecom/RoleManagerAdapterImpl.java
@@ -26,11 +26,10 @@
import java.util.stream.Collectors;
public class RoleManagerAdapterImpl implements RoleManagerAdapter {
- private static final String ROLE_CALL_REDIRECTION_APP = "android.app.role.PROXY_CALLING_APP";
- private static final String ROLE_CAR_MODE_DIALER = "android.app.role.CAR_MODE_DIALER_APP";
- private static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING_APP";
- private static final String ROLE_CALL_COMPANION_APP =
- "android.app.role.CALL_COMPANION_APP";
+ private static final String ROLE_CALL_REDIRECTION_APP = RoleManager.ROLE_PROXY_CALLING_APP;
+ private static final String ROLE_CAR_MODE_DIALER = RoleManager.ROLE_CAR_MODE_DIALER_APP;
+ private static final String ROLE_CALL_SCREENING = RoleManager.ROLE_CALL_SCREENING_APP;
+ private static final String ROLE_CALL_COMPANION_APP = RoleManager.ROLE_CALL_COMPANION_APP;
private String mOverrideDefaultCallRedirectionApp = null;
private String mOverrideDefaultCallScreeningApp = null;
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
index 3ca95ff..b72ac0b 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceController.java
@@ -18,14 +18,11 @@
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PersistableBundle;
-import android.os.UserHandle;
import android.provider.CallLog;
-import android.provider.Settings;
import android.telecom.Log;
import android.telecom.Logging.Runnable;
import android.telecom.TelecomManager;
@@ -34,6 +31,7 @@
import com.android.internal.telephony.CallerInfo;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallScreeningServiceHelper;
import com.android.server.telecom.CallerInfoLookupHelper;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.LogUtils;
@@ -51,14 +49,6 @@
public class CallScreeningServiceController implements IncomingCallFilter.CallFilter,
CallScreeningServiceFilter.CallScreeningFilterResultCallback {
- /**
- * Abstracts away dependency on the {@link PackageManager} required to fetch the label for an
- * app.
- */
- public interface AppLabelProxy {
- String getAppLabel(String packageName);
- }
-
private final Context mContext;
private final CallsManager mCallsManager;
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
@@ -66,7 +56,7 @@
private final TelecomSystem.SyncRoot mTelecomLock;
private final TelecomServiceImpl.SettingsSecureAdapter mSettingsSecureAdapter;
private final CallerInfoLookupHelper mCallerInfoLookupHelper;
- private final AppLabelProxy mAppLabelProxy;
+ private final CallScreeningServiceHelper.AppLabelProxy mAppLabelProxy;
private final int CARRIER_CALL_FILTERING_TIMED_OUT = 2000; // 2 seconds
private final int CALL_FILTERING_TIMED_OUT = 4500; // 4.5 seconds
@@ -96,7 +86,7 @@
TelecomSystem.SyncRoot lock,
TelecomServiceImpl.SettingsSecureAdapter settingsSecureAdapter,
CallerInfoLookupHelper callerInfoLookupHelper,
- AppLabelProxy appLabelProxy) {
+ CallScreeningServiceHelper.AppLabelProxy appLabelProxy) {
mContext = context;
mCallsManager = callsManager;
mPhoneAccountRegistrar = phoneAccountRegistrar;
@@ -144,6 +134,8 @@
}
} else if (!TextUtils.isEmpty(packageName) &&
packageName.equals(getDefaultDialerPackageName())) {
+ // Default dialer defined CallScreeningService cannot skip the call log.
+ mResult.shouldAddToCallLog = true;
mIsDefaultDialerFinished = true;
if (result.mCallBlockReason == CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE ||
mIsUserChosenFinished) {
@@ -151,6 +143,8 @@
}
} else if (!TextUtils.isEmpty(packageName) &&
packageName.equals(getUserChosenPackageName())) {
+ // User defined CallScreeningService cannot skip the call log.
+ mResult.shouldAddToCallLog = true;
mIsUserChosenFinished = true;
if (mIsDefaultDialerFinished) {
finishCallScreening();
diff --git a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
index b23cbe9..3ec1569 100644
--- a/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
+++ b/src/com/android/server/telecom/callfiltering/CallScreeningServiceFilter.java
@@ -40,6 +40,7 @@
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallScreeningServiceHelper;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.LogUtils;
import com.android.server.telecom.ParcelableCallUtils;
@@ -220,7 +221,12 @@
mCallback = callback;
mPackageName = packageName;
mAppName = appName;
- if (!bindService()) {
+
+ mConnection = new CallScreeningServiceConnection();
+ if (!CallScreeningServiceHelper.bindCallScreeningService(mContext,
+ mCallsManager.getCurrentUserHandle(),
+ mPackageName,
+ mConnection)) {
Log.i(this, "Could not bind to call screening service");
finishCallScreening();
}
@@ -233,7 +239,11 @@
if (mConnection != null) {
// We still need to call unbind even if the service disconnected.
- mContext.unbindService(mConnection);
+ try {
+ mContext.unbindService(mConnection);
+ } catch (IllegalArgumentException ie) {
+ Log.e(this, ie, "Unbind error");
+ }
mConnection = null;
}
mService = null;
@@ -241,52 +251,6 @@
}
}
- private boolean bindService() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.i(this, "PackageName is empty. Not performing call screening.");
- return false;
- }
-
- Intent intent = new Intent(CallScreeningService.SERVICE_INTERFACE)
- .setPackage(mPackageName);
- List<ResolveInfo> entries = mContext.getPackageManager().queryIntentServicesAsUser(
- intent, 0, mCallsManager.getCurrentUserHandle().getIdentifier());
- if (entries.isEmpty()) {
- Log.i(this, mPackageName + "is no call screening services installed on this device.");
- return false;
- }
-
- ResolveInfo entry = entries.get(0);
- if (entry.serviceInfo == null) {
- Log.w(this, mPackageName + " call screening service has invalid service info");
- return false;
- }
-
- if (entry.serviceInfo.permission == null || !entry.serviceInfo.permission.equals(
- Manifest.permission.BIND_SCREENING_SERVICE)) {
- Log.w(this, "CallScreeningService must require BIND_SCREENING_SERVICE permission: " +
- entry.serviceInfo.packageName);
- return false;
- }
-
- ComponentName componentName =
- new ComponentName(entry.serviceInfo.packageName, entry.serviceInfo.name);
- Log.addEvent(mCall, LogUtils.Events.BIND_SCREENING, componentName);
- intent.setComponent(componentName);
- ServiceConnection connection = new CallScreeningServiceConnection();
- if (mContext.bindServiceAsUser(
- intent,
- connection,
- Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
- UserHandle.CURRENT)) {
- Log.d(this, "bindService, found service, waiting for it to connect");
- mConnection = connection;
- return true;
- }
-
- return false;
- }
-
private void onServiceBound(ICallScreeningService service) {
mService = service;
try {
diff --git a/testapps/res/layout/self_managed_sample_main.xml b/testapps/res/layout/self_managed_sample_main.xml
index 68ae65c..28f4473 100644
--- a/testapps/res/layout/self_managed_sample_main.xml
+++ b/testapps/res/layout/self_managed_sample_main.xml
@@ -106,6 +106,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Accept Handover"/>
+ <Button
+ android:id="@+id/requestCallScreeningRole"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Req CallScreen Role"/>
</LinearLayout>
<ListView
diff --git a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
index 959b855..49879d1 100644
--- a/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
+++ b/testapps/src/com/android/server/telecom/testapps/SelfManagedCallingActivity.java
@@ -19,6 +19,8 @@
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.role.RoleManager;
+import android.content.Intent;
import android.media.AudioAttributes;
import android.media.RingtoneManager;
import android.net.Uri;
@@ -48,11 +50,13 @@
*/
public class SelfManagedCallingActivity extends Activity {
private static final String TAG = "SelfMgCallActivity";
+ private static final int REQUEST_ID = 1;
private SelfManagedCallList mCallList = SelfManagedCallList.getInstance();
private CheckBox mCheckIfPermittedBeforeCalling;
private Button mPlaceOutgoingCallButton;
private Button mPlaceIncomingCallButton;
private Button mHandoverFrom;
+ private Button mRequestCallScreeningRole;
private RadioButton mUseAcct1Button;
private RadioButton mUseAcct2Button;
private CheckBox mHoldableCheckbox;
@@ -126,6 +130,10 @@
mHandoverFrom.setOnClickListener((v -> {
initiateHandover();
}));
+ mRequestCallScreeningRole = (Button) findViewById(R.id.requestCallScreeningRole);
+ mRequestCallScreeningRole.setOnClickListener((v -> {
+ requestCallScreeningRole();
+ }));
mUseAcct1Button = findViewById(R.id.useAcct1Button);
mUseAcct2Button = findViewById(R.id.useAcct2Button);
@@ -221,4 +229,21 @@
NotificationManager mgr = getSystemService(NotificationManager.class);
mgr.createNotificationChannel(channel);
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_ID) {
+ if (resultCode == android.app.Activity.RESULT_OK) {
+ Toast.makeText(this, "Call screening role granted.", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(this, "Call screening role NOT granted.", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void requestCallScreeningRole() {
+ RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_SCREENING_APP);
+ startActivityForResult(intent, REQUEST_ID);
+ }
}
\ No newline at end of file
diff --git a/testapps/src/com/android/server/telecom/testapps/TestCallScreeningService.java b/testapps/src/com/android/server/telecom/testapps/TestCallScreeningService.java
index fb231da..0c0b303 100644
--- a/testapps/src/com/android/server/telecom/testapps/TestCallScreeningService.java
+++ b/testapps/src/com/android/server/telecom/testapps/TestCallScreeningService.java
@@ -41,9 +41,20 @@
sTestCallScreeningService = this;
mDetails = callDetails;
- Intent errorIntent = new Intent(this, CallScreeningActivity.class);
- errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(errorIntent);
+ if (callDetails.getCallDirection() == Call.Details.DIRECTION_INCOMING) {
+ Intent errorIntent = new Intent(this, CallScreeningActivity.class);
+ errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(errorIntent);
+ } else {
+ CallIdentification callIdentification = new CallIdentification.Builder()
+ .setNuisanceConfidence(CallIdentification.CONFIDENCE_NOT_NUISANCE)
+ .setName("Janes's Laundry")
+ .setDescription("1255 DirtySocks Lane")
+ .setDetails("Open 16 hrs")
+ .setPhoto(Icon.createWithResource(this, R.drawable.ic_android_black_24dp))
+ .build();
+ provideCallIdentification(mDetails, callIdentification);
+ }
}
public void blockCall() {
diff --git a/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java b/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
index 0004035..1001d23 100644
--- a/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallScreeningServiceControllerTest.java
@@ -35,6 +35,7 @@
import com.android.internal.telephony.CallerInfo;
import com.android.server.telecom.Call;
+import com.android.server.telecom.CallScreeningServiceHelper;
import com.android.server.telecom.CallerInfoLookupHelper;
import com.android.server.telecom.CallsManager;
import com.android.server.telecom.ParcelableCallUtils;
@@ -80,8 +81,8 @@
@Mock PhoneAccountRegistrar mPhoneAccountRegistrar;
@Mock private CallerInfoLookupHelper mCallerInfoLookupHelper;
- CallScreeningServiceController.AppLabelProxy mAppLabelProxy =
- new CallScreeningServiceController.AppLabelProxy() {
+ CallScreeningServiceHelper.AppLabelProxy mAppLabelProxy =
+ new CallScreeningServiceHelper.AppLabelProxy() {
@Override
public String getAppLabel(String packageName) {
return APP_NAME;
@@ -292,7 +293,7 @@
verify(mCallback).onCallFilteringComplete(eq(mCall), eq(new CallFilteringResult(
false, // shouldAllowCall
true, // shouldReject
- false, // shouldAddToCallLog
+ true, // shouldAddToCallLog (we don't allow services to skip call log)
true, // shouldShowNotification
CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
APP_NAME, //callScreeningAppName
@@ -340,7 +341,7 @@
verify(mCallback).onCallFilteringComplete(eq(mCall), eq(new CallFilteringResult(
false, // shouldAllowCall
true, // shouldReject
- false, // shouldAddToCallLog
+ true, // shouldAddToCallLog (we don't allow services to skip call log)
true, // shouldShowNotification
CallLog.Calls.BLOCK_REASON_CALL_SCREENING_SERVICE, //callBlockReason
APP_NAME, //callScreeningAppName