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