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
This commit is contained in:
Roy Chou
2023-04-14 09:06:25 +00:00
parent c01c5f8875
commit 3f09839e1d
3 changed files with 129 additions and 57 deletions

View File

@@ -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;
}
}

View File

@@ -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(

View File

@@ -0,0 +1,115 @@
/*
* 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);
}
}