New method ContentCaptureManager.getServiceSettingsComponentName()
Test: manual verification
Test: atest CtsContentCaptureServiceTestCases # sanity check
Bug: 119264902
Change-Id: I3f387386652dad7b187ae04b29f16512ff444ca1
diff --git a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
index 6ecd82f..fb60619 100644
--- a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
+++ b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
@@ -139,6 +139,7 @@
mSettingsActivity = settingsActivity;
}
+ @NonNull
public ServiceInfo getServiceInfo() {
return mServiceInfo;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index a3e6549..afddc38 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -32,6 +32,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
import android.view.contentcapture.ContentCaptureSession.FlushReason;
@@ -52,11 +53,13 @@
private static final String TAG = ContentCaptureManager.class.getSimpleName();
/** @hide */
+ public static final int RESULT_CODE_OK = 0;
+ /** @hide */
public static final int RESULT_CODE_TRUE = 1;
/** @hide */
public static final int RESULT_CODE_FALSE = 2;
/** @hide */
- public static final int RESULT_CODE_NOT_SERVICE = -1;
+ public static final int RESULT_CODE_SECURITY_EXCEPTION = -1;
/**
* Timeout for calls to system_server.
@@ -297,6 +300,34 @@
}
/**
+ * Gets the (optional) intent used to launch the service-specific settings.
+ *
+ * <p>This method is static because it's called by Settings, which might not be whitelisted
+ * for content capture (in which case the ContentCaptureManager on its context would be null).
+ *
+ * @hide
+ */
+ @Nullable
+ public static ComponentName getServiceSettingsComponentName() {
+ final IBinder binder = ServiceManager
+ .checkService(Context.CONTENT_CAPTURE_MANAGER_SERVICE);
+ if (binder == null) return null;
+
+ final IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(binder);
+ final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
+ try {
+ service.getServiceSettingsActivity(resultReceiver);
+ final int resultCode = resultReceiver.getIntResult();
+ if (resultCode == RESULT_CODE_SECURITY_EXCEPTION) {
+ throw new SecurityException(resultReceiver.getStringResult());
+ }
+ return resultReceiver.getParcelableResult();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Checks whether content capture is enabled for this activity.
*
* <p>There are many reasons it could be disabled, such as:
@@ -365,7 +396,7 @@
return true;
case RESULT_CODE_FALSE:
return false;
- case RESULT_CODE_NOT_SERVICE:
+ case RESULT_CODE_SECURITY_EXCEPTION:
throw new SecurityException("caller is not user's ContentCapture service");
default:
Log.wtf(TAG, "received invalid result: " + resultCode);
diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
index e3b0372..15fbaa2 100644
--- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl
+++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl
@@ -67,4 +67,9 @@
* Returns whether the content capture feature is enabled for the calling user.
*/
void isContentCaptureFeatureEnabled(in IResultReceiver result);
+
+ /**
+ * Returns a ComponentName with the name of custom service activity, if defined.
+ */
+ void getServiceSettingsActivity(in IResultReceiver result);
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 9995d8e..3619a9d 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -18,6 +18,12 @@
import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE;
import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION;
+import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_TRUE;
+
+import static com.android.internal.util.SyncResultReceiver.bundleFor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -57,7 +63,6 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.SyncResultReceiver;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -414,8 +419,7 @@
if (isService) return true;
try {
- result.send(ContentCaptureManager.RESULT_CODE_NOT_SERVICE,
- /* resultData= */ null);
+ result.send(RESULT_CODE_SECURITY_EXCEPTION, /* resultData= */ null);
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
}
@@ -518,8 +522,7 @@
connectedServiceComponentName = service.getServiceComponentName();
}
try {
- result.send(/* resultCode= */ 0,
- SyncResultReceiver.bundleFor(connectedServiceComponentName));
+ result.send(RESULT_CODE_OK, bundleFor(connectedServiceComponentName));
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send service component name: " + e);
}
@@ -547,14 +550,40 @@
enabled = !mDisabledByDeviceConfig && !isDisabledBySettingsLocked(userId);
}
try {
- result.send(enabled ? ContentCaptureManager.RESULT_CODE_TRUE
- : ContentCaptureManager.RESULT_CODE_FALSE, /* resultData= */null);
+ result.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, /* resultData= */null);
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
}
}
@Override
+ public void getServiceSettingsActivity(@NonNull IResultReceiver result) {
+ try {
+ enforceCallingPermissionForManagement();
+ } catch (SecurityException e) {
+ try {
+ result.send(RESULT_CODE_SECURITY_EXCEPTION, bundleFor(e.getMessage()));
+ } catch (RemoteException e2) {
+ Slog.w(mTag, "Unable to send getServiceSettingsIntent() exception: " + e2);
+ return;
+ }
+ }
+
+ final int userId = UserHandle.getCallingUserId();
+ final ComponentName componentName;
+ synchronized (mLock) {
+ final ContentCapturePerUserService service = getServiceForUserLocked(userId);
+ if (service == null) return;
+ componentName = service.getServiceSettingsActivityLocked();
+ }
+ try {
+ result.send(RESULT_CODE_OK, bundleFor(componentName));
+ } catch (RemoteException e) {
+ Slog.w(mTag, "Unable to send getServiceSettingsIntent(): " + e);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return;
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index d7d97b3..955f260 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -332,6 +332,18 @@
mRemoteService.onUserDataRemovalRequest(request);
}
+ @GuardedBy("mLock")
+ @Nullable
+ public ComponentName getServiceSettingsActivityLocked() {
+ if (mInfo == null) return null;
+
+ final String activityName = mInfo.getSettingsActivity();
+ if (activityName == null) return null;
+
+ final String packageName = mInfo.getServiceInfo().packageName;
+ return new ComponentName(packageName, activityName);
+ }
+
/**
* Asserts the component is owned by the caller.
*/