• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.aconfigd;
18 
19 import android.aconfigd.Aconfigd.StorageRequestMessage;
20 import android.aconfigd.Aconfigd.StorageRequestMessages;
21 import android.aconfigd.Aconfigd.StorageReturnMessage;
22 import android.aconfigd.Aconfigd.StorageReturnMessages;
23 import android.util.Slog;
24 import android.util.proto.ProtoInputStream;
25 import android.util.proto.ProtoOutputStream;
26 
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.ArrayDeque;
30 import java.util.Deque;
31 import java.util.HashMap;
32 import java.util.Map;
33 
34 /** @hide */
35 public class AconfigdJavaUtils {
36 
37     private static String TAG = "AconfigdJavaUtils";
38 
getAconfigdClientSocket()39     public static AconfigdClientSocket getAconfigdClientSocket() {
40         return new AconfigdClientSocketImpl();
41     }
42 
43     /**
44      * serialize a storage reset request proto via proto output stream
45      *
46      * @param proto
47      * @hide
48      */
writeResetStorageRequest(ProtoOutputStream proto)49     public static void writeResetStorageRequest(ProtoOutputStream proto) {
50         long msgsToken = proto.start(StorageRequestMessages.MSGS);
51         long msgToken = proto.start(StorageRequestMessage.RESET_STORAGE_MESSAGE);
52         proto.write(StorageRequestMessage.ResetStorageMessage.ALL, true);
53         proto.end(msgToken);
54         proto.end(msgsToken);
55     }
56 
57     /**
58      * deserialize a flag input proto stream and log
59      *
60      * @param proto
61      * @hide
62      */
writeFlagOverrideRequest( ProtoOutputStream proto, String packageName, String flagName, String flagValue, long overrideType)63     public static void writeFlagOverrideRequest(
64             ProtoOutputStream proto,
65             String packageName,
66             String flagName,
67             String flagValue,
68             long overrideType) {
69         long msgsToken = proto.start(StorageRequestMessages.MSGS);
70         long msgToken = proto.start(StorageRequestMessage.FLAG_OVERRIDE_MESSAGE);
71         proto.write(StorageRequestMessage.FlagOverrideMessage.PACKAGE_NAME, packageName);
72         proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_NAME, flagName);
73         proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_VALUE, flagValue);
74         proto.write(StorageRequestMessage.FlagOverrideMessage.OVERRIDE_TYPE, overrideType);
75         proto.end(msgToken);
76         proto.end(msgsToken);
77     }
78 
79     /**
80      * Send a request to aconfig storage to remove a flag local override.
81      *
82      * @param proto
83      * @param packageName the package of the flag
84      * @param flagName the name of the flag
85      *
86      * @hide
87      */
writeFlagOverrideRemovalRequest( ProtoOutputStream proto, String packageName, String flagName)88     public static void writeFlagOverrideRemovalRequest(
89         ProtoOutputStream proto, String packageName, String flagName) {
90       long msgsToken = proto.start(StorageRequestMessages.MSGS);
91       long msgToken = proto.start(StorageRequestMessage.REMOVE_LOCAL_OVERRIDE_MESSAGE);
92       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.PACKAGE_NAME, packageName);
93       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.FLAG_NAME, flagName);
94       proto.write(StorageRequestMessage.RemoveLocalOverrideMessage.REMOVE_ALL, false);
95       proto.end(msgToken);
96       proto.end(msgsToken);
97     }
98 
99     /**
100      * deserialize a flag input proto stream and log
101      *
102      * @param inputStream
103      * @hide
104      */
parseAndLogAconfigdReturn(InputStream inputStream)105     public static void parseAndLogAconfigdReturn(InputStream inputStream) throws IOException {
106         ProtoInputStream proto = new ProtoInputStream(inputStream);
107         while (true) {
108             switch (proto.nextField()) {
109                 case (int) StorageReturnMessages.MSGS:
110                     long msgsToken = proto.start(StorageReturnMessages.MSGS);
111                     switch (proto.nextField()) {
112                         case (int) StorageReturnMessage.FLAG_OVERRIDE_MESSAGE:
113                             Slog.i(TAG, "successfully handled override requests");
114                             long msgToken = proto.start(StorageReturnMessage.FLAG_OVERRIDE_MESSAGE);
115                             proto.end(msgToken);
116                             break;
117                         case (int) StorageReturnMessage.ERROR_MESSAGE:
118                             String errmsg = proto.readString(StorageReturnMessage.ERROR_MESSAGE);
119                             Slog.i(TAG, "override request failed: " + errmsg);
120                             break;
121                         case ProtoInputStream.NO_MORE_FIELDS:
122                             break;
123                         default:
124                             Slog.e(
125                                     TAG,
126                                     "invalid message type, expecting only flag override return or"
127                                             + " error message");
128                             break;
129                     }
130                     proto.end(msgsToken);
131                     break;
132                 case ProtoInputStream.NO_MORE_FIELDS:
133                     return;
134                 default:
135                     Slog.e(TAG, "invalid message type, expect storage return message");
136                     break;
137             }
138         }
139     }
140 
141     /**
142      * this method will new flag value into new storage, and stage the new values
143      *
144      * @param propsToStage the map of flags <namespace, <flagName, value>>
145      * @param isLocal indicates whether this is a local override
146      * @hide
147      */
stageFlagsInNewStorage( AconfigdClientSocket localSocket, Map<String, Map<String, String>> propsToStage, boolean isLocal)148     public static void stageFlagsInNewStorage(
149             AconfigdClientSocket localSocket,
150             Map<String, Map<String, String>> propsToStage,
151             boolean isLocal) {
152         // write aconfigd requests proto to proto output stream
153         int num_requests = 0;
154         ProtoOutputStream requests = new ProtoOutputStream();
155         for (Map.Entry<String, Map<String, String>> entry : propsToStage.entrySet()) {
156             String actualNamespace = entry.getKey();
157             Map<String, String> flagValuesToStage = entry.getValue();
158             for (String fullFlagName : flagValuesToStage.keySet()) {
159                 String stagedValue = flagValuesToStage.get(fullFlagName);
160                 int idx = fullFlagName.lastIndexOf(".");
161                 if (idx == -1) {
162                     Slog.i(TAG, "invalid flag name: " + fullFlagName);
163                     continue;
164                 }
165                 String packageName = fullFlagName.substring(0, idx);
166                 String flagName = fullFlagName.substring(idx + 1);
167                 long overrideType =
168                         isLocal
169                                 ? StorageRequestMessage.LOCAL_ON_REBOOT
170                                 : StorageRequestMessage.SERVER_ON_REBOOT;
171                 writeFlagOverrideRequest(requests, packageName, flagName, stagedValue,
172                     overrideType);
173                 ++num_requests;
174             }
175         }
176 
177         if (num_requests == 0) {
178             return;
179         }
180 
181         // send requests to aconfigd and obtain the return
182         InputStream returns = localSocket.send(requests.getBytes());
183 
184         // deserialize back using proto input stream
185         try {
186             parseAndLogAconfigdReturn(returns);
187         } catch (IOException ioe) {
188             Slog.e(TAG, "failed to parse aconfigd return", ioe);
189         }
190     }
191 
192     /** @hide */
listFlagsValueInNewStorage( AconfigdClientSocket localSocket)193     public static Map<String, AconfigdFlagInfo> listFlagsValueInNewStorage(
194             AconfigdClientSocket localSocket) {
195 
196         ProtoOutputStream requests = new ProtoOutputStream();
197         long msgsToken = requests.start(StorageRequestMessages.MSGS);
198         long msgToken = requests.start(StorageRequestMessage.LIST_STORAGE_MESSAGE);
199         requests.write(StorageRequestMessage.ListStorageMessage.ALL, 1);
200         requests.end(msgToken);
201         requests.end(msgsToken);
202 
203         InputStream inputStream = localSocket.send(requests.getBytes());
204         ProtoInputStream res = new ProtoInputStream(inputStream);
205         Map<String, AconfigdFlagInfo> flagMap = new HashMap<>();
206         Deque<Long> tokens = new ArrayDeque<>();
207         try {
208             while (res.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
209                 switch (res.getFieldNumber()) {
210                     case (int) StorageReturnMessages.MSGS:
211                         tokens.push(res.start(StorageReturnMessages.MSGS));
212                         break;
213                     case (int) StorageReturnMessage.LIST_STORAGE_MESSAGE:
214                         tokens.push(res.start(StorageReturnMessage.LIST_STORAGE_MESSAGE));
215                         while (res.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
216                             switch (res.getFieldNumber()) {
217                                 case (int) StorageReturnMessage.ListStorageReturnMessage.FLAGS:
218                                     tokens.push(
219                                             res.start(
220                                                     StorageReturnMessage.ListStorageReturnMessage
221                                                             .FLAGS));
222                                     AconfigdFlagInfo flagQueryReturnMessage = readFromProto(res);
223                                     flagMap.put(
224                                             flagQueryReturnMessage.getFullFlagName(),
225                                             flagQueryReturnMessage);
226                                     res.end(tokens.pop());
227                                     break;
228                                 default:
229                                     Slog.i(
230                                             TAG,
231                                             "Could not read undefined field: "
232                                                     + res.getFieldNumber());
233                             }
234                         }
235                         break;
236                     case (int) StorageReturnMessage.ERROR_MESSAGE:
237                         String errmsg = res.readString(StorageReturnMessage.ERROR_MESSAGE);
238                         Slog.w(TAG, "list request failed: " + errmsg);
239                         break;
240                     default:
241                         Slog.i(TAG, "Could not read undefined field: " + res.getFieldNumber());
242                 }
243             }
244         } catch (IOException e) {
245             Slog.e(TAG, "Failed to read protobuf input stream.", e);
246         }
247 
248         while (!tokens.isEmpty()) {
249             res.end(tokens.pop());
250         }
251         return flagMap;
252     }
253 
readFromProto(ProtoInputStream protoInputStream)254     private static AconfigdFlagInfo readFromProto(ProtoInputStream protoInputStream)
255             throws IOException {
256         AconfigdFlagInfo.Builder builder = new AconfigdFlagInfo.Builder();
257         while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
258             switch (protoInputStream.getFieldNumber()) {
259                 case (int) StorageReturnMessage.FlagQueryReturnMessage.PACKAGE_NAME:
260                     builder.setPackageName(
261                             protoInputStream.readString(
262                                     StorageReturnMessage.FlagQueryReturnMessage.PACKAGE_NAME));
263                     break;
264                 case (int) StorageReturnMessage.FlagQueryReturnMessage.FLAG_NAME:
265                     builder.setFlagName(
266                             protoInputStream.readString(
267                                     StorageReturnMessage.FlagQueryReturnMessage.FLAG_NAME));
268                     break;
269                 case (int) StorageReturnMessage.FlagQueryReturnMessage.SERVER_FLAG_VALUE:
270                     builder.setServerFlagValue(
271                             protoInputStream.readString(
272                                     StorageReturnMessage.FlagQueryReturnMessage.SERVER_FLAG_VALUE));
273                     break;
274                 case (int) StorageReturnMessage.FlagQueryReturnMessage.LOCAL_FLAG_VALUE:
275                     builder.setLocalFlagValue(
276                             protoInputStream.readString(
277                                     StorageReturnMessage.FlagQueryReturnMessage.LOCAL_FLAG_VALUE));
278                     break;
279                 case (int) StorageReturnMessage.FlagQueryReturnMessage.BOOT_FLAG_VALUE:
280                     builder.setBootFlagValue(
281                             protoInputStream.readString(
282                                     StorageReturnMessage.FlagQueryReturnMessage.BOOT_FLAG_VALUE));
283                     break;
284                 case (int) StorageReturnMessage.FlagQueryReturnMessage.DEFAULT_FLAG_VALUE:
285                     builder.setDefaultFlagValue(
286                             protoInputStream.readString(
287                                     StorageReturnMessage.FlagQueryReturnMessage
288                                             .DEFAULT_FLAG_VALUE));
289                     break;
290                 case (int) StorageReturnMessage.FlagQueryReturnMessage.HAS_SERVER_OVERRIDE:
291                     builder.setHasServerOverride(
292                             protoInputStream.readBoolean(
293                                     StorageReturnMessage.FlagQueryReturnMessage
294                                             .HAS_SERVER_OVERRIDE));
295                     break;
296                 case (int) StorageReturnMessage.FlagQueryReturnMessage.HAS_LOCAL_OVERRIDE:
297                     builder.setHasLocalOverride(
298                             protoInputStream.readBoolean(
299                                     StorageReturnMessage.FlagQueryReturnMessage
300                                             .HAS_LOCAL_OVERRIDE));
301                     break;
302                 case (int) StorageReturnMessage.FlagQueryReturnMessage.IS_READWRITE:
303                     builder.setIsReadWrite(
304                             protoInputStream.readBoolean(
305                                     StorageReturnMessage.FlagQueryReturnMessage.IS_READWRITE));
306                     break;
307                 default:
308                     Slog.i(
309                             TAG,
310                             "Could not read undefined field: " + protoInputStream.getFieldNumber());
311             }
312         }
313         return builder.build();
314     }
315 }
316