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