• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.server.healthconnect;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.health.connect.ratelimiter.RateLimiter;
22 import android.health.connect.ratelimiter.RateLimiter.QuotaBucket;
23 import android.provider.DeviceConfig;
24 import android.util.ArraySet;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.time.Duration;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.Set;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.locks.ReentrantReadWriteLock;
36 
37 /**
38  * Singleton class to provide values and listen changes of settings flags.
39  *
40  * @hide
41  */
42 public class HealthConnectDeviceConfigManager implements DeviceConfig.OnPropertiesChangedListener {
43     private static Set<String> sFlagsToTrack = new ArraySet<>();
44     private static final String EXERCISE_ROUTE_FEATURE_FLAG = "exercise_routes_enable";
45     public static final String ENABLE_RATE_LIMITER_FLAG = "enable_rate_limiter";
46     private static final String MAX_READ_REQUESTS_PER_24H_FOREGROUND_FLAG =
47             "max_read_requests_per_24h_foreground";
48     private static final String MAX_READ_REQUESTS_PER_24H_BACKGROUND_FLAG =
49             "max_read_requests_per_24h_background";
50     private static final String MAX_READ_REQUESTS_PER_15M_FOREGROUND_FLAG =
51             "max_read_requests_per_15m_foreground";
52     private static final String MAX_READ_REQUESTS_PER_15M_BACKGROUND_FLAG =
53             "max_read_requests_per_15m_background";
54     private static final String MAX_WRITE_REQUESTS_PER_24H_FOREGROUND_FLAG =
55             "max_write_requests_per_24h_foreground";
56     private static final String MAX_WRITE_REQUESTS_PER_24H_BACKGROUND_FLAG =
57             "max_write_requests_per_24h_background";
58     private static final String MAX_WRITE_REQUESTS_PER_15M_FOREGROUND_FLAG =
59             "max_write_requests_per_15m_foreground";
60     private static final String MAX_WRITE_REQUESTS_PER_15M_BACKGROUND_FLAG =
61             "max_write_requests_per_15m_background";
62     private static final String MAX_WRITE_CHUNK_SIZE_FLAG = "max_write_chunk_size";
63     private static final String MAX_WRITE_SINGLE_RECORD_SIZE_FLAG = "max_write_single_record_size";
64 
65     // Flag to enable/disable sleep and exercise sessions.
66     private static final String SESSION_DATATYPE_FEATURE_FLAG = "session_types_enable";
67 
68     @VisibleForTesting
69     public static final String COUNT_MIGRATION_STATE_IN_PROGRESS_FLAG =
70             "count_migration_state_in_progress";
71 
72     @VisibleForTesting
73     public static final String COUNT_MIGRATION_STATE_ALLOWED_FLAG = "count_migration_state_allowed";
74 
75     @VisibleForTesting
76     public static final String MAX_START_MIGRATION_CALLS_ALLOWED_FLAG =
77             "max_start_migration_calls_allowed";
78 
79     @VisibleForTesting
80     public static final String IDLE_STATE_TIMEOUT_DAYS_FLAG = "idle_state_timeout_days";
81 
82     @VisibleForTesting
83     public static final String NON_IDLE_STATE_TIMEOUT_DAYS_FLAG = "non_idle_state_timeout_days";
84 
85     @VisibleForTesting
86     public static final String IN_PROGRESS_STATE_TIMEOUT_HOURS_FLAG =
87             "in_progress_state_timeout_hours";
88 
89     @VisibleForTesting
90     public static final String EXECUTION_TIME_BUFFER_MINUTES_FLAG = "execution_time_buffer_minutes";
91 
92     @VisibleForTesting
93     public static final String MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_FLAG =
94             "migration_completion_job_run_interval_days";
95 
96     @VisibleForTesting
97     public static final String MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_FLAG =
98             "migration_pause_job_run_interval_hours";
99 
100     @VisibleForTesting
101     public static final String ENABLE_PAUSE_STATE_CHANGE_JOBS_FLAG =
102             "enable_pause_state_change_jobs";
103 
104     @VisibleForTesting
105     public static final String ENABLE_COMPLETE_STATE_CHANGE_JOBS_FLAG =
106             "enable_complete_state_change_jobs";
107 
108     @VisibleForTesting
109     public static final String ENABLE_MIGRATION_NOTIFICATIONS_FLAG =
110             "enable_migration_notifications";
111 
112     private static final boolean SESSION_DATATYPE_DEFAULT_FLAG_VALUE = true;
113     private static final boolean EXERCISE_ROUTE_DEFAULT_FLAG_VALUE = true;
114     public static final boolean ENABLE_RATE_LIMITER_DEFAULT_FLAG_VALUE = true;
115     public static final int QUOTA_BUCKET_PER_15M_FOREGROUND_DEFAULT_FLAG_VALUE = 1000;
116     public static final int QUOTA_BUCKET_PER_24H_FOREGROUND_DEFAULT_FLAG_VALUE = 8000;
117     public static final int QUOTA_BUCKET_PER_15M_BACKGROUND_DEFAULT_FLAG_VALUE = 1000;
118     public static final int QUOTA_BUCKET_PER_24H_BACKGROUND_DEFAULT_FLAG_VALUE = 8000;
119     public static final int CHUNK_SIZE_LIMIT_IN_BYTES_DEFAULT_FLAG_VALUE = 5000000;
120     public static final int RECORD_SIZE_LIMIT_IN_BYTES_DEFAULT_FLAG_VALUE = 1000000;
121 
122     @VisibleForTesting
123     public static final int MIGRATION_STATE_IN_PROGRESS_COUNT_DEFAULT_FLAG_VALUE = 5;
124 
125     @VisibleForTesting public static final int MIGRATION_STATE_ALLOWED_COUNT_DEFAULT_FLAG_VALUE = 5;
126     @VisibleForTesting public static final int MAX_START_MIGRATION_CALLS_DEFAULT_FLAG_VALUE = 6;
127     @VisibleForTesting public static final int IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE = 120;
128     @VisibleForTesting public static final int NON_IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE = 15;
129 
130     @VisibleForTesting
131     public static final int IN_PROGRESS_STATE_TIMEOUT_HOURS_DEFAULT_FLAG_VALUE = 12;
132 
133     @VisibleForTesting
134     public static final int EXECUTION_TIME_BUFFER_MINUTES_DEFAULT_FLAG_VALUE = 30;
135 
136     @VisibleForTesting
137     public static final int MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_DEFAULT_FLAG_VALUE = 1;
138 
139     @VisibleForTesting
140     public static final int MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_DEFAULT_FLAG_VALUE = 4;
141 
142     @VisibleForTesting
143     public static final boolean ENABLE_PAUSE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE = true;
144 
145     @VisibleForTesting
146     public static final boolean ENABLE_COMPLETE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE = false;
147 
148     @VisibleForTesting
149     public static final boolean ENABLE_MIGRATION_NOTIFICATIONS_DEFAULT_FLAG_VALUE = true;
150 
151     private static HealthConnectDeviceConfigManager sDeviceConfigManager;
152     private final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
153     private static final String HEALTH_FITNESS_NAMESPACE = DeviceConfig.NAMESPACE_HEALTH_FITNESS;
154 
155     @GuardedBy("mLock")
156     private boolean mExerciseRouteEnabled =
157             DeviceConfig.getBoolean(
158                     HEALTH_FITNESS_NAMESPACE,
159                     EXERCISE_ROUTE_FEATURE_FLAG,
160                     EXERCISE_ROUTE_DEFAULT_FLAG_VALUE);
161 
162     @GuardedBy("mLock")
163     private boolean mSessionDatatypeEnabled =
164             DeviceConfig.getBoolean(
165                     HEALTH_FITNESS_NAMESPACE,
166                     SESSION_DATATYPE_FEATURE_FLAG,
167                     SESSION_DATATYPE_DEFAULT_FLAG_VALUE);
168 
169     @GuardedBy("mLock")
170     private int mMigrationStateInProgressCount =
171             DeviceConfig.getInt(
172                     HEALTH_FITNESS_NAMESPACE,
173                     COUNT_MIGRATION_STATE_IN_PROGRESS_FLAG,
174                     MIGRATION_STATE_IN_PROGRESS_COUNT_DEFAULT_FLAG_VALUE);
175 
176     @GuardedBy("mLock")
177     private int mMigrationStateAllowedCount =
178             DeviceConfig.getInt(
179                     HEALTH_FITNESS_NAMESPACE,
180                     COUNT_MIGRATION_STATE_ALLOWED_FLAG,
181                     MIGRATION_STATE_ALLOWED_COUNT_DEFAULT_FLAG_VALUE);
182 
183     @GuardedBy("mLock")
184     private int mMaxStartMigrationCalls =
185             DeviceConfig.getInt(
186                     HEALTH_FITNESS_NAMESPACE,
187                     MAX_START_MIGRATION_CALLS_ALLOWED_FLAG,
188                     MAX_START_MIGRATION_CALLS_DEFAULT_FLAG_VALUE);
189 
190     @GuardedBy("mLock")
191     private int mIdleStateTimeoutPeriod =
192             DeviceConfig.getInt(
193                     HEALTH_FITNESS_NAMESPACE,
194                     IDLE_STATE_TIMEOUT_DAYS_FLAG,
195                     IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE);
196 
197     @GuardedBy("mLock")
198     private int mNonIdleStateTimeoutPeriod =
199             DeviceConfig.getInt(
200                     HEALTH_FITNESS_NAMESPACE,
201                     NON_IDLE_STATE_TIMEOUT_DAYS_FLAG,
202                     NON_IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE);
203 
204     @GuardedBy("mLock")
205     private int mInProgressStateTimeoutPeriod =
206             DeviceConfig.getInt(
207                     HEALTH_FITNESS_NAMESPACE,
208                     IN_PROGRESS_STATE_TIMEOUT_HOURS_FLAG,
209                     IN_PROGRESS_STATE_TIMEOUT_HOURS_DEFAULT_FLAG_VALUE);
210 
211     @GuardedBy("mLock")
212     private int mExecutionTimeBuffer =
213             DeviceConfig.getInt(
214                     HEALTH_FITNESS_NAMESPACE,
215                     EXECUTION_TIME_BUFFER_MINUTES_FLAG,
216                     EXECUTION_TIME_BUFFER_MINUTES_DEFAULT_FLAG_VALUE);
217 
218     @GuardedBy("mLock")
219     private int mMigrationCompletionJobRunInterval =
220             DeviceConfig.getInt(
221                     HEALTH_FITNESS_NAMESPACE,
222                     MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_FLAG,
223                     MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_DEFAULT_FLAG_VALUE);
224 
225     @GuardedBy("mLock")
226     private int mMigrationPauseJobRunInterval =
227             DeviceConfig.getInt(
228                     HEALTH_FITNESS_NAMESPACE,
229                     MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_FLAG,
230                     MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_DEFAULT_FLAG_VALUE);
231 
232     @GuardedBy("mLock")
233     private boolean mEnablePauseStateChangeJob =
234             DeviceConfig.getBoolean(
235                     HEALTH_FITNESS_NAMESPACE,
236                     ENABLE_PAUSE_STATE_CHANGE_JOBS_FLAG,
237                     ENABLE_PAUSE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE);
238 
239     @GuardedBy("mLock")
240     private boolean mEnableCompleteStateChangeJob =
241             DeviceConfig.getBoolean(
242                     HEALTH_FITNESS_NAMESPACE,
243                     ENABLE_COMPLETE_STATE_CHANGE_JOBS_FLAG,
244                     ENABLE_COMPLETE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE);
245 
246     @GuardedBy("mLock")
247     private boolean mEnableMigrationNotifications =
248             DeviceConfig.getBoolean(
249                     HEALTH_FITNESS_NAMESPACE,
250                     ENABLE_MIGRATION_NOTIFICATIONS_FLAG,
251                     ENABLE_MIGRATION_NOTIFICATIONS_DEFAULT_FLAG_VALUE);
252 
253     @NonNull
254     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
initializeInstance(Context context)255     public static void initializeInstance(Context context) {
256         if (sDeviceConfigManager == null) {
257             sDeviceConfigManager = new HealthConnectDeviceConfigManager();
258             DeviceConfig.addOnPropertiesChangedListener(
259                     HEALTH_FITNESS_NAMESPACE, context.getMainExecutor(), sDeviceConfigManager);
260             addFlagsToTrack();
261         }
262     }
263 
264     /** Returns initialised instance of this class. */
265     @NonNull
getInitialisedInstance()266     public static HealthConnectDeviceConfigManager getInitialisedInstance() {
267         Objects.requireNonNull(sDeviceConfigManager);
268 
269         return sDeviceConfigManager;
270     }
271 
272     /** Adds flags that need to be updated if their values are changed on the server. */
addFlagsToTrack()273     private static void addFlagsToTrack() {
274         sFlagsToTrack.add(EXERCISE_ROUTE_FEATURE_FLAG);
275         sFlagsToTrack.add(SESSION_DATATYPE_FEATURE_FLAG);
276         sFlagsToTrack.add(ENABLE_RATE_LIMITER_FLAG);
277         sFlagsToTrack.add(COUNT_MIGRATION_STATE_IN_PROGRESS_FLAG);
278         sFlagsToTrack.add(COUNT_MIGRATION_STATE_ALLOWED_FLAG);
279         sFlagsToTrack.add(MAX_START_MIGRATION_CALLS_ALLOWED_FLAG);
280         sFlagsToTrack.add(IDLE_STATE_TIMEOUT_DAYS_FLAG);
281         sFlagsToTrack.add(NON_IDLE_STATE_TIMEOUT_DAYS_FLAG);
282         sFlagsToTrack.add(IN_PROGRESS_STATE_TIMEOUT_HOURS_FLAG);
283         sFlagsToTrack.add(EXECUTION_TIME_BUFFER_MINUTES_FLAG);
284         sFlagsToTrack.add(MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_FLAG);
285         sFlagsToTrack.add(MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_FLAG);
286         sFlagsToTrack.add(ENABLE_PAUSE_STATE_CHANGE_JOBS_FLAG);
287         sFlagsToTrack.add(ENABLE_COMPLETE_STATE_CHANGE_JOBS_FLAG);
288         sFlagsToTrack.add(ENABLE_MIGRATION_NOTIFICATIONS_FLAG);
289     }
290 
291     /** Returns if operations with exercise route are enabled. */
isExerciseRouteFeatureEnabled()292     public boolean isExerciseRouteFeatureEnabled() {
293         mLock.readLock().lock();
294         try {
295             return mExerciseRouteEnabled;
296         } finally {
297             mLock.readLock().unlock();
298         }
299     }
300 
301     @GuardedBy("mLock")
302     private boolean mRateLimiterEnabled =
303             DeviceConfig.getBoolean(
304                     DeviceConfig.NAMESPACE_HEALTH_FITNESS,
305                     ENABLE_RATE_LIMITER_FLAG,
306                     ENABLE_RATE_LIMITER_DEFAULT_FLAG_VALUE);
307 
308     /** Returns if operations with sessions datatypes are enabled. */
isSessionDatatypeFeatureEnabled()309     public boolean isSessionDatatypeFeatureEnabled() {
310         mLock.readLock().lock();
311         try {
312             return mSessionDatatypeEnabled;
313         } finally {
314             mLock.readLock().unlock();
315         }
316     }
317 
318     /**
319      * Returns the required count for {@link
320      * android.health.connect.HealthConnectDataState.MIGRATION_STATE_IN_PROGRESS}.
321      */
getMigrationStateInProgressCount()322     public int getMigrationStateInProgressCount() {
323         mLock.readLock().lock();
324         try {
325             return mMigrationStateInProgressCount;
326         } finally {
327             mLock.readLock().unlock();
328         }
329     }
330 
331     /**
332      * Returns the required count for {@link
333      * android.health.connect.HealthConnectDataState.MIGRATION_STATE_ALLOWED}.
334      */
getMigrationStateAllowedCount()335     public int getMigrationStateAllowedCount() {
336         mLock.readLock().lock();
337         try {
338             return mMigrationStateAllowedCount;
339         } finally {
340             mLock.readLock().unlock();
341         }
342     }
343 
344     /** Returns the maximum number of start migration calls allowed. */
getMaxStartMigrationCalls()345     public int getMaxStartMigrationCalls() {
346         mLock.readLock().lock();
347         try {
348             return mMaxStartMigrationCalls;
349         } finally {
350             mLock.readLock().unlock();
351         }
352     }
353 
354     /**
355      * Returns the timeout period of {@link
356      * android.health.connect.HealthConnectDataState.MIGRATION_STATE_IDLE}.
357      */
getIdleStateTimeoutPeriod()358     public Duration getIdleStateTimeoutPeriod() {
359         mLock.readLock().lock();
360         try {
361             return Duration.ofDays(mIdleStateTimeoutPeriod);
362         } finally {
363             mLock.readLock().unlock();
364         }
365     }
366 
367     /** Returns the timeout period of non-idle migration states. */
getNonIdleStateTimeoutPeriod()368     public Duration getNonIdleStateTimeoutPeriod() {
369         mLock.readLock().lock();
370         try {
371             return Duration.ofDays(mNonIdleStateTimeoutPeriod);
372         } finally {
373             mLock.readLock().unlock();
374         }
375     }
376 
377     /**
378      * Returns the timeout period of {@link
379      * android.health.connect.HealthConnectDataState.MIGRATION_STATE_IN_PROGRESS}.
380      */
getInProgressStateTimeoutPeriod()381     public Duration getInProgressStateTimeoutPeriod() {
382         mLock.readLock().lock();
383         try {
384             return Duration.ofHours(mInProgressStateTimeoutPeriod);
385         } finally {
386             mLock.readLock().unlock();
387         }
388     }
389 
390     /** Returns the time buffer kept to ensure that job execution is not skipped. */
getExecutionTimeBuffer()391     public long getExecutionTimeBuffer() {
392         mLock.readLock().lock();
393         try {
394             return TimeUnit.MINUTES.toMillis(mExecutionTimeBuffer);
395         } finally {
396             mLock.readLock().unlock();
397         }
398     }
399 
400     /** Returns the time interval at which the migration completion job will run periodically. */
getMigrationCompletionJobRunInterval()401     public long getMigrationCompletionJobRunInterval() {
402         mLock.readLock().lock();
403         try {
404             return TimeUnit.DAYS.toMillis(mMigrationCompletionJobRunInterval);
405         } finally {
406             mLock.readLock().unlock();
407         }
408     }
409 
410     /** Returns the time interval at which the migration pause job will run periodically. */
getMigrationPauseJobRunInterval()411     public long getMigrationPauseJobRunInterval() {
412         mLock.readLock().lock();
413         try {
414             return TimeUnit.HOURS.toMillis(mMigrationPauseJobRunInterval);
415         } finally {
416             mLock.readLock().unlock();
417         }
418     }
419 
420     /** Returns if migration pause change jobs are enabled. */
isPauseStateChangeJobEnabled()421     public boolean isPauseStateChangeJobEnabled() {
422         mLock.readLock().lock();
423         try {
424             return mEnablePauseStateChangeJob;
425         } finally {
426             mLock.readLock().unlock();
427         }
428     }
429 
430     /** Returns if migration completion jobs are enabled. */
isCompleteStateChangeJobEnabled()431     public boolean isCompleteStateChangeJobEnabled() {
432         mLock.readLock().lock();
433         try {
434             return mEnableCompleteStateChangeJob;
435         } finally {
436             mLock.readLock().unlock();
437         }
438     }
439 
440     /** Returns if migration notifications are enabled. */
areMigrationNotificationsEnabled()441     public boolean areMigrationNotificationsEnabled() {
442         mLock.readLock().lock();
443         try {
444             return mEnableMigrationNotifications;
445         } finally {
446             mLock.readLock().unlock();
447         }
448     }
449 
450     /** Updates rate limiting quota values. */
updateRateLimiterValues()451     public void updateRateLimiterValues() {
452         Map<Integer, Integer> quotaBucketToMaxApiCallQuotaMap = new HashMap<>();
453         Map<String, Integer> quotaBucketToMaxMemoryQuotaMap = new HashMap<>();
454         quotaBucketToMaxApiCallQuotaMap.put(
455                 QuotaBucket.QUOTA_BUCKET_READS_PER_24H_FOREGROUND,
456                 DeviceConfig.getInt(
457                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
458                         MAX_READ_REQUESTS_PER_24H_FOREGROUND_FLAG,
459                         QUOTA_BUCKET_PER_24H_FOREGROUND_DEFAULT_FLAG_VALUE));
460         quotaBucketToMaxApiCallQuotaMap.put(
461                 QuotaBucket.QUOTA_BUCKET_READS_PER_24H_BACKGROUND,
462                 DeviceConfig.getInt(
463                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
464                         MAX_READ_REQUESTS_PER_24H_BACKGROUND_FLAG,
465                         QUOTA_BUCKET_PER_24H_BACKGROUND_DEFAULT_FLAG_VALUE));
466         quotaBucketToMaxApiCallQuotaMap.put(
467                 QuotaBucket.QUOTA_BUCKET_READS_PER_15M_FOREGROUND,
468                 DeviceConfig.getInt(
469                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
470                         MAX_READ_REQUESTS_PER_15M_FOREGROUND_FLAG,
471                         QUOTA_BUCKET_PER_15M_FOREGROUND_DEFAULT_FLAG_VALUE));
472         quotaBucketToMaxApiCallQuotaMap.put(
473                 QuotaBucket.QUOTA_BUCKET_READS_PER_15M_BACKGROUND,
474                 DeviceConfig.getInt(
475                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
476                         MAX_READ_REQUESTS_PER_15M_BACKGROUND_FLAG,
477                         QUOTA_BUCKET_PER_15M_BACKGROUND_DEFAULT_FLAG_VALUE));
478         quotaBucketToMaxApiCallQuotaMap.put(
479                 QuotaBucket.QUOTA_BUCKET_WRITES_PER_24H_FOREGROUND,
480                 DeviceConfig.getInt(
481                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
482                         MAX_WRITE_REQUESTS_PER_24H_FOREGROUND_FLAG,
483                         QUOTA_BUCKET_PER_24H_FOREGROUND_DEFAULT_FLAG_VALUE));
484         quotaBucketToMaxApiCallQuotaMap.put(
485                 QuotaBucket.QUOTA_BUCKET_WRITES_PER_24H_BACKGROUND,
486                 DeviceConfig.getInt(
487                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
488                         MAX_WRITE_REQUESTS_PER_24H_BACKGROUND_FLAG,
489                         QUOTA_BUCKET_PER_24H_BACKGROUND_DEFAULT_FLAG_VALUE));
490         quotaBucketToMaxApiCallQuotaMap.put(
491                 QuotaBucket.QUOTA_BUCKET_WRITES_PER_15M_FOREGROUND,
492                 DeviceConfig.getInt(
493                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
494                         MAX_WRITE_REQUESTS_PER_15M_FOREGROUND_FLAG,
495                         QUOTA_BUCKET_PER_15M_FOREGROUND_DEFAULT_FLAG_VALUE));
496         quotaBucketToMaxApiCallQuotaMap.put(
497                 QuotaBucket.QUOTA_BUCKET_WRITES_PER_15M_BACKGROUND,
498                 DeviceConfig.getInt(
499                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
500                         MAX_WRITE_REQUESTS_PER_15M_BACKGROUND_FLAG,
501                         QUOTA_BUCKET_PER_15M_BACKGROUND_DEFAULT_FLAG_VALUE));
502         quotaBucketToMaxMemoryQuotaMap.put(
503                 RateLimiter.CHUNK_SIZE_LIMIT_IN_BYTES,
504                 DeviceConfig.getInt(
505                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
506                         MAX_WRITE_CHUNK_SIZE_FLAG,
507                         CHUNK_SIZE_LIMIT_IN_BYTES_DEFAULT_FLAG_VALUE));
508         quotaBucketToMaxMemoryQuotaMap.put(
509                 RateLimiter.RECORD_SIZE_LIMIT_IN_BYTES,
510                 DeviceConfig.getInt(
511                         DeviceConfig.NAMESPACE_HEALTH_FITNESS,
512                         MAX_WRITE_SINGLE_RECORD_SIZE_FLAG,
513                         RECORD_SIZE_LIMIT_IN_BYTES_DEFAULT_FLAG_VALUE));
514         RateLimiter.updateApiCallQuotaMap(quotaBucketToMaxApiCallQuotaMap);
515         RateLimiter.updateMemoryQuotaMap(quotaBucketToMaxMemoryQuotaMap);
516         mLock.readLock().lock();
517         try {
518             RateLimiter.updateEnableRateLimiterFlag(mRateLimiterEnabled);
519         } finally {
520             mLock.readLock().unlock();
521         }
522     }
523 
524     @Override
onPropertiesChanged(DeviceConfig.Properties properties)525     public void onPropertiesChanged(DeviceConfig.Properties properties) {
526         if (!properties.getNamespace().equals(HEALTH_FITNESS_NAMESPACE)) {
527             return;
528         }
529 
530         Set<String> changedFlags = new ArraySet<>(properties.getKeyset());
531         changedFlags.retainAll(sFlagsToTrack);
532 
533         for (String name : changedFlags) {
534             if (name.equals(EXERCISE_ROUTE_FEATURE_FLAG)) {
535                 mLock.writeLock().lock();
536                 try {
537                     mExerciseRouteEnabled =
538                             properties.getBoolean(
539                                     EXERCISE_ROUTE_FEATURE_FLAG, EXERCISE_ROUTE_DEFAULT_FLAG_VALUE);
540                 } finally {
541                     mLock.writeLock().unlock();
542                 }
543             } else if (name.equals(SESSION_DATATYPE_FEATURE_FLAG)) {
544                 mLock.writeLock().lock();
545                 try {
546                     mSessionDatatypeEnabled =
547                             properties.getBoolean(
548                                     SESSION_DATATYPE_FEATURE_FLAG,
549                                     SESSION_DATATYPE_DEFAULT_FLAG_VALUE);
550                 } finally {
551                     mLock.writeLock().unlock();
552                 }
553             } else if (name.equals(ENABLE_RATE_LIMITER_FLAG)) {
554                 mLock.writeLock().lock();
555                 try {
556                     mRateLimiterEnabled =
557                             properties.getBoolean(
558                                     ENABLE_RATE_LIMITER_FLAG,
559                                     ENABLE_RATE_LIMITER_DEFAULT_FLAG_VALUE);
560                     RateLimiter.updateEnableRateLimiterFlag(mRateLimiterEnabled);
561                 } finally {
562                     mLock.writeLock().unlock();
563                 }
564             } else if (name.equals(COUNT_MIGRATION_STATE_IN_PROGRESS_FLAG)) {
565                 mLock.writeLock().lock();
566                 try {
567                     mMigrationStateInProgressCount =
568                             properties.getInt(
569                                     COUNT_MIGRATION_STATE_IN_PROGRESS_FLAG,
570                                     MIGRATION_STATE_IN_PROGRESS_COUNT_DEFAULT_FLAG_VALUE);
571                 } finally {
572                     mLock.writeLock().unlock();
573                 }
574             } else if (name.equals(COUNT_MIGRATION_STATE_ALLOWED_FLAG)) {
575                 mLock.writeLock().lock();
576                 try {
577                     mMigrationStateAllowedCount =
578                             properties.getInt(
579                                     COUNT_MIGRATION_STATE_ALLOWED_FLAG,
580                                     MIGRATION_STATE_ALLOWED_COUNT_DEFAULT_FLAG_VALUE);
581                 } finally {
582                     mLock.writeLock().unlock();
583                 }
584             } else if (name.equals(MAX_START_MIGRATION_CALLS_ALLOWED_FLAG)) {
585                 mLock.writeLock().lock();
586                 try {
587                     mMaxStartMigrationCalls =
588                             properties.getInt(
589                                     MAX_START_MIGRATION_CALLS_ALLOWED_FLAG,
590                                     MAX_START_MIGRATION_CALLS_DEFAULT_FLAG_VALUE);
591                 } finally {
592                     mLock.writeLock().unlock();
593                 }
594             } else if (name.equals(IDLE_STATE_TIMEOUT_DAYS_FLAG)) {
595                 mLock.writeLock().lock();
596                 try {
597                     mIdleStateTimeoutPeriod =
598                             properties.getInt(
599                                     IDLE_STATE_TIMEOUT_DAYS_FLAG,
600                                     IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE);
601                 } finally {
602                     mLock.writeLock().unlock();
603                 }
604             } else if (name.equals(NON_IDLE_STATE_TIMEOUT_DAYS_FLAG)) {
605                 mLock.writeLock().lock();
606                 try {
607                     mNonIdleStateTimeoutPeriod =
608                             properties.getInt(
609                                     NON_IDLE_STATE_TIMEOUT_DAYS_FLAG,
610                                     NON_IDLE_STATE_TIMEOUT_DAYS_DEFAULT_FLAG_VALUE);
611                 } finally {
612                     mLock.writeLock().unlock();
613                 }
614             } else if (name.equals(IN_PROGRESS_STATE_TIMEOUT_HOURS_FLAG)) {
615                 mLock.writeLock().lock();
616                 try {
617                     mInProgressStateTimeoutPeriod =
618                             properties.getInt(
619                                     IN_PROGRESS_STATE_TIMEOUT_HOURS_FLAG,
620                                     IN_PROGRESS_STATE_TIMEOUT_HOURS_DEFAULT_FLAG_VALUE);
621                 } finally {
622                     mLock.writeLock().unlock();
623                 }
624             } else if (name.equals(EXECUTION_TIME_BUFFER_MINUTES_FLAG)) {
625                 mLock.writeLock().lock();
626                 try {
627                     mExecutionTimeBuffer =
628                             properties.getInt(
629                                     EXECUTION_TIME_BUFFER_MINUTES_FLAG,
630                                     EXECUTION_TIME_BUFFER_MINUTES_DEFAULT_FLAG_VALUE);
631                 } finally {
632                     mLock.writeLock().unlock();
633                 }
634             } else if (name.equals(MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_FLAG)) {
635                 mLock.writeLock().lock();
636                 try {
637                     mMigrationCompletionJobRunInterval =
638                             properties.getInt(
639                                     MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_FLAG,
640                                     MIGRATION_COMPLETION_JOB_RUN_INTERVAL_DAYS_DEFAULT_FLAG_VALUE);
641                 } finally {
642                     mLock.writeLock().unlock();
643                 }
644             } else if (name.equals(MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_FLAG)) {
645                 mLock.writeLock().lock();
646                 try {
647                     mMigrationPauseJobRunInterval =
648                             properties.getInt(
649                                     MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_FLAG,
650                                     MIGRATION_PAUSE_JOB_RUN_INTERVAL_HOURS_DEFAULT_FLAG_VALUE);
651                 } finally {
652                     mLock.writeLock().unlock();
653                 }
654             } else if (name.equals(ENABLE_PAUSE_STATE_CHANGE_JOBS_FLAG)) {
655                 mLock.writeLock().lock();
656                 try {
657                     mEnablePauseStateChangeJob =
658                             properties.getBoolean(
659                                     ENABLE_PAUSE_STATE_CHANGE_JOBS_FLAG,
660                                     ENABLE_PAUSE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE);
661                 } finally {
662                     mLock.writeLock().unlock();
663                 }
664             } else if (name.equals(ENABLE_COMPLETE_STATE_CHANGE_JOBS_FLAG)) {
665                 mLock.writeLock().lock();
666                 try {
667                     mEnableCompleteStateChangeJob =
668                             properties.getBoolean(
669                                     ENABLE_COMPLETE_STATE_CHANGE_JOBS_FLAG,
670                                     ENABLE_COMPLETE_STATE_CHANGE_JOB_DEFAULT_FLAG_VALUE);
671                 } finally {
672                     mLock.writeLock().unlock();
673                 }
674             } else if (name.equals(ENABLE_MIGRATION_NOTIFICATIONS_FLAG)) {
675                 mLock.writeLock().lock();
676                 try {
677                     mEnableMigrationNotifications =
678                             properties.getBoolean(
679                                     ENABLE_MIGRATION_NOTIFICATIONS_FLAG,
680                                     ENABLE_MIGRATION_NOTIFICATIONS_DEFAULT_FLAG_VALUE);
681                 } finally {
682                     mLock.writeLock().unlock();
683                 }
684             }
685         }
686     }
687 }
688