termux-app

Форк
0
169 строк · 7.7 Кб
1
package com.termux.shared.android;
2

3
import android.annotation.SuppressLint;
4
import android.content.Context;
5

6
import androidx.annotation.NonNull;
7
import androidx.annotation.Nullable;
8

9
import com.termux.shared.logger.Logger;
10
import com.termux.shared.reflection.ReflectionUtils;
11

12
import java.lang.reflect.Method;
13
import java.util.Map;
14

15
/**
16
 * Utils for Developer Options -> Feature Flags. The page won't show in user/production builds and
17
 * is only shown in userdebug builds.
18
 * https://cs.android.com/android/_/android/platform/frameworks/base/+/09dcdad5ebc159861920f090e07da60fac71ac0a:core/java/android/util/FeatureFlagUtils.java
19
 * 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
20
 *
21
 * The feature flags value can be modified in two ways.
22
 *
23
 * 1. sysprops with `setprop` command with root. Will be unset by default.
24
 *  Set value: `setprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs false`
25
 *  Get value: `getprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs`
26
 *  Unset value: `setprop persist.sys.fflag.override.settings_enable_monitor_phantom_procs ""`
27
 * Running `setprop` command requires root and even adb `shell` user cannot modify the values
28
 * since selinux will not allow it by default. Some props like `settings_dynamic_system` can be
29
 * set since they are exempted for `shell` in sepolicy.
30
 *
31
 * 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
32
 * [ 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'
33
 *
34
 * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:system/sepolicy/private/property_contexts;l=71
35
 * https://cs.android.com/android/platform/superproject/+/android-12.0.0_r4:system/sepolicy/private/shell.te;l=149
36
 *
37
 * 2. settings global list with adb or root. Will be unset by default. This takes precedence over
38
 * sysprop value since `FeatureFlagUtils.isEnabled()`
39
 * checks its value first. Override precedence: Settings.Global -> sys.fflag.override.* -> static list.
40
 * Set value: `adb shell settings put global settings_enable_monitor_phantom_procs false`
41
 * Get value: adb shell settings get global settings_enable_monitor_phantom_procs`
42
 * Unset value: `adb shell settings delete global settings_enable_monitor_phantom_procs`
43
 *
44
 * https://cs.android.com/android/_/android/platform/frameworks/base/+/refs/tags/android-12.0.0_r31:core/java/android/util/FeatureFlagUtils.java;l=113
45
 *
46
 * The feature flag values can be modified in user builds with settings global list, but since the
47
 * developer options feature flags page is not shown and considering that getprop values for features
48
 * will be unset by default and settings global list will not be set either and there is no shell API,
49
 * it will require an android app process to check if feature is supported on a device and what its
50
 * default value is with reflection after bypassing hidden api restrictions since {@link #FEATURE_FLAGS_CLASS}
51
 * is annotated as `@hide`.
52
 */
53
public class FeatureFlagUtils {
54

55
    public enum FeatureFlagValue {
56

57
        /** Unknown like due to exception raised while getting value. */
58
        UNKNOWN("<unknown>"),
59

60
        /** Flag is unsupported on current android build. */
61
        UNSUPPORTED("<unsupported>"),
62

63
        /** Flag is enabled. */
64
        TRUE("true"),
65

66
        /** Flag is not enabled. */
67
        FALSE("false");
68

69
        private final String name;
70

71
        FeatureFlagValue(final String name) {
72
            this.name = name;
73
        }
74

75
        public String getName() {
76
            return name;
77
        }
78

79
    }
80

81
    public static final String FEATURE_FLAGS_CLASS = "android.util.FeatureFlagUtils";
82

83
    private static final String LOG_TAG = "FeatureFlagUtils";
84

85
    /**
86
     * Get all feature flags in their raw form.
87
     */
88
    @SuppressWarnings("unchecked")
89
    public static Map<String, String> getAllFeatureFlags() {
90
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
91
        try {
92
            @SuppressLint("PrivateApi") Class<?> clazz = Class.forName(FEATURE_FLAGS_CLASS);
93
            Method getAllFeatureFlagsMethod = ReflectionUtils.getDeclaredMethod(clazz, "getAllFeatureFlags");
94
            if (getAllFeatureFlagsMethod == null) return null;
95
            return (Map<String, String>) ReflectionUtils.invokeMethod(getAllFeatureFlagsMethod, null).value;
96
        } catch (Exception e) {
97
            // ClassCastException may be thrown
98
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to get all feature flags", e);
99
            return null;
100
        }
101
    }
102

103
    /**
104
     * Check if a feature flag exists.
105
     *
106
     * @return Returns {@code true} if flag exists, otherwise {@code false}. This will be
107
     * {@code null} if an exception is raised.
108
     */
109
    @Nullable
110
    public static Boolean featureFlagExists(@NonNull String feature) {
111
        Map<String, String> featureFlags = getAllFeatureFlags();
112
        if (featureFlags == null) return null;
113
        return featureFlags.containsKey(feature);
114
    }
115

116
    /**
117
     * Get {@link FeatureFlagValue} for a feature.
118
     *
119
     * @param context The {@link Context} for operations.
120
     * @param feature The {@link String} name for feature.
121
     * @return Returns {@link FeatureFlagValue}.
122
     */
123
    @NonNull
124
    public static FeatureFlagValue getFeatureFlagValueString(@NonNull Context context, @NonNull String feature) {
125
        Boolean featureFlagExists = featureFlagExists(feature);
126
        if (featureFlagExists == null) {
127
            Logger.logError(LOG_TAG, "Failed to get feature flags \"" + feature + "\" value");
128
            return FeatureFlagValue.UNKNOWN;
129
        } else if (!featureFlagExists) {
130
            return FeatureFlagValue.UNSUPPORTED;
131
        }
132

133
        Boolean featureFlagValue = isFeatureEnabled(context, feature);
134
        if (featureFlagValue == null) {
135
            Logger.logError(LOG_TAG, "Failed to get feature flags \"" + feature + "\" value");
136
            return FeatureFlagValue.UNKNOWN;
137
        } else {
138
            return featureFlagValue ? FeatureFlagValue.TRUE : FeatureFlagValue.FALSE;
139
        }
140
    }
141

142
    /**
143
     * Check if a feature flag exists.
144
     *
145
     * @param context The {@link Context} for operations.
146
     * @param feature The {@link String} name for feature.
147
     * @return Returns {@code true} if flag exists, otherwise {@code false}. This will be
148
     * {@code null} if an exception is raised.
149
     */
150
    @Nullable
151
    public static Boolean isFeatureEnabled(@NonNull Context context, @NonNull String feature) {
152
        ReflectionUtils.bypassHiddenAPIReflectionRestrictions();
153
        try {
154
            @SuppressLint("PrivateApi") Class<?> clazz = Class.forName(FEATURE_FLAGS_CLASS);
155
            Method isFeatureEnabledMethod = ReflectionUtils.getDeclaredMethod(clazz, "isEnabled", Context.class, String.class);
156
            if (isFeatureEnabledMethod == null) {
157
                Logger.logError(LOG_TAG, "Failed to check if feature flag \"" + feature + "\" is enabled");
158
                return null;
159
            }
160

161
            return (boolean) ReflectionUtils.invokeMethod(isFeatureEnabledMethod, null, context, feature).value;
162
        } catch (Exception e) {
163
            // ClassCastException may be thrown
164
            Logger.logStackTraceWithMessage(LOG_TAG, "Failed to check if feature flag \"" + feature + "\" is enabled", e);
165
            return null;
166
        }
167
    }
168

169
}
170

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.