1 /*
2  * Copyright (C) 2021 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 android.alarmmanager.util;
18 
19 import static org.junit.Assert.assertTrue;
20 
21 import android.provider.DeviceConfig;
22 import android.util.Log;
23 
24 import com.android.compatibility.common.util.PollingCheck;
25 import com.android.compatibility.common.util.SystemUtil;
26 
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Map;
30 
31 public class AlarmManagerDeviceConfigHelper {
32     private static final String TAG = AlarmManagerDeviceConfigHelper.class.getSimpleName();
33 
34     private static final long UPDATE_TIMEOUT = 30_000;
35 
36     private volatile Map<String, String> mCommittedMap;
37     private final Map<String, String> mInitialPropertiesMap;
38     private final Map<String, String> mPropertiesToSetMap;
39 
AlarmManagerDeviceConfigHelper()40     public AlarmManagerDeviceConfigHelper() {
41         mPropertiesToSetMap = new HashMap<>();
42         DeviceConfig.Properties initialProperties = null;
43         try {
44             initialProperties = SystemUtil.callWithShellPermissionIdentity(
45                     () -> DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
46         } catch (Exception e) {
47             Log.e(TAG, "Unexpected exception while fetching device_config properties", e);
48         }
49         if (initialProperties != null) {
50             for (String key : initialProperties.getKeyset()) {
51                 mPropertiesToSetMap.put(key, initialProperties.getString(key, null));
52             }
53             mCommittedMap = mInitialPropertiesMap = Collections.unmodifiableMap(
54                     new HashMap<>(mPropertiesToSetMap));
55         } else {
56             mCommittedMap = mInitialPropertiesMap =  Collections.emptyMap();
57         }
58     }
59 
with(String key, long value)60     public AlarmManagerDeviceConfigHelper with(String key, long value) {
61         return with(key, Long.toString(value));
62     }
63 
with(String key, int value)64     public AlarmManagerDeviceConfigHelper with(String key, int value) {
65         return with(key, Integer.toString(value));
66     }
67 
with(String key, boolean value)68     public AlarmManagerDeviceConfigHelper with(String key, boolean value) {
69         return with(key, Boolean.toString(value));
70     }
71 
with(String key, String value)72     public AlarmManagerDeviceConfigHelper with(String key, String value) {
73         mPropertiesToSetMap.put(key, value);
74         return this;
75     }
76 
without(String key)77     public AlarmManagerDeviceConfigHelper without(String key) {
78         mPropertiesToSetMap.remove(key);
79         return this;
80     }
81 
getCurrentConfigVersion()82     private static int getCurrentConfigVersion() {
83         final String output = SystemUtil.runShellCommand("cmd alarm get-config-version").trim();
84         return Integer.parseInt(output);
85     }
86 
commitAndAwaitPropagation(DeviceConfig.Properties propertiesToSet)87     public static void commitAndAwaitPropagation(DeviceConfig.Properties propertiesToSet) {
88         final int currentVersion = getCurrentConfigVersion();
89         SystemUtil.runWithShellPermissionIdentity(
90                 () -> assertTrue(DeviceConfig.setProperties(propertiesToSet)));
91         PollingCheck.waitFor(UPDATE_TIMEOUT, () -> (getCurrentConfigVersion() > currentVersion),
92                 "Could not update config within " + UPDATE_TIMEOUT + "ms. Current version: "
93                         + currentVersion);
94     }
95 
commitAndAwaitPropagation()96     public void commitAndAwaitPropagation() {
97         if (mPropertiesToSetMap.equals(mCommittedMap)) {
98             // This will not cause any change, and will not bump up the config version.
99             return;
100         }
101         commitAndAwaitPropagation(
102                 new DeviceConfig.Properties(DeviceConfig.NAMESPACE_ALARM_MANAGER,
103                         mPropertiesToSetMap));
104         mCommittedMap = Collections.unmodifiableMap(new HashMap<>(mPropertiesToSetMap));
105     }
106 
restoreAll()107     public void restoreAll() {
108         if (mCommittedMap.equals(mInitialPropertiesMap)) {
109             // This will not cause any change, and will not bump up the config version.
110             return;
111         }
112         commitAndAwaitPropagation(new DeviceConfig.Properties(DeviceConfig.NAMESPACE_ALARM_MANAGER,
113                 mInitialPropertiesMap));
114         mCommittedMap = Collections.emptyMap();
115     }
116 }
117