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

Commit 3f09839e authored by Roy Chou's avatar Roy Chou
Browse files

chore(#Magnification): add MagnificationFeatureFlagBase

We'll add thumbnail feature flag in next cl and joystick feature flag in the future. These feature flag files are almost same as AlwaysOnMagnificationFeatureFlag, just in difference on namespace and feature name. Therefore, we add a feature flag base file to handle the logics, so that the extended feature flag files can share the base codes and customize namespace and feature name on its own.

In MagnificationFeatureFlagBase, we would clear binder identity, then access the DEVICE_CONFIG with given namespace. It's to prevent permission denied exceptions. we would restore the identity after the access.

Bug: 276979625
Test: manually
      atest MagnificationControllerTest
Change-Id: I2857cd332bd98726718cf5b5913e6eccf741c3da
parent c01c5f88
Loading
Loading
Loading
Loading
+10 −55
Original line number Diff line number Diff line
@@ -16,76 +16,31 @@

package com.android.server.accessibility.magnification;

import android.annotation.NonNull;
import android.provider.DeviceConfig;

import com.android.internal.annotations.VisibleForTesting;

import java.util.concurrent.Executor;

/**
 * Encapsulates the feature flags for always on magnification. {@see DeviceConfig}
 *
 * @hide
 */
public class AlwaysOnMagnificationFeatureFlag {
public class AlwaysOnMagnificationFeatureFlag extends MagnificationFeatureFlagBase {

    private static final String NAMESPACE = DeviceConfig.NAMESPACE_WINDOW_MANAGER;
    private static final String FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION =
            "AlwaysOnMagnifier__enable_always_on_magnifier";

    private AlwaysOnMagnificationFeatureFlag() {}

    /** Returns true if the feature flag is enabled for always on magnification */
    public static boolean isAlwaysOnMagnificationEnabled() {
        return DeviceConfig.getBoolean(
                NAMESPACE,
                FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION,
                /* defaultValue= */ false);
    @Override
    String getNamespace() {
        return NAMESPACE;
    }

    /** Sets the feature flag. Only used for testing; requires shell permissions. */
    @VisibleForTesting
    public static boolean setAlwaysOnMagnificationEnabled(boolean isEnabled) {
        return DeviceConfig.setProperty(
                NAMESPACE,
                FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION,
                Boolean.toString(isEnabled),
                /* makeDefault= */ false);
    @Override
    String getFeatureName() {
        return FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION;
    }

    /**
     * Adds a listener for when the feature flag changes.
     *
     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
     * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
     */
    @NonNull
    public static DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
            @NonNull Executor executor, @NonNull Runnable listener) {
        DeviceConfig.OnPropertiesChangedListener onChangedListener =
                properties -> {
                    if (properties.getKeyset().contains(
                            FEATURE_NAME_ENABLE_ALWAYS_ON_MAGNIFICATION)) {
                        listener.run();
                    }
                };
        DeviceConfig.addOnPropertiesChangedListener(
                NAMESPACE,
                executor,
                onChangedListener);

        return onChangedListener;
    }

    /**
     * Remove a listener for when the feature flag changes.
     *
     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
     * DeviceConfig.OnPropertiesChangedListener)}
     */
    public static void removeOnChangedListener(
            @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
        DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
    @Override
    boolean getDefaultValue() {
        return false;
    }
}
+4 −2
Original line number Diff line number Diff line
@@ -93,6 +93,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
    private final SparseArray<DisableMagnificationCallback>
            mMagnificationEndRunnableSparseArray = new SparseArray();

    private final AlwaysOnMagnificationFeatureFlag mAlwaysOnMagnificationFeatureFlag;
    private final MagnificationScaleProvider mScaleProvider;
    private FullScreenMagnificationController mFullScreenMagnificationController;
    private WindowMagnificationManager mWindowMagnificationMgr;
@@ -151,7 +152,8 @@ public class MagnificationController implements WindowMagnificationManager.Callb
        mSupportWindowMagnification = context.getPackageManager().hasSystemFeature(
                FEATURE_WINDOW_MAGNIFICATION);

        AlwaysOnMagnificationFeatureFlag.addOnChangedListener(
        mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag();
        mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
                ConcurrentUtils.DIRECT_EXECUTOR, mAms::updateAlwaysOnMagnification);
    }

@@ -698,7 +700,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb
    }

    public boolean isAlwaysOnMagnificationFeatureFlagEnabled() {
        return AlwaysOnMagnificationFeatureFlag.isAlwaysOnMagnificationEnabled();
        return mAlwaysOnMagnificationFeatureFlag.isFeatureFlagEnabled();
    }

    private DisableMagnificationCallback getDisableMagnificationEndRunnableLocked(
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.accessibility.magnification;

import android.annotation.NonNull;
import android.os.Binder;
import android.provider.DeviceConfig;

import com.android.internal.annotations.VisibleForTesting;

import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Abstract base class to encapsulates the feature flags for magnification features.
 * {@see DeviceConfig}
 *
 * @hide
 */
abstract class MagnificationFeatureFlagBase {

    abstract String getNamespace();
    abstract String getFeatureName();
    abstract boolean getDefaultValue();

    private void clearCallingIdentifyAndTryCatch(Runnable tryBlock, Runnable catchBlock) {
        try {
            Binder.withCleanCallingIdentity(() -> tryBlock.run());
        } catch (Throwable throwable) {
            catchBlock.run();
        }
    }

    /** Returns true iff the feature flag is readable and enabled */
    public boolean isFeatureFlagEnabled() {
        AtomicBoolean isEnabled = new AtomicBoolean(getDefaultValue());

        clearCallingIdentifyAndTryCatch(
                () -> isEnabled.set(DeviceConfig.getBoolean(
                        getNamespace(),
                        getFeatureName(),
                        getDefaultValue())),
                () -> isEnabled.set(getDefaultValue()));

        return isEnabled.get();
    }

    /** Sets the feature flag. Only used for testing; requires shell permissions. */
    @VisibleForTesting
    public boolean setFeatureFlagEnabled(boolean isEnabled) {
        AtomicBoolean success = new AtomicBoolean(getDefaultValue());

        clearCallingIdentifyAndTryCatch(
                () -> success.set(DeviceConfig.setProperty(
                        getNamespace(),
                        getFeatureName(),
                        Boolean.toString(isEnabled),
                        /* makeDefault= */ false)),
                () -> success.set(getDefaultValue()));

        return success.get();
    }

    /**
     * Adds a listener for when the feature flag changes.
     *
     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(
     * String, Executor, DeviceConfig.OnPropertiesChangedListener)}
     */
    @NonNull
    public DeviceConfig.OnPropertiesChangedListener addOnChangedListener(
            @NonNull Executor executor, @NonNull Runnable listener) {
        DeviceConfig.OnPropertiesChangedListener onChangedListener =
                properties -> {
                    if (properties.getKeyset().contains(
                            getFeatureName())) {
                        listener.run();
                    }
                };

        clearCallingIdentifyAndTryCatch(
                () -> DeviceConfig.addOnPropertiesChangedListener(
                        getNamespace(),
                        executor,
                        onChangedListener),
                () -> {});

        return onChangedListener;
    }

    /**
     * Remove a listener for when the feature flag changes.
     *
     * <p>{@see DeviceConfig#addOnPropertiesChangedListener(String, Executor,
     * DeviceConfig.OnPropertiesChangedListener)}
     */
    public void removeOnChangedListener(
            @NonNull DeviceConfig.OnPropertiesChangedListener onChangedListener) {
        DeviceConfig.removeOnPropertiesChangedListener(onChangedListener);
    }
}