Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa4eecad authored by Felipe Leme's avatar Felipe Leme Committed by Android (Google) Code Review
Browse files

Merge changes from topics "kill_switch", "disabled_by_device_config"

* changes:
  Reverted ContentCapture kill-switch mechanism.
  Disable ContentCapture at runtime when it's disabled by DeviceConfig.
parents 10357ba7 14ef461f
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -5712,6 +5712,7 @@ package android.provider {
    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String);
    method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean);
    field public static final String NAMESPACE_AUTOFILL = "autofill";
    field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
    field public static final String NAMESPACE_GAME_DRIVER = "game_driver";
    field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot";
    field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
@@ -5741,10 +5742,6 @@ package android.provider {
    field public static final String SERVICE_ENABLED = "service_enabled";
  }
  public static interface DeviceConfig.ContentCapture {
    field public static final String NAMESPACE = "content_capture";
  }
  public static interface DeviceConfig.DexBoot {
    field public static final String NAMESPACE = "dex_boot";
    field public static final String PRIV_APPS_OOB_ENABLED = "priv_apps_oob_enabled";
+2 −5
Original line number Diff line number Diff line
@@ -1788,11 +1788,7 @@ package android.provider {
    method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(String, String);
    method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static void resetToDefaults(int, @Nullable String);
    method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean);
  }

  public static interface DeviceConfig.ContentCapture {
    field public static final String NAMESPACE = "content_capture";
    field public static final String PROPERTY_CONTENTCAPTURE_ENABLED = "enable_contentcapture";
    field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
  }

  public static interface DeviceConfig.Privacy {
@@ -2725,6 +2721,7 @@ package android.view.contentcapture {
  public final class ContentCaptureManager {
    method public boolean isContentCaptureFeatureEnabled();
    method public void setContentCaptureFeatureEnabled(boolean);
    field public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED = "service_explicitly_enabled";
  }

  public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
+3 −28
Original line number Diff line number Diff line
@@ -72,39 +72,14 @@ public final class DeviceConfig {
    public static final String NAMESPACE_AUTOFILL = "autofill";

    /**
     * ContentCapture-related properties definitions.
     * Namespace for content capture feature used by on-device machine intelligence
     * to provide suggestions in a privacy-safe manner.
     *
     * @hide
     */
    @SystemApi
    @TestApi
    public interface ContentCapture {
        String NAMESPACE = "content_capture";

        /**
         * Property used by {@code com.android.server.SystemServer} on start to decide whether
         * the Content Capture service should be created or not.
         *
         * <p>Possible values are:
         *
         * <ul>
         *   <li>If set to {@code default}, it will only be set if the OEM provides and defines the
         *   service name by overlaying {@code config_defaultContentCaptureService} (this is the
         *   "default" mode)
         *   <li>If set to {@code always}, it will always be enabled, even when the resource is not
         *   overlaid (this is useful during development and to run the CTS tests on AOSP builds).
         *   <li>Otherwise, it's explicitly disabled (this could work as a "kill switch" so OEMs
         *   can disable it remotely in case of emergency by setting to something else (like
         *   {@code "false"}); notice that it's also disabled if the OEM doesn't explicitly set one
         *   of the values above).
         * </ul>
         *
         * @hide
         */
        // TODO(b/121153631): revert back to SERVICE_EXPLICITLY_ENABLED approach
        @TestApi
        String PROPERTY_CONTENTCAPTURE_ENABLED = "enable_contentcapture";
    }
    public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";

    /**
     * Namespace for all input-related features that are used at the native level.
+19 −0
Original line number Diff line number Diff line
@@ -66,6 +66,25 @@ public final class ContentCaptureManager {
     */
    private static final int SYNC_CALLS_TIMEOUT_MS = 5000;

    /**
     * DeviceConfig property used by {@code com.android.server.SystemServer} on start to decide
     * whether the Content Capture service should be created or not
     *
     * <p>By default it should *NOT* be set (or set to {@code "default"}, so the decision is based
     * on whether the OEM provides an implementation for the service), but it can be overridden to:
     *
     * <ul>
     *   <li>Provide a "kill switch" so OEMs can disable it remotely in case of emergency (when
     *   it's set to {@code "false"}).
     *   <li>Enable the CTS tests to be run on AOSP builds (when it's set to {@code "true"}).
     * </ul>
     *
     * @hide
     */
    @TestApi
    public static final String DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED =
            "service_explicitly_enabled";

    private final Object mLock = new Object();

    @NonNull
+68 −5
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -39,6 +40,7 @@ import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.LocalLog;
import android.util.Slog;
@@ -89,19 +91,38 @@ public final class ContentCaptureManagerService extends
    @Nullable
    private SparseBooleanArray mDisabledUsers;

    /**
     * Global kill-switch based on value defined by
     * {@link ContentCaptureManager#DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED}.
     */
    @GuardedBy("mLock")
    @Nullable
    private boolean mDisabledByDeviceConfig;

    public ContentCaptureManagerService(@NonNull Context context) {
        super(context, new FrameworkResourcesServiceNameResolver(context,
                com.android.internal.R.string.config_defaultContentCaptureService),
                UserManager.DISALLOW_CONTENT_CAPTURE);
        // Sets which serviecs are disabled
        DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                ActivityThread.currentApplication().getMainExecutor(),
                (namespace, key, value) -> {
                    if (!ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED
                            .equals(key)) {
                        Slog.i(mTag, "Ignoring change on " + key);
                        return;
                    }
                    setDisabledByDeviceConfig(value);
                });
        setDisabledByDeviceConfig();

        // Sets which services are disabled
        final UserManager um = getContext().getSystemService(UserManager.class);
        final List<UserInfo> users = um.getUsers();
        for (int i = 0; i < users.size(); i++) {
            final int userId = users.get(i).id;
            final boolean disabled = isDisabledBySettings(userId);
            final boolean disabled = mDisabledByDeviceConfig || isDisabledBySettings(userId);
            if (disabled) {
                Slog.i(mTag, "user " + userId + " disabled by settings");
                Slog.i(mTag, "user " + userId + " disabled by settings or device config");
                if (mDisabledUsers == null) {
                    mDisabledUsers = new SparseBooleanArray(1);
                }
@@ -160,7 +181,8 @@ public final class ContentCaptureManagerService extends

    @Override // from AbstractMasterSystemService
    protected boolean isDisabledLocked(@UserIdInt int userId) {
        return isDisabledBySettingsLocked(userId) || super.isDisabledLocked(userId);
        return mDisabledByDeviceConfig || isDisabledBySettingsLocked(userId)
                || super.isDisabledLocked(userId);
    }

    private boolean isDisabledBySettingsLocked(@UserIdInt int userId) {
@@ -191,6 +213,45 @@ public final class ContentCaptureManagerService extends
        return false;
    }

    private void setDisabledByDeviceConfig() {
        final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED);
        setDisabledByDeviceConfig(value);
    }

    private void setDisabledByDeviceConfig(@Nullable String value) {
        if (verbose) Slog.v(mTag, "setDisabledByDeviceConfig(): value=" + value);
        final UserManager um = getContext().getSystemService(UserManager.class);
        final List<UserInfo> users = um.getUsers();

        final boolean newDisabledValue;

        if (value != null && value.equalsIgnoreCase("false")) {
            newDisabledValue = true;
        } else {
            newDisabledValue = false;
        }

        synchronized (mLock) {
            if (mDisabledByDeviceConfig == newDisabledValue) {
                if (verbose) {
                    Slog.v(mTag, "setDisabledByDeviceConfig(): already " + newDisabledValue);
                }
                return;
            }
            mDisabledByDeviceConfig = newDisabledValue;

            Slog.i(mTag, "setDisabledByDeviceConfig(): set to " + mDisabledByDeviceConfig);
            for (int i = 0; i < users.size(); i++) {
                final int userId = users.get(i).id;
                boolean disabled = mDisabledByDeviceConfig || isDisabledBySettingsLocked(userId);
                Slog.i(mTag, "setDisabledByDeviceConfig(): updating service for user "
                        + userId + " to " + (disabled ? "'disabled'" : "'enabled'"));
                updateCachedServiceLocked(userId, disabled);
            }
        }
    }

    private void setContentCaptureFeatureEnabledForUser(@UserIdInt int userId, boolean enabled) {
        synchronized (mLock) {
            if (mDisabledUsers == null) {
@@ -338,6 +399,8 @@ public final class ContentCaptureManagerService extends
        super.dumpLocked(prefix, pw);

        pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
        pw.print(prefix); pw.print("Disabled by DeviceConfig: ");
        pw.println(mDisabledByDeviceConfig);
    }

    final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub {
@@ -406,7 +469,7 @@ public final class ContentCaptureManagerService extends
                        "isContentCaptureFeatureEnabled()", userId, Binder.getCallingUid(), result);
                if (!isService) return;

                enabled = !isDisabledBySettingsLocked(userId);
                enabled = !mDisabledByDeviceConfig && !isDisabledBySettingsLocked(userId);
            }
            try {
                result.send(enabled ? ContentCaptureManager.RESULT_CODE_TRUE
Loading