• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.UserIdInt;
25 import android.content.pm.PackageManager;
26 import android.util.Slog;
27 
28 import java.util.function.BooleanSupplier;
29 
30 /**
31  * Utility class which helps with handling with properties to opt-in or
32  * opt-out a specific feature.
33  */
34 public class OptPropFactory {
35 
36     @NonNull
37     private final PackageManager mPackageManager;
38 
39     @NonNull
40     private final String mPackageName;
41 
42     @UserIdInt
43     private final int mUserId;
44 
45     /**
46      * Object responsible to handle optIn and optOut properties.
47      *
48      * @param packageManager The PackageManager reference
49      * @param packageName    The name of the package.
50      */
OptPropFactory(@onNull PackageManager packageManager, @NonNull String packageName, @UserIdInt int userId)51     public OptPropFactory(@NonNull PackageManager packageManager, @NonNull String packageName,
52             @UserIdInt int userId) {
53         mPackageManager = packageManager;
54         mPackageName = packageName;
55         mUserId = userId;
56     }
57 
58     /**
59      * Creates an OptProp for the given property
60      *
61      * @param propertyName The name of the property.
62      * @return The OptProp for the given property
63      */
64     @NonNull
create(@onNull String propertyName)65     public OptProp create(@NonNull String propertyName) {
66         return OptProp.create(
67                 () -> mPackageManager.getPropertyAsUser(propertyName, mPackageName,
68                         null /* className */, mUserId).getBoolean(),
69                 propertyName);
70     }
71 
72     /**
73      * Creates an OptProp for the given property behind a gate condition.
74      *
75      * @param propertyName  The name of the property.
76      * @param gateCondition If this resolves to false, the property is unset. This is evaluated at
77      *                      every interaction with the OptProp.
78      * @return The OptProp for the given property
79      */
80     @NonNull
create(@onNull String propertyName, @NonNull BooleanSupplier gateCondition)81     public OptProp create(@NonNull String propertyName, @NonNull BooleanSupplier gateCondition) {
82         return OptProp.create(
83                 () -> mPackageManager.getPropertyAsUser(propertyName, mPackageName,
84                         null /* className */, mUserId).getBoolean(),
85                 propertyName,
86                 gateCondition);
87     }
88 
89     @FunctionalInterface
90     private interface ThrowableBooleanSupplier {
get()91         boolean get() throws Exception;
92     }
93 
94     public static class OptProp {
95 
96         private static final int VALUE_UNSET = -2;
97         private static final int VALUE_UNDEFINED = -1;
98         private static final int VALUE_FALSE = 0;
99         private static final int VALUE_TRUE = 1;
100 
101         @IntDef(prefix = {"VALUE_"}, value = {
102                 VALUE_UNSET,
103                 VALUE_UNDEFINED,
104                 VALUE_FALSE,
105                 VALUE_TRUE,
106         })
107         @interface OptionalValue {}
108 
109         private static final String TAG = "OptProp";
110 
111         // The condition is evaluated every time the OptProp state is accessed.
112         @NonNull
113         private final BooleanSupplier mCondition;
114 
115         // This is evaluated only once in the lifetime of an OptProp.
116         @NonNull
117         private final ThrowableBooleanSupplier mValueSupplier;
118 
119         @NonNull
120         private final String mPropertyName;
121 
122         @OptionalValue
123         private int mValue = VALUE_UNDEFINED;
124 
OptProp(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName, @NonNull BooleanSupplier condition)125         private OptProp(@NonNull ThrowableBooleanSupplier valueSupplier,
126                 @NonNull String propertyName,
127                 @NonNull BooleanSupplier condition) {
128             mValueSupplier = valueSupplier;
129             mPropertyName = propertyName;
130             mCondition = condition;
131         }
132 
133         @NonNull
create(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName)134         private static OptProp create(@NonNull ThrowableBooleanSupplier valueSupplier,
135                 @NonNull String propertyName) {
136             return new OptProp(valueSupplier, propertyName, () -> true);
137         }
138 
139         @NonNull
create(@onNull ThrowableBooleanSupplier valueSupplier, @NonNull String propertyName, @NonNull BooleanSupplier condition)140         private static OptProp create(@NonNull ThrowableBooleanSupplier valueSupplier,
141                 @NonNull String propertyName, @NonNull BooleanSupplier condition) {
142             return new OptProp(valueSupplier, propertyName, condition);
143         }
144 
145         /**
146          * @return {@code true} when the guarding condition is {@code true} and the property has
147          * been explicitly set to {@code true}. {@code false} otherwise. The guarding condition is
148          * evaluated every time this method is invoked.
149          */
isTrue()150         public boolean isTrue() {
151             return mCondition.getAsBoolean() && getValue() == VALUE_TRUE;
152         }
153 
154         /**
155          * @return {@code true} when the guarding condition is {@code true} and the property has
156          * been explicitly set to {@code false}. {@code false} otherwise. The guarding condition is
157          * evaluated every time this method is invoked.
158          */
isFalse()159         public boolean isFalse() {
160             return mCondition.getAsBoolean() && getValue() == VALUE_FALSE;
161         }
162 
163         /**
164          * Returns {@code true} when the following conditions are met:
165          * <ul>
166          *     <li>{@code gatingCondition} doesn't evaluate to {@code false}
167          *     <li>App developers didn't opt out with a component {@code property}
168          *     <li>App developers opted in with a component {@code property} or an OEM opted in with
169          *     a per-app override
170          * </ul>
171          *
172          * <p>This is used for the treatments that are enabled only on per-app basis.
173          */
shouldEnableWithOverrideAndProperty(boolean overrideValue)174         public boolean shouldEnableWithOverrideAndProperty(boolean overrideValue) {
175             if (!mCondition.getAsBoolean()) {
176                 return false;
177             }
178             if (getValue() == VALUE_FALSE) {
179                 return false;
180             }
181             return getValue() == VALUE_TRUE || overrideValue;
182         }
183 
184         /**
185          * Returns {@code true} when the following conditions are met:
186          * <ul>
187          *     <li>{@code gatingCondition} doesn't evaluate to {@code false}
188          *     <li>App developers didn't opt out with a component {@code property}
189          *     <li>OEM opted in with a per-app override
190          * </ul>
191          *
192          * <p>This is used for the treatments that are enabled based with the heuristic but can be
193          * disabled on per-app basis by OEMs or app developers.
194          */
shouldEnableWithOptInOverrideAndOptOutProperty( boolean overrideValue)195         public boolean shouldEnableWithOptInOverrideAndOptOutProperty(
196                 boolean overrideValue) {
197             if (!mCondition.getAsBoolean()) {
198                 return false;
199             }
200             return getValue() != VALUE_FALSE && overrideValue;
201         }
202 
203         /**
204          * Returns {@code true} when the following conditions are met:
205          * <ul>
206          *     <li>{@code gatingCondition} doesn't resolve to {@code false}
207          *     <li>OEM didn't opt out with a per-app override
208          *     <li>App developers didn't opt out with a component {@code property}
209          * </ul>
210          *
211          * <p>This is used for the treatments that are enabled based with the heuristic but can be
212          * disabled on per-app basis by OEMs or app developers.
213          */
shouldEnableWithOptOutOverrideAndProperty(boolean overrideValue)214         public boolean shouldEnableWithOptOutOverrideAndProperty(boolean overrideValue) {
215             if (!mCondition.getAsBoolean()) {
216                 return false;
217             }
218             return getValue() != VALUE_FALSE && !overrideValue;
219         }
220 
221         @OptionalValue
getValue()222         private int getValue() {
223             if (mValue == VALUE_UNDEFINED) {
224                 try {
225                     final Boolean value = mValueSupplier.get();
226                     if (TRUE.equals(value)) {
227                         mValue = VALUE_TRUE;
228                     } else if (FALSE.equals(value)) {
229                         mValue = VALUE_FALSE;
230                     } else {
231                         mValue = VALUE_UNSET;
232                     }
233                 } catch (Exception e) {
234                     Slog.w(TAG, "Cannot read opt property " + mPropertyName);
235                     mValue = VALUE_UNSET;
236                 }
237             }
238             return mValue;
239         }
240     }
241 }
242