1 /*
2  * Copyright (C) 2023 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.ondevicepersonalization.services;
18 
19 import android.annotation.NonNull;
20 import android.provider.DeviceConfig;
21 import com.android.modules.utils.build.SdkLevel;
22 import java.util.HashMap;
23 import java.util.Map;
24 
25 /** Flags Implementation that delegates to DeviceConfig. */
26 public final class PhFlags implements Flags {
27     /*
28      * Keys for ALL the flags stored in DeviceConfig.
29      */
30     // Killswitch keys
31     public static final String KEY_GLOBAL_KILL_SWITCH = "global_kill_switch";
32 
33     public static final String KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE =
34             "enable_personalization_status_override";
35 
36     public static final String KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE =
37             "personalization_status_override_value";
38 
39     public static final String KEY_ISOLATED_SERVICE_DEADLINE_SECONDS =
40             "isolated_service_deadline_seconds";
41 
42     public static final String KEY_APP_REQUEST_FLOW_DEADLINE_SECONDS =
43             "app_request_flow_deadline_seconds";
44 
45     public static final String KEY_RENDER_FLOW_DEADLINE_SECONDS =
46             "render_flow_deadline_seconds";
47 
48     public static final String KEY_WEB_VIEW_FLOW_DEADLINE_SECONDS =
49             "web_view_flow_deadline_seconds";
50 
51     public static final String KEY_WEB_TRIGGER_FLOW_DEADLINE_SECONDS =
52             "web_trigger_flow_deadline_seconds";
53 
54     public static final String KEY_TRUSTED_PARTNER_APPS_LIST = "trusted_partner_apps_list";
55 
56     public static final String KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED =
57             "shared_isolated_process_feature_enabled";
58 
59     public static final String KEY_CALLER_APP_ALLOW_LIST = "caller_app_allow_list";
60 
61     public static final String KEY_ISOLATED_SERVICE_ALLOW_LIST = "isolated_service_allow_list";
62 
63     public static final String KEY_OUTPUT_DATA_ALLOW_LIST = "output_data_allow_list";
64 
65     public static final String KEY_USER_CONTROL_CACHE_IN_MILLIS =
66             "user_control_cache_duration_millis";
67 
68     public static final String KEY_ODP_ENABLE_CLIENT_ERROR_LOGGING =
69             "odp_enable_client_error_logging";
70 
71     public static final String KEY_ODP_BACKGROUND_JOBS_LOGGING_ENABLED =
72             "odp_background_jobs_logging_enabled";
73 
74     public static final String KEY_ODP_BACKGROUND_JOB_SAMPLING_LOGGING_RATE =
75             "odp_background_job_sampling_logging_rate";
76 
77     public static final String KEY_ODP_JOB_SCHEDULING_LOGGING_ENABLED =
78             "odp_job_scheduling_logging_enabled";
79 
80     public static final String KEY_ODP_JOB_SCHEDULING_LOGGING_SAMPLING_RATE =
81             "odp_job_scheduling_logging_sampling_rate";
82 
83     public static final String KEY_ODP_MODULE_JOB_POLICY = "odp_module_job_policy";
84 
85     public static final String KEY_ODP_SPE_PILOT_JOB_ENABLED = "odp_spe_pilot_job_enabled";
86 
87     public static final String KEY_IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED =
88             "is_art_image_loading_optimization_enabled";
89 
90     public static final String KEY_ISOLATED_SERVICE_DEBUGGING_ENABLED =
91             "isolated_service_debugging_enabled";
92 
93     public static final String KEY_RESET_DATA_DELAY_SECONDS = "reset_data_delay_seconds";
94 
95     public static final String KEY_RESET_DATA_DEADLINE_SECONDS = "reset_data_deadline_seconds";
96 
97     public static final String APP_INSTALL_HISTORY_TTL = "app_install_history_ttl";
98 
99     // OnDevicePersonalization Namespace String from DeviceConfig class
100     public static final String NAMESPACE_ON_DEVICE_PERSONALIZATION = "on_device_personalization";
101 
102     private final Map<String, Object> mStableFlags = new HashMap<>();
103 
PhFlags()104     PhFlags() {
105         // This is only called onece so stable flags require process restart to be reset.
106         setStableFlags();
107     }
108 
109     /** Returns the singleton instance of the PhFlags. */
110     @NonNull
getInstance()111     public static PhFlags getInstance() {
112         return PhFlagsLazyInstanceHolder.sSingleton;
113     }
114 
115     private static class PhFlagsLazyInstanceHolder {
116         private static final PhFlags sSingleton = new PhFlags();
117     }
118 
119     /** Sets the stable flag map. */
setStableFlags()120     public void setStableFlags() {
121         mStableFlags.put(KEY_APP_REQUEST_FLOW_DEADLINE_SECONDS,
122                 getAppRequestFlowDeadlineSeconds());
123         mStableFlags.put(KEY_RENDER_FLOW_DEADLINE_SECONDS,
124                 getRenderFlowDeadlineSeconds());
125         mStableFlags.put(KEY_WEB_TRIGGER_FLOW_DEADLINE_SECONDS,
126                 getWebTriggerFlowDeadlineSeconds());
127         mStableFlags.put(KEY_WEB_VIEW_FLOW_DEADLINE_SECONDS,
128                 getWebViewFlowDeadlineSeconds());
129         mStableFlags.put(KEY_EXAMPLE_STORE_FLOW_DEADLINE_SECONDS,
130                 getExampleStoreFlowDeadlineSeconds());
131         mStableFlags.put(KEY_DOWNLOAD_FLOW_DEADLINE_SECONDS,
132                 getDownloadFlowDeadlineSeconds());
133         mStableFlags.put(KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED,
134                 isSharedIsolatedProcessFeatureEnabled());
135         mStableFlags.put(KEY_TRUSTED_PARTNER_APPS_LIST,
136                 getTrustedPartnerAppsList());
137         mStableFlags.put(KEY_IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED,
138                 isArtImageLoadingOptimizationEnabled());
139         mStableFlags.put(KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE,
140                 isPersonalizationStatusOverrideEnabled());
141         mStableFlags.put(KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE,
142                 getPersonalizationStatusOverrideValue());
143         mStableFlags.put(KEY_USER_CONTROL_CACHE_IN_MILLIS,
144                 getUserControlCacheInMillis());
145     }
146 
147     /** Gets a stable flag value based on flag name. */
getStableFlag(String flagName)148     public Object getStableFlag(String flagName) {
149         if (!mStableFlags.containsKey(flagName)) {
150             throw new IllegalArgumentException("Flag " + flagName + " is not stable.");
151         }
152         return mStableFlags.get(flagName);
153     }
154 
155     // Group of All Killswitches
156     @Override
getGlobalKillSwitch()157     public boolean getGlobalKillSwitch() {
158         // The priority of applying the flag values: PH (DeviceConfig), then hard-coded value.
159         return DeviceConfig.getBoolean(
160                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
161                 /* name= */ KEY_GLOBAL_KILL_SWITCH,
162                 /* defaultValue= */ GLOBAL_KILL_SWITCH);
163     }
164 
165     @Override
isPersonalizationStatusOverrideEnabled()166     public boolean isPersonalizationStatusOverrideEnabled() {
167         if (getGlobalKillSwitch()) {
168             return false;
169         }
170         // The priority of applying the flag values: PH (DeviceConfig), then user hard-coded value.
171         return DeviceConfig.getBoolean(
172                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
173                 /* name= */ KEY_ENABLE_PERSONALIZATION_STATUS_OVERRIDE,
174                 /* defaultValue= */ ENABLE_PERSONALIZATION_STATUS_OVERRIDE);
175     }
176 
177     @Override
getPersonalizationStatusOverrideValue()178     public boolean getPersonalizationStatusOverrideValue() {
179         if (getGlobalKillSwitch()) {
180             return false;
181         }
182         return DeviceConfig.getBoolean(
183                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
184                 /* name= */ KEY_PERSONALIZATION_STATUS_OVERRIDE_VALUE,
185                 /* defaultValue= */ PERSONALIZATION_STATUS_OVERRIDE_VALUE);
186     }
187 
188     @Override
getIsolatedServiceDeadlineSeconds()189     public int getIsolatedServiceDeadlineSeconds() {
190         return DeviceConfig.getInt(
191                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
192                 /* name= */ KEY_ISOLATED_SERVICE_DEADLINE_SECONDS,
193                 /* defaultValue= */ ISOLATED_SERVICE_DEADLINE_SECONDS);
194     }
195 
196     @Override
getAppRequestFlowDeadlineSeconds()197     public int getAppRequestFlowDeadlineSeconds() {
198         return DeviceConfig.getInt(
199                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
200                 /* name= */ KEY_APP_REQUEST_FLOW_DEADLINE_SECONDS,
201                 /* defaultValue= */ APP_REQUEST_FLOW_DEADLINE_SECONDS);
202     }
203 
204     @Override
getRenderFlowDeadlineSeconds()205     public int getRenderFlowDeadlineSeconds() {
206         return DeviceConfig.getInt(
207                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
208                 /* name= */ KEY_RENDER_FLOW_DEADLINE_SECONDS,
209                 /* defaultValue= */ RENDER_FLOW_DEADLINE_SECONDS);
210     }
211 
212     @Override
getWebViewFlowDeadlineSeconds()213     public int getWebViewFlowDeadlineSeconds() {
214         return DeviceConfig.getInt(
215                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
216                 /* name= */ KEY_WEB_VIEW_FLOW_DEADLINE_SECONDS,
217                 /* defaultValue= */ WEB_VIEW_FLOW_DEADLINE_SECONDS);
218     }
219 
220     @Override
getWebTriggerFlowDeadlineSeconds()221     public int getWebTriggerFlowDeadlineSeconds() {
222         return DeviceConfig.getInt(
223                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
224                 /* name= */ KEY_WEB_TRIGGER_FLOW_DEADLINE_SECONDS,
225                 /* defaultValue= */ WEB_TRIGGER_FLOW_DEADLINE_SECONDS);
226     }
227 
228     public static final String KEY_EXAMPLE_STORE_FLOW_DEADLINE_SECONDS =
229             "example_store_flow_deadline_seconds";
230 
231     @Override
getExampleStoreFlowDeadlineSeconds()232     public int getExampleStoreFlowDeadlineSeconds() {
233         return DeviceConfig.getInt(
234                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
235                 /* name= */ KEY_EXAMPLE_STORE_FLOW_DEADLINE_SECONDS,
236                 /* defaultValue= */ EXAMPLE_STORE_FLOW_DEADLINE_SECONDS);
237     }
238 
239     public static final String KEY_DOWNLOAD_FLOW_DEADLINE_SECONDS =
240             "download_flow_deadline_seconds";
241 
242     @Override
getDownloadFlowDeadlineSeconds()243     public int getDownloadFlowDeadlineSeconds() {
244         return DeviceConfig.getInt(
245                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
246                 /* name= */ KEY_DOWNLOAD_FLOW_DEADLINE_SECONDS,
247                 /* defaultValue= */ DOWNLOAD_FLOW_DEADLINE_SECONDS);
248     }
249 
250     @Override
getTrustedPartnerAppsList()251     public String getTrustedPartnerAppsList() {
252         return SdkLevel.isAtLeastU()
253                 ? DeviceConfig.getString(
254                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
255                 /* name= */ KEY_TRUSTED_PARTNER_APPS_LIST,
256                 /* defaultValue */ DEFAULT_TRUSTED_PARTNER_APPS_LIST)
257                 : "";
258     }
259 
260     @Override
isSharedIsolatedProcessFeatureEnabled()261     public boolean isSharedIsolatedProcessFeatureEnabled() {
262         return SdkLevel.isAtLeastU() && DeviceConfig.getBoolean(
263                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
264                 /* name= */ KEY_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED,
265                 /* defaultValue= */ DEFAULT_SHARED_ISOLATED_PROCESS_FEATURE_ENABLED);
266     }
267 
268     @Override
isIsolatedServiceDebuggingEnabled()269     public boolean isIsolatedServiceDebuggingEnabled() {
270         return DeviceConfig.getBoolean(
271                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
272                 /* name= */ KEY_ISOLATED_SERVICE_DEBUGGING_ENABLED,
273                 /* defaultValue= */ DEFAULT_ISOLATED_SERVICE_DEBUGGING_ENABLED);
274     }
275 
276     @Override
getCallerAppAllowList()277     public String getCallerAppAllowList() {
278         return DeviceConfig.getString(
279                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
280                 /* name= */ KEY_CALLER_APP_ALLOW_LIST,
281                 /* defaultValue= */ DEFAULT_CALLER_APP_ALLOW_LIST);
282     }
283 
284     @Override
getIsolatedServiceAllowList()285     public String getIsolatedServiceAllowList() {
286         return DeviceConfig.getString(
287                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
288                 /* name= */ KEY_ISOLATED_SERVICE_ALLOW_LIST,
289                 /* defaultValue= */ DEFAULT_ISOLATED_SERVICE_ALLOW_LIST);
290     }
291 
292     @Override
getOutputDataAllowList()293     public String getOutputDataAllowList() {
294         return DeviceConfig.getString(
295                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
296                 /* name */ KEY_OUTPUT_DATA_ALLOW_LIST,
297                 /* defaultValue */ DEFAULT_OUTPUT_DATA_ALLOW_LIST);
298     }
299 
300     @Override
getUserControlCacheInMillis()301     public long getUserControlCacheInMillis() {
302         return DeviceConfig.getLong(
303                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
304                 /* name= */ KEY_USER_CONTROL_CACHE_IN_MILLIS,
305                 /* defaultValue= */ USER_CONTROL_CACHE_IN_MILLIS);
306     }
307 
308     @Override
getEnableClientErrorLogging()309     public boolean getEnableClientErrorLogging() {
310         return DeviceConfig.getBoolean(
311                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
312                 /* name= */ KEY_ODP_ENABLE_CLIENT_ERROR_LOGGING,
313                 /* defaultValue= */ DEFAULT_CLIENT_ERROR_LOGGING_ENABLED);
314     }
315 
316     @Override
getBackgroundJobsLoggingEnabled()317     public boolean getBackgroundJobsLoggingEnabled() {
318         // needs stable: execution stats may be less accurate if flag changed during job execution
319         return (boolean)
320                 mStableFlags.computeIfAbsent(
321                         KEY_ODP_BACKGROUND_JOBS_LOGGING_ENABLED,
322                         key -> {
323                             return DeviceConfig.getBoolean(
324                                     /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
325                                     /* name= */ KEY_ODP_BACKGROUND_JOBS_LOGGING_ENABLED,
326                                     /* defaultValue= */ BACKGROUND_JOB_LOGGING_ENABLED);
327                         });
328     }
329 
330     @Override
getBackgroundJobSamplingLoggingRate()331     public int getBackgroundJobSamplingLoggingRate() {
332         return DeviceConfig.getInt(
333                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
334                 /* name= */ KEY_ODP_BACKGROUND_JOB_SAMPLING_LOGGING_RATE,
335                 /* defaultValue= */ BACKGROUND_JOB_SAMPLING_LOGGING_RATE);
336     }
337 
338     @Override
getJobSchedulingLoggingEnabled()339     public boolean getJobSchedulingLoggingEnabled() {
340         return DeviceConfig.getBoolean(
341                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
342                 /* name= */ KEY_ODP_JOB_SCHEDULING_LOGGING_ENABLED,
343                 /* defaultValue= */ DEFAULT_JOB_SCHEDULING_LOGGING_ENABLED);
344     }
345 
346     @Override
getJobSchedulingLoggingSamplingRate()347     public int getJobSchedulingLoggingSamplingRate() {
348         return DeviceConfig.getInt(
349                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
350                 /* name= */ KEY_ODP_JOB_SCHEDULING_LOGGING_SAMPLING_RATE,
351                 /* defaultValue= */ DEFAULT_JOB_SCHEDULING_LOGGING_SAMPLING_RATE);
352     }
353 
354     @Override
getOdpModuleJobPolicy()355     public String getOdpModuleJobPolicy() {
356         return DeviceConfig.getString(
357                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
358                 /* name */ KEY_ODP_MODULE_JOB_POLICY,
359                 /* defaultValue */ DEFAULT_ODP_MODULE_JOB_POLICY);
360     }
361 
362     @Override
getSpePilotJobEnabled()363     public boolean getSpePilotJobEnabled() {
364         return DeviceConfig.getBoolean(
365                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
366                 /* name= */ KEY_ODP_SPE_PILOT_JOB_ENABLED,
367                 /* defaultValue= */ DEFAULT_SPE_PILOT_JOB_ENABLED);
368     }
369 
370     @Override
isArtImageLoadingOptimizationEnabled()371     public boolean isArtImageLoadingOptimizationEnabled() {
372         return DeviceConfig.getBoolean(
373                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
374                 /* name= */ KEY_IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED,
375                 /* defaultValue= */ IS_ART_IMAGE_LOADING_OPTIMIZATION_ENABLED);
376     }
377 
378     @Override
getResetDataDelaySeconds()379     public int getResetDataDelaySeconds() {
380         return DeviceConfig.getInt(
381                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
382                 /* name= */ KEY_RESET_DATA_DELAY_SECONDS,
383                 /* defaultValue= */ DEFAULT_RESET_DATA_DELAY_SECONDS);
384     }
385 
386     @Override
getResetDataDeadlineSeconds()387     public int getResetDataDeadlineSeconds() {
388         return DeviceConfig.getInt(
389                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
390                 /* name= */ KEY_RESET_DATA_DEADLINE_SECONDS,
391                 /* defaultValue= */ DEFAULT_RESET_DATA_DEADLINE_SECONDS);
392     }
393 
394     @Override
getAppInstallHistoryTtlInMillis()395     public long getAppInstallHistoryTtlInMillis() {
396         return DeviceConfig.getLong(
397                 /* namespace= */ NAMESPACE_ON_DEVICE_PERSONALIZATION,
398                 /* name= */ APP_INSTALL_HISTORY_TTL,
399                 /* defaultValue= */ DEFAULT_APP_INSTALL_HISTORY_TTL_MILLIS);
400     }
401 }
402