1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base; 6 7 import org.jni_zero.JNINamespace; 8 import org.jni_zero.NativeMethods; 9 10 import java.util.Collections; 11 import java.util.HashMap; 12 import java.util.Map; 13 14 /** 15 * Java accessor for state of feature flags and their field trial parameters. 16 * 17 * This class provides methods to access values of feature flags listed in a native feature list 18 * and to access their field trial parameters. 19 * 20 * This class needs to be derived for each native feature list (such as a component's feature list) 21 * and the derived class must implement the abstract {@link #getNativeMap()} by calling a JNI method 22 * to get the pointer to the base::android::FeatureMap. The derived class will provide Java code 23 * access to the list of base::Features passed to the base::android::FeatureMap. 24 */ 25 @JNINamespace("base::android") 26 public abstract class FeatureMap { 27 private long mNativeMapPtr; 28 FeatureMap()29 protected FeatureMap() {} 30 31 /** 32 * Should return the native pointer to the specific base::FeatureMap for the component/layer. 33 */ getNativeMap()34 protected abstract long getNativeMap(); 35 36 /** 37 * Returns whether the specified feature is enabled or not. 38 * 39 * Calling this has the side effect of bucketing this client, which may cause an experiment to 40 * be marked as active. 41 * 42 * Should be called only after native is loaded. If {@link FeatureList#isInitialized()} returns 43 * true, this method is safe to call. In tests, this will return any values set through 44 * {@link FeatureList#setTestFeatures(Map)}, even before native is loaded. 45 * 46 * @param featureName The name of the feature to query. 47 * @return Whether the feature is enabled or not. 48 */ isEnabledInNative(String featureName)49 public boolean isEnabledInNative(String featureName) { 50 Boolean testValue = FeatureList.getTestValueForFeature(featureName); 51 if (testValue != null) return testValue; 52 ensureNativeMapInit(); 53 return FeatureMapJni.get().isEnabled(mNativeMapPtr, featureName); 54 } 55 56 /** 57 * Returns a field trial param for the specified feature. 58 * 59 * @param featureName The name of the feature to retrieve a param for. 60 * @param paramName The name of the param for which to get as an integer. 61 * @return The parameter value as a String. The string is empty if the feature does not exist or 62 * the specified parameter does not exist. 63 */ getFieldTrialParamByFeature(String featureName, String paramName)64 public String getFieldTrialParamByFeature(String featureName, String paramName) { 65 String testValue = FeatureList.getTestValueForFieldTrialParam(featureName, paramName); 66 if (testValue != null) return testValue; 67 if (FeatureList.hasTestFeatures()) return ""; 68 ensureNativeMapInit(); 69 return FeatureMapJni.get() 70 .getFieldTrialParamByFeature(mNativeMapPtr, featureName, paramName); 71 } 72 73 /** 74 * Returns a field trial param as a boolean for the specified feature. 75 * 76 * @param featureName The name of the feature to retrieve a param for. 77 * @param paramName The name of the param for which to get as an integer. 78 * @param defaultValue The boolean value to use if the param is not available. 79 * @return The parameter value as a boolean. Default value if the feature does not exist or the 80 * specified parameter does not exist or its string value is neither "true" nor "false". 81 */ getFieldTrialParamByFeatureAsBoolean( String featureName, String paramName, boolean defaultValue)82 public boolean getFieldTrialParamByFeatureAsBoolean( 83 String featureName, String paramName, boolean defaultValue) { 84 String testValue = FeatureList.getTestValueForFieldTrialParam(featureName, paramName); 85 if (testValue != null) return Boolean.valueOf(testValue); 86 if (FeatureList.hasTestFeatures()) return defaultValue; 87 ensureNativeMapInit(); 88 return FeatureMapJni.get() 89 .getFieldTrialParamByFeatureAsBoolean( 90 mNativeMapPtr, featureName, paramName, defaultValue); 91 } 92 93 /** 94 * Returns a field trial param as an int for the specified feature. 95 * 96 * @param featureName The name of the feature to retrieve a param for. 97 * @param paramName The name of the param for which to get as an integer. 98 * @param defaultValue The integer value to use if the param is not available. 99 * @return The parameter value as an int. Default value if the feature does not exist or the 100 * specified parameter does not exist or its string value does not represent an int. 101 */ getFieldTrialParamByFeatureAsInt( String featureName, String paramName, int defaultValue)102 public int getFieldTrialParamByFeatureAsInt( 103 String featureName, String paramName, int defaultValue) { 104 String testValue = FeatureList.getTestValueForFieldTrialParam(featureName, paramName); 105 if (testValue != null) return Integer.valueOf(testValue); 106 if (FeatureList.hasTestFeatures()) return defaultValue; 107 ensureNativeMapInit(); 108 return FeatureMapJni.get() 109 .getFieldTrialParamByFeatureAsInt( 110 mNativeMapPtr, featureName, paramName, defaultValue); 111 } 112 113 /** 114 * Returns a field trial param as a double for the specified feature. 115 * 116 * @param featureName The name of the feature to retrieve a param for. 117 * @param paramName The name of the param for which to get as an integer. 118 * @param defaultValue The double value to use if the param is not available. 119 * @return The parameter value as a double. Default value if the feature does not exist or the 120 * specified parameter does not exist or its string value does not represent a double. 121 */ getFieldTrialParamByFeatureAsDouble( String featureName, String paramName, double defaultValue)122 public double getFieldTrialParamByFeatureAsDouble( 123 String featureName, String paramName, double defaultValue) { 124 String testValue = FeatureList.getTestValueForFieldTrialParam(featureName, paramName); 125 if (testValue != null) return Double.valueOf(testValue); 126 if (FeatureList.hasTestFeatures()) return defaultValue; 127 ensureNativeMapInit(); 128 return FeatureMapJni.get() 129 .getFieldTrialParamByFeatureAsDouble( 130 mNativeMapPtr, featureName, paramName, defaultValue); 131 } 132 133 /** Returns all the field trial parameters for the specified feature. */ getFieldTrialParamsForFeature(String featureName)134 public Map<String, String> getFieldTrialParamsForFeature(String featureName) { 135 Map<String, String> testValues = 136 FeatureList.getTestValuesForAllFieldTrialParamsForFeature(featureName); 137 if (testValues != null) return testValues; 138 if (FeatureList.hasTestFeatures()) return Collections.emptyMap(); 139 140 ensureNativeMapInit(); 141 Map<String, String> result = new HashMap<>(); 142 String[] flattenedParams = 143 FeatureMapJni.get() 144 .getFlattedFieldTrialParamsForFeature(mNativeMapPtr, featureName); 145 for (int i = 0; i < flattenedParams.length; i += 2) { 146 result.put(flattenedParams[i], flattenedParams[i + 1]); 147 } 148 return result; 149 } 150 151 /** Create a {@link MutableFlagWithSafeDefault} in this FeatureMap. */ mutableFlagWithSafeDefault( String featureName, boolean defaultValue)152 public MutableFlagWithSafeDefault mutableFlagWithSafeDefault( 153 String featureName, boolean defaultValue) { 154 return new MutableFlagWithSafeDefault(this, featureName, defaultValue); 155 } 156 ensureNativeMapInit()157 private void ensureNativeMapInit() { 158 assert FeatureList.isNativeInitialized(); 159 160 if (mNativeMapPtr == 0) { 161 mNativeMapPtr = getNativeMap(); 162 assert mNativeMapPtr != 0; 163 } 164 } 165 166 @NativeMethods 167 interface Natives { isEnabled(long featureMap, String featureName)168 boolean isEnabled(long featureMap, String featureName); 169 getFieldTrialParamByFeature(long featureMap, String featureName, String paramName)170 String getFieldTrialParamByFeature(long featureMap, String featureName, String paramName); 171 getFieldTrialParamByFeatureAsInt( long featureMap, String featureName, String paramName, int defaultValue)172 int getFieldTrialParamByFeatureAsInt( 173 long featureMap, String featureName, String paramName, int defaultValue); 174 getFieldTrialParamByFeatureAsDouble( long featureMap, String featureName, String paramName, double defaultValue)175 double getFieldTrialParamByFeatureAsDouble( 176 long featureMap, String featureName, String paramName, double defaultValue); 177 getFieldTrialParamByFeatureAsBoolean( long featureMap, String featureName, String paramName, boolean defaultValue)178 boolean getFieldTrialParamByFeatureAsBoolean( 179 long featureMap, String featureName, String paramName, boolean defaultValue); 180 getFlattedFieldTrialParamsForFeature(long featureMap, String featureName)181 String[] getFlattedFieldTrialParamsForFeature(long featureMap, String featureName); 182 } 183 } 184