NaTo1000's picture
download
raw
7.85 kB
package com.termux.shared.android;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.termux.shared.logger.Logger;
import com.termux.shared.reflection.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Utils for Developer Options -> Feature Flags. The page won't show in user/production builds and
* is only shown in userdebug builds.
* https://cs.android.com/android/_/android/platform/frameworks/base/+/09dcdad5ebc159861920f090e07da60fac71ac0a:core/java/android/util/FeatureFlagUtils.java
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r31:packages/apps/Settings/src/com/android/settings/development/featureflags/FeatureFlagsPreferenceController.java;l=42
*
* The feature flags value can be modified in two ways.
*
* 1. sysprops with `setprop` command with root. Will be unset by default.
* Set value: `setprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs false`
* Get value: `getprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs`
* Unset value: `setprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs ""`
* Running `setprop` command requires root and even adb `shell` user cannot modify the values
* since selinux will not allow it by default. Some props like `settings_dynamic_system` can be
* set since they are exempted for `shell` in sepolicy.
*
* init: Unable to set property 'persist.sys.fflag.override.settings_enable_monitor_phantom_procs' from uid:2000 gid:2000 pid:9576: SELinux permission check failed
* [ 1034.877067] type=1107 audit(1644436809.637:34): uid=0 auid=4294967295 ses=4294967295 subj=u:r:init:s0 msg='avc: denied { set } for property=persist.sys.fflag.override.settings_enable_monitor_phantom_procs pid=9576 uid=2000 gid=2000 scontext=u:r:shell:s0 tcontext=u:object_r:system_prop:s0 tclass=property_service permissive=0'
*
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:system/sepolicy/private/property_contexts;l=71
* https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:system/sepolicy/private/shell.te;l=149
*
* 2. settings global list with adb or root. Will be unset by default. This takes precedence over
* sysprop value since `FeatureFlagUtils.isEnabled()`
* checks its value first. Override precedence: Settings.Global -> sys.fflag.override.* -> static list.
* Set value: `adb shell settings put global settings_enable_monitor_phantom_procs false`
* Get value: adb shell settings get global settings_enable_monitor_phantom_procs`
* Unset value: `adb shell settings delete global settings_enable_monitor_phantom_procs`
*
* https://cs.android.com/android/_/android/platform/frameworks/base/+/refs/tags/android-12.0.0_r31:core/java/android/util/FeatureFlagUtils.java;l=113
*
* The feature flag values can be modified in user builds with settings global list, but since the
* developer options feature flags page is not shown and considering that getprop values for features
* will be unset by default and settings global list will not be set either and there is no shell API,
* it will require an android app process to check if feature is supported on a device and what its
* default value is with reflection after bypassing hidden api restrictions since {@link #FEATURE_FLAGS_CLASS}
* is annotated as `@hide`.
*/
public class FeatureFlagUtils {
public enum FeatureFlagValue {
/** Unknown like due to exception raised while getting value. */
UNKNOWN("<unknown>"),
/** Flag is unsupported on current android build. */
UNSUPPORTED("<unsupported>"),
/** Flag is enabled. */
TRUE("true"),
/** Flag is not enabled. */
FALSE("false");
private final String name;
FeatureFlagValue(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public static final String FEATURE_FLAGS_CLASS = "android.util.FeatureFlagUtils";
private static final String LOG_TAG = "FeatureFlagUtils";
/**
* Get all feature flags in their raw form.
*/
@SuppressWarnings("unchecked")
public static Map<String, String> getAllFeatureFlags() {
ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
try {
@SuppressLint("PrivateApi") Class<?> clazz = Class.forName(FEATURE_FLAGS_CLASS);
Method getAllFeatureFlagsMethod = ReflectionUtils.getDeclaredMethod(clazz, "getAllFeatureFlags");
if (getAllFeatureFlagsMethod == null) return null;
return (Map<String, String>) ReflectionUtils.invokeMethod(getAllFeatureFlagsMethod, null).value;
} catch (Exception e) {
// ClassCastException may be thrown
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get all feature flags", e);
return null;
}
}
/**
* Check if a feature flag exists.
*
* @return Returns {@code true} if flag exists, otherwise {@code false}. This will be
* {@code null} if an exception is raised.
*/
@Nullable
public static Boolean featureFlagExists(@NonNull String feature) {
Map<String, String> featureFlags = getAllFeatureFlags();
if (featureFlags == null) return null;
return featureFlags.containsKey(feature);
}
/**
* Get {@link FeatureFlagValue} for a feature.
*
* @param context The {@link Context} for operations.
* @param feature The {@link String} name for feature.
* @return Returns {@link FeatureFlagValue}.
*/
@NonNull
public static FeatureFlagValue getFeatureFlagValueString(@NonNull Context context, @NonNull String feature) {
Boolean featureFlagExists = featureFlagExists(feature);
if (featureFlagExists == null) {
Logger.logError(LOG_TAG, "Failed to get feature flags \"" + feature + "\" value");
return FeatureFlagValue.UNKNOWN;
} else if (!featureFlagExists) {
return FeatureFlagValue.UNSUPPORTED;
}
Boolean featureFlagValue = isFeatureEnabled(context, feature);
if (featureFlagValue == null) {
Logger.logError(LOG_TAG, "Failed to get feature flags \"" + feature + "\" value");
return FeatureFlagValue.UNKNOWN;
} else {
return featureFlagValue ? FeatureFlagValue.TRUE : FeatureFlagValue.FALSE;
}
}
/**
* Check if a feature flag exists.
*
* @param context The {@link Context} for operations.
* @param feature The {@link String} name for feature.
* @return Returns {@code true} if flag exists, otherwise {@code false}. This will be
* {@code null} if an exception is raised.
*/
@Nullable
public static Boolean isFeatureEnabled(@NonNull Context context, @NonNull String feature) {
ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
try {
@SuppressLint("PrivateApi") Class<?> clazz = Class.forName(FEATURE_FLAGS_CLASS);
Method isFeatureEnabledMethod = ReflectionUtils.getDeclaredMethod(clazz, "isEnabled", Context.class, String.class);
if (isFeatureEnabledMethod == null) {
Logger.logError(LOG_TAG, "Failed to check if feature flag \"" + feature + "\" is enabled");
return null;
}
return (boolean) ReflectionUtils.invokeMethod(isFeatureEnabledMethod, null, context, feature).value;
} catch (Exception e) {
// ClassCastException may be thrown
Logger.logStackTraceWithMessage(LOG_TAG, "Failed to check if feature flag \"" + feature + "\" is enabled", e);
return null;
}
}
}

Xet Storage Details

Size:
7.85 kB
·
Xet hash:
5da9f48952177d8be2e37e01cfd69f6e2c49891437d9e0d51a23c0cc1edfcb46

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.