1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm.utils; 18 19 import static java.lang.Boolean.FALSE; 20 import static java.lang.Boolean.TRUE; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.content.pm.PackageManager; 25 import android.util.Slog; 26 27 import java.util.function.BooleanSupplier; 28 29 /** 30 * Utility class which helps with handling with properties to opt-in or 31 * opt-out a specific feature. 32 */ 33 public class OptPropFactory { 34 35 @NonNull 36 private final PackageManager mPackageManager; 37 38 @NonNull 39 private final String mPackageName; 40 41 /** 42 * Object responsible to handle optIn and optOut properties. 43 * 44 * @param packageManager The PackageManager reference 45 * @param packageName The name of the package. 46 */ OptPropFactory(@onNull PackageManager packageManager, @NonNull String packageName)47 public OptPropFactory(@NonNull PackageManager packageManager, @NonNull String packageName) { 48 mPackageManager = packageManager; 49 mPackageName = packageName; 50 } 51 52 /** 53 * Creates an OptProp for the given property 54 * 55 * @param propertyName The name of the property. 56 * @return The OptProp for the given property 57 */ 58 @NonNull create(@onNull String propertyName)59 public OptProp create(@NonNull String propertyName) { 60 return OptProp.create( 61 () -> mPackageManager.getProperty(propertyName, mPackageName).getBoolean(), 62 propertyName); 63 } 64 65 /** 66 * Creates an OptProp for the given property behind a gate condition. 67 * 68 * @param propertyName The name of the property. 69 * @param gateCondition If this resolves to false, the property is unset. This is evaluated at 70 * every interaction with the OptProp. 71 * @return The OptProp for the given property 72 */ 73 @NonNull create(@onNull String propertyName, @NonNull BooleanSupplier gateCondition)74 public OptProp create(@NonNull String propertyName, @NonNull BooleanSupplier gateCondition) { 75 return OptProp.create( 76 () -> mPackageManager.getProperty(propertyName, mPackageName).getBoolean(), 77 propertyName, 78 gateCondition); 79 } 80 81 @FunctionalInterface 82 private interface ThrowableBooleanSupplier { get()83 boolean get() throws Exception; 84 } 85 86 public static class OptProp { 87 88 private static final int VALUE_UNSET = -2; 89 private static final int VALUE_UNDEFINED = -1; 90 private static final int VALUE_FALSE = 0; 91 private static final int VALUE_TRUE = 1; 92 93 @IntDef(prefix = {"VALUE_"}, value = { 94 VALUE_UNSET, 95 VALUE_UNDEFINED, 96 VALUE_FALSE, 97 VALUE_TRUE, 98 }) 99 @interface OptionalValue {} 100 101 private static final String TAG = "OptProp"; 102 103 // The condition is evaluated every time the OptProp state is accessed. 104 @NonNull 105 private final BooleanSupplier mCondition; 106 107 // This is evaluated only once in the lifetime of an OptProp. 108 @NonNull 109 private final ThrowableBooleanSupplier mValueSupplier; 110 111 @NonNull 112 private final String mPropertyName; 113 114 @OptionalValue 115 private int mValue = VALUE_UNDEFINED; 116 OptProp(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName, @NonNull BooleanSupplier condition)117 private OptProp(@NonNull ThrowableBooleanSupplier valueSupplier, 118 @NonNull String propertyName, 119 @NonNull BooleanSupplier condition) { 120 mValueSupplier = valueSupplier; 121 mPropertyName = propertyName; 122 mCondition = condition; 123 } 124 125 @NonNull create(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName)126 private static OptProp create(@NonNull ThrowableBooleanSupplier valueSupplier, 127 @NonNull String propertyName) { 128 return new OptProp(valueSupplier, propertyName, () -> true); 129 } 130 131 @NonNull create(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName, @NonNull BooleanSupplier condition)132 private static OptProp create(@NonNull ThrowableBooleanSupplier valueSupplier, 133 @NonNull String propertyName, @NonNull BooleanSupplier condition) { 134 return new OptProp(valueSupplier, propertyName, condition); 135 } 136 137 /** 138 * @return {@code true} when the guarding condition is {@code true} and the property has 139 * been explicitly set to {@code true}. {@code false} otherwise. The guarding condition is 140 * evaluated every time this method is invoked. 141 */ isTrue()142 public boolean isTrue() { 143 return mCondition.getAsBoolean() && getValue() == VALUE_TRUE; 144 } 145 146 /** 147 * @return {@code true} when the guarding condition is {@code true} and the property has 148 * been explicitly set to {@code false}. {@code false} otherwise. The guarding condition is 149 * evaluated every time this method is invoked. 150 */ isFalse()151 public boolean isFalse() { 152 return mCondition.getAsBoolean() && getValue() == VALUE_FALSE; 153 } 154 155 /** 156 * Returns {@code true} when the following conditions are met: 157 * <ul> 158 * <li>{@code gatingCondition} doesn't evaluate to {@code false} 159 * <li>App developers didn't opt out with a component {@code property} 160 * <li>App developers opted in with a component {@code property} or an OEM opted in with 161 * a per-app override 162 * </ul> 163 * 164 * <p>This is used for the treatments that are enabled only on per-app basis. 165 */ shouldEnableWithOverrideAndProperty(boolean overrideValue)166 public boolean shouldEnableWithOverrideAndProperty(boolean overrideValue) { 167 if (!mCondition.getAsBoolean()) { 168 return false; 169 } 170 if (getValue() == VALUE_FALSE) { 171 return false; 172 } 173 return getValue() == VALUE_TRUE || overrideValue; 174 } 175 176 /** 177 * Returns {@code true} when the following conditions are met: 178 * <ul> 179 * <li>{@code gatingCondition} doesn't evaluate to {@code false} 180 * <li>App developers didn't opt out with a component {@code property} 181 * <li>OEM opted in with a per-app override 182 * </ul> 183 * 184 * <p>This is used for the treatments that are enabled based with the heuristic but can be 185 * disabled on per-app basis by OEMs or app developers. 186 */ shouldEnableWithOptInOverrideAndOptOutProperty( boolean overrideValue)187 public boolean shouldEnableWithOptInOverrideAndOptOutProperty( 188 boolean overrideValue) { 189 if (!mCondition.getAsBoolean()) { 190 return false; 191 } 192 return getValue() != VALUE_FALSE && overrideValue; 193 } 194 195 /** 196 * Returns {@code true} when the following conditions are met: 197 * <ul> 198 * <li>{@code gatingCondition} doesn't resolve to {@code false} 199 * <li>OEM didn't opt out with a per-app override 200 * <li>App developers didn't opt out with a component {@code property} 201 * </ul> 202 * 203 * <p>This is used for the treatments that are enabled based with the heuristic but can be 204 * disabled on per-app basis by OEMs or app developers. 205 */ shouldEnableWithOptOutOverrideAndProperty(boolean overrideValue)206 public boolean shouldEnableWithOptOutOverrideAndProperty(boolean overrideValue) { 207 if (!mCondition.getAsBoolean()) { 208 return false; 209 } 210 return getValue() != VALUE_FALSE && !overrideValue; 211 } 212 213 @OptionalValue getValue()214 private int getValue() { 215 if (mValue == VALUE_UNDEFINED) { 216 try { 217 final Boolean value = mValueSupplier.get(); 218 if (TRUE.equals(value)) { 219 mValue = VALUE_TRUE; 220 } else if (FALSE.equals(value)) { 221 mValue = VALUE_FALSE; 222 } else { 223 mValue = VALUE_UNSET; 224 } 225 } catch (Exception e) { 226 Slog.w(TAG, "Cannot read opt property " + mPropertyName); 227 mValue = VALUE_UNSET; 228 } 229 } 230 return mValue; 231 } 232 } 233 } 234