Added new APIs to let ContentCaptureService enable / disable the feature.

Bug: 123286662
Test: atest ChildlessActivityTest#testSetContentCaptureFeatureEnabled_disabledByApi \
        ChildlessActivityTest#testSetContentCaptureFeatureEnabled_disabledThenReEnabledByApi
Test: atest CtsContentCaptureServiceTestCases # for sanity check

Change-Id: Ideefb4c8e122e5f3f55dd7de8085212b2d8ce073
This commit is contained in:
Felipe Leme
2019-01-25 17:29:29 -08:00
parent 91ddecac0a
commit bb0c2a2a33
4 changed files with 149 additions and 3 deletions

View File

@@ -9203,6 +9203,7 @@ package android.view.contentcapture {
public final class ContentCaptureManager {
method public boolean isContentCaptureFeatureEnabled();
method public void setContentCaptureFeatureEnabled(boolean);
}
public final class UserDataRemovalRequest implements android.os.Parcelable {

View File

@@ -15,6 +15,7 @@
*/
package android.view.contentcapture;
import static android.view.contentcapture.ContentCaptureHelper.DEBUG;
import static android.view.contentcapture.ContentCaptureHelper.VERBOSE;
import android.annotation.NonNull;
@@ -52,6 +53,13 @@ public final class ContentCaptureManager {
private static final String TAG = ContentCaptureManager.class.getSimpleName();
/** @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;
/**
* Timeout for calls to system_server.
*/
@@ -184,6 +192,10 @@ public final class ContentCaptureManager {
* it on {@link android.app.Activity#onCreate(android.os.Bundle, android.os.PersistableBundle)}.
*/
public void setContentCaptureEnabled(boolean enabled) {
if (DEBUG) {
Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
}
synchronized (mLock) {
mFlags |= enabled ? 0 : ContentCaptureContext.FLAG_DISABLED_BY_APP;
}
@@ -195,6 +207,9 @@ public final class ContentCaptureManager {
* <p>This method is typically used by the Content Capture Service settings page, so it can
* provide a toggle to enable / disable it.
*
* @throws SecurityException if caller is not the app that owns the Content Capture service
* associated with the user.
*
* @hide
*/
@SystemApi
@@ -202,13 +217,54 @@ public final class ContentCaptureManager {
if (mService == null) return false;
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
final int resultCode;
try {
mService.isContentCaptureFeatureEnabled(resultReceiver);
return resultReceiver.getIntResult() == 1;
resultCode = resultReceiver.getIntResult();
} catch (RemoteException e) {
// Unable to retrieve component name in a reasonable amount of time.
throw e.rethrowFromSystemServer();
}
switch (resultCode) {
case RESULT_CODE_TRUE:
return true;
case RESULT_CODE_FALSE:
return false;
case RESULT_CODE_NOT_SERVICE:
throw new SecurityException("caller is not user's ContentCapture service");
default:
throw new IllegalStateException("received invalid result: " + resultCode);
}
}
/**
* Sets whether Content Capture is enabled for the given user.
*
* @throws SecurityException if caller is not the app that owns the Content Capture service
* associated with the user.
*
* @hide
*/
@SystemApi
public void setContentCaptureFeatureEnabled(boolean enabled) {
if (DEBUG) Log.d(TAG, "setContentCaptureFeatureEnabled(): setting to " + enabled);
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
final int resultCode;
try {
mService.setContentCaptureFeatureEnabled(enabled, resultReceiver);
resultCode = resultReceiver.getIntResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
switch (resultCode) {
case RESULT_CODE_TRUE:
// Our work is done here, in our void existance...
return;
case RESULT_CODE_NOT_SERVICE:
throw new SecurityException("caller is not user's ContentCapture service");
default:
throw new IllegalStateException("received invalid result: " + resultCode);
}
}
/**

View File

@@ -67,4 +67,9 @@ oneway interface IContentCaptureManager {
* Returns whether the content capture feature is enabled for the calling user.
*/
void isContentCaptureFeatureEnabled(in IResultReceiver result);
/**
* Sets whether the content capture feature is enabled for the given user.
*/
void setContentCaptureFeatureEnabled(boolean enabled, in IResultReceiver result);
}

View File

@@ -26,6 +26,8 @@ import android.app.ActivityManagerInternal;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Binder;
@@ -40,6 +42,7 @@ import android.provider.Settings;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.IContentCaptureManager;
import android.view.contentcapture.UserDataRemovalRequest;
@@ -278,6 +281,57 @@ public final class ContentCaptureManagerService extends
return mAm;
}
@GuardedBy("mLock")
private boolean assertCalledByServiceLocked(@NonNull String methodName, @UserIdInt int userId,
int callingUid, @NonNull IResultReceiver result) {
final boolean isService = isCalledByServiceLocked(methodName, userId, callingUid);
if (isService) return true;
try {
result.send(ContentCaptureManager.RESULT_CODE_NOT_SERVICE,
/* resultData= */ null);
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
}
return false;
}
@GuardedBy("mLock")
private boolean isCalledByServiceLocked(@NonNull String methodName, @UserIdInt int userId,
int callingUid) {
final String serviceName = mServiceNameResolver.getServiceName(userId);
if (serviceName == null) {
Slog.e(mTag, methodName + ": called by UID " + callingUid
+ ", but there's no service set for user " + userId);
return false;
}
final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
if (serviceComponent == null) {
Slog.w(mTag, methodName + ": invalid service name: " + serviceName);
return false;
}
final String servicePackageName = serviceComponent.getPackageName();
final PackageManager pm = getContext().getPackageManager();
final int serviceUid;
try {
serviceUid = pm.getPackageUidAsUser(servicePackageName, UserHandle.getCallingUserId());
} catch (NameNotFoundException e) {
Slog.w(mTag, methodName + ": could not verify UID for " + serviceName);
return false;
}
if (callingUid != serviceUid) {
Slog.e(mTag, methodName + ": called by UID " + callingUid + ", but service UID is "
+ serviceUid);
return false;
}
return true;
}
@Override // from AbstractMasterSystemService
protected void dumpLocked(String prefix, PrintWriter pw) {
super.dumpLocked(prefix, pw);
@@ -352,15 +406,45 @@ public final class ContentCaptureManagerService extends
final int userId = UserHandle.getCallingUserId();
boolean enabled;
synchronized (mLock) {
final boolean isService = assertCalledByServiceLocked(
"isContentCaptureFeatureEnabled()", userId, Binder.getCallingUid(), result);
if (!isService) return;
enabled = !isDisabledBySettingsLocked(userId);
}
try {
result.send(enabled ? 1 : 0, /* resultData= */null);
result.send(enabled ? ContentCaptureManager.RESULT_CODE_TRUE
: ContentCaptureManager.RESULT_CODE_FALSE, /* resultData= */null);
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send isContentCaptureFeatureEnabled(): " + e);
}
}
@Override
public void setContentCaptureFeatureEnabled(boolean enabled,
@NonNull IResultReceiver result) {
final int userId = UserHandle.getCallingUserId();
final boolean isService;
synchronized (mLock) {
isService = assertCalledByServiceLocked("setContentCaptureFeatureEnabled()", userId,
Binder.getCallingUid(), result);
}
if (!isService) return;
final long token = Binder.clearCallingIdentity();
try {
Settings.Secure.putStringForUser(getContext().getContentResolver(),
Settings.Secure.CONTENT_CAPTURE_ENABLED, Boolean.toString(enabled), userId);
} finally {
Binder.restoreCallingIdentity(token);
}
try {
result.send(ContentCaptureManager.RESULT_CODE_TRUE, /* resultData= */null);
} catch (RemoteException e) {
Slog.w(mTag, "Unable to send setContentCaptureFeatureEnabled(): " + e);
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), mTag, pw)) return;