1 /*
2  * Copyright (C) 2019 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.utils;
18 
19 import android.annotation.Nullable;
20 import android.provider.DeviceConfig;
21 
22 import com.android.internal.annotations.VisibleForTesting;
23 import com.android.server.RescueParty;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 
29 /**
30  * Utilities for interacting with the {@link android.provider.DeviceConfig}.
31  *
32  * @hide
33  */
34 public final class FlagNamespaceUtils {
35     /**
36      * Special String used for communicating through {@link #RESET_PLATFORM_PACKAGE_FLAG} that
37      * Settings were reset by the RescueParty, no actual namespace with this name exists in
38      * {@link DeviceConfig}.
39      */
40     public static final String NAMESPACE_NO_PACKAGE = "no_package";
41 
42     /**
43      * Name of the special namespace in DeviceConfig table used for communicating resets.
44      */
45     @VisibleForTesting
46     public static final String NAMESPACE_RESCUE_PARTY = "rescue_party_namespace";
47     /**
48      * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY}, holding all known {@link
49      * DeviceConfig} namespaces, as a {@link #DELIMITER} separated String. It's updated after the
50      * first time flags are written to the new namespace in the {@link DeviceConfig}.
51      */
52     @VisibleForTesting
53     public static final String ALL_KNOWN_NAMESPACES_FLAG = "all_known_namespaces";
54     /**
55      * Flag in the {@link DeviceConfig} in {@link #NAMESPACE_RESCUE_PARTY} with integer counter
56      * suffix added to it, holding {@link DeviceConfig} namespace value whose flags were recently
57      * reset by the {@link RescueParty}. It's updated by {@link RescueParty} every time given
58      * namespace flags are reset.
59      */
60     @VisibleForTesting
61     public static final String RESET_PLATFORM_PACKAGE_FLAG = "reset_platform_package";
62     private static final String DELIMITER = ":";
63     /**
64      * Maximum value of the counter used in combination with {@link #RESET_PLATFORM_PACKAGE_FLAG}
65      * when communicating recently reset by the RescueParty namespace values.
66      */
67     private static final int MAX_COUNTER_VALUE = 50;
68 
69     private static int sKnownResetNamespacesFlagCounter = -1;
70 
71     /**
72      * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
73      * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for each namespace
74      * in the consumed namespacesList. These flags are used for communicating the namespaces
75      * (aka platform packages) whose flags in {@link DeviceConfig} were just reset
76      * by the RescueParty.
77      */
addToKnownResetNamespaces(@ullable List<String> namespacesList)78     public static void addToKnownResetNamespaces(@Nullable List<String> namespacesList) {
79         if (namespacesList == null) {
80             return;
81         }
82         for (String namespace : namespacesList) {
83             addToKnownResetNamespaces(namespace);
84         }
85     }
86 
87     /**
88      * Sets the union of {@link #RESET_PLATFORM_PACKAGE_FLAG} with
89      * {@link #sKnownResetNamespacesFlagCounter} in the DeviceConfig for the consumed namespace.
90      * This flag is used for communicating the namespace (aka platform package) whose flags
91      * in {@link DeviceConfig} were just reset by the RescueParty.
92      */
addToKnownResetNamespaces(String namespace)93     public static void addToKnownResetNamespaces(String namespace) {
94         int nextFlagCounter = incrementAndRetrieveResetNamespacesFlagCounter();
95         DeviceConfig.setProperty(NAMESPACE_RESCUE_PARTY,
96                 RESET_PLATFORM_PACKAGE_FLAG + nextFlagCounter,
97                 namespace, /*makeDefault=*/ true);
98     }
99 
100     /**
101      * Reset all namespaces in DeviceConfig with consumed resetMode.
102      */
resetDeviceConfig(int resetMode)103     public static void resetDeviceConfig(int resetMode) {
104         resetDeviceConfig(resetMode, getAllKnownDeviceConfigNamespacesList());
105     }
106 
107     /**
108      * Reset all consumed namespaces in DeviceConfig with consumed resetMode.
109      */
resetDeviceConfig(int resetMode, List<String> namespacesList)110     public static void resetDeviceConfig(int resetMode, List<String> namespacesList) {
111         for (String namespace : namespacesList) {
112             DeviceConfig.resetToDefaults(resetMode, namespace);
113         }
114         addToKnownResetNamespaces(namespacesList);
115     }
116 
117     /**
118      * Resets known reset namespaces flag counter for tests only.
119      */
120     @VisibleForTesting
resetKnownResetNamespacesFlagCounterForTest()121     public static void resetKnownResetNamespacesFlagCounterForTest() {
122         sKnownResetNamespacesFlagCounter = -1;
123     }
124 
125     /**
126      * Returns a list of all known DeviceConfig namespaces, except for the special {@link
127      * #NAMESPACE_RESCUE_PARTY}
128      */
getAllKnownDeviceConfigNamespacesList()129     private static List<String> getAllKnownDeviceConfigNamespacesList() {
130         String namespacesStr = DeviceConfig.getProperty(NAMESPACE_RESCUE_PARTY,
131                 ALL_KNOWN_NAMESPACES_FLAG);
132         List<String> namespacesList = toStringList(namespacesStr);
133         namespacesList.remove(NAMESPACE_RESCUE_PARTY);
134         return namespacesList;
135     }
136 
toStringList(String serialized)137     private static List<String> toStringList(String serialized) {
138         if (serialized == null || serialized.length() == 0) {
139             return new ArrayList<>();
140         }
141         return Arrays.asList(serialized.split(DELIMITER));
142     }
143 
incrementAndRetrieveResetNamespacesFlagCounter()144     private static int incrementAndRetrieveResetNamespacesFlagCounter() {
145         sKnownResetNamespacesFlagCounter++;
146         if (sKnownResetNamespacesFlagCounter == MAX_COUNTER_VALUE) {
147             sKnownResetNamespacesFlagCounter = 0;
148         }
149         return sKnownResetNamespacesFlagCounter;
150     }
151 }
152