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