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 package com.android.bedstead.dpmwrapper; 17 18 import static com.android.bedstead.dpmwrapper.Utils.EXTRA_ARG_PREFIX; 19 import static com.android.bedstead.dpmwrapper.Utils.VERBOSE; 20 21 import android.annotation.Nullable; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.os.CpuUsageInfo; 25 import android.os.Parcelable; 26 import android.util.ArraySet; 27 import android.util.Log; 28 29 import java.io.Serializable; 30 import java.security.PrivateKey; 31 import java.security.cert.Certificate; 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Set; 35 36 final class DataFormatter { 37 38 private static final String TAG = DataFormatter.class.getSimpleName(); 39 40 // NOTE: Bundle has a putObject() method that would make it much easier to marshal the args, 41 // but unfortunately there is no Intent.putObjectExtra() method (and intent.getBundle() returns 42 // a copy, so we need to explicitly marshal any supported type). 43 private static final String TYPE_BOOLEAN = "boolean"; 44 private static final String TYPE_INT = "int"; 45 private static final String TYPE_LONG = "long"; 46 private static final String TYPE_BYTE_ARRAY = "byte_array"; 47 private static final String TYPE_FLOAT_ARRAY = "float_array"; 48 private static final String TYPE_STRING_OR_CHAR_SEQUENCE = "string"; 49 private static final String TYPE_PARCELABLE = "parcelable"; 50 private static final String TYPE_SERIALIZABLE = "serializable"; 51 private static final String TYPE_ARRAY_LIST_STRING = "array_list_string"; 52 private static final String TYPE_ARRAY_LIST_PARCELABLE = "array_list_parcelable"; 53 // NOTE: the value of a TYPE_ARRAY_LIST_BYTE_ARRAY is its length - the individual elements 54 // are contained on separate extras, one per index, whose name is defined by 55 // getExtraNameForArrayListElement() 56 private static final String TYPE_ARRAY_LIST_BYTE_ARRAY = "array_list_byte_array"; 57 private static final String TYPE_SET_STRING = "set_string"; 58 // Must handle each array of parcelable subclass , as they need to be explicitly converted 59 private static final String TYPE_CPU_USAFE_INFO_ARRAY = "cpu_usage_info_array"; 60 private static final String TYPE_CERTIFICATE = "certificate"; 61 private static final String TYPE_PRIVATE_KEY = "private_key"; 62 63 // Used when a method is called passing a null argument - the proper method will have to be 64 // infered using findMethod() 65 private static final String TYPE_NULL = "null"; 66 addArg(Intent intent, Object[] args, int index)67 static void addArg(Intent intent, Object[] args, int index) { 68 Object value = args[index]; 69 String extraTypeName = getArgExtraTypeName(index); 70 String extraValueName = getArgExtraValueName(index); 71 if (VERBOSE) { 72 Log.v(TAG, "addArg(" + index + "): typeName= " + extraTypeName 73 + ", valueName= " + extraValueName); 74 } 75 if (value == null) { 76 logMarshalling("Adding Null", index, extraTypeName, TYPE_NULL, extraValueName, value); 77 intent.putExtra(extraTypeName, TYPE_NULL); 78 return; 79 80 } 81 if ((value instanceof Boolean)) { 82 logMarshalling("Adding Boolean", index, extraTypeName, TYPE_BOOLEAN, extraValueName, 83 value); 84 intent.putExtra(extraTypeName, TYPE_BOOLEAN); 85 intent.putExtra(extraValueName, ((Boolean) value).booleanValue()); 86 return; 87 } 88 if ((value instanceof Integer)) { 89 logMarshalling("Adding Integer", index, extraTypeName, TYPE_INT, extraValueName, value); 90 intent.putExtra(extraTypeName, TYPE_INT); 91 intent.putExtra(extraValueName, ((Integer) value).intValue()); 92 return; 93 } 94 if ((value instanceof Long)) { 95 logMarshalling("Adding Long", index, extraTypeName, TYPE_LONG, extraValueName, value); 96 intent.putExtra(extraTypeName, TYPE_LONG); 97 intent.putExtra(extraValueName, ((Long) value).longValue()); 98 return; 99 } 100 if ((value instanceof byte[])) { 101 logMarshalling("Adding byte[]", index, extraTypeName, TYPE_BYTE_ARRAY, extraValueName, 102 value); 103 intent.putExtra(extraTypeName, TYPE_BYTE_ARRAY); 104 intent.putExtra(extraValueName, (byte[]) value); 105 return; 106 } 107 if ((value instanceof float[])) { 108 logMarshalling("Adding float[]", index, extraTypeName, TYPE_FLOAT_ARRAY, extraValueName, 109 value); 110 intent.putExtra(extraTypeName, TYPE_FLOAT_ARRAY); 111 intent.putExtra(extraValueName, (float[]) value); 112 return; 113 } 114 if ((value instanceof CpuUsageInfo[])) { 115 logMarshalling("Adding CpuUsageInfo[]", index, extraTypeName, 116 TYPE_CPU_USAFE_INFO_ARRAY, extraValueName, value); 117 intent.putExtra(extraTypeName, TYPE_CPU_USAFE_INFO_ARRAY); 118 intent.putExtra(extraValueName, (CpuUsageInfo[]) value); 119 return; 120 } 121 if ((value instanceof CharSequence)) { 122 logMarshalling("Adding CharSequence", index, extraTypeName, 123 TYPE_STRING_OR_CHAR_SEQUENCE, extraValueName, value); 124 intent.putExtra(extraTypeName, TYPE_STRING_OR_CHAR_SEQUENCE); 125 intent.putExtra(extraValueName, (CharSequence) value); 126 return; 127 } 128 if (value instanceof PrivateKey) { 129 if (!(value instanceof Serializable)) { 130 throw new IllegalArgumentException("PrivateKey is not Serializable: " + value); 131 } 132 logMarshalling("Adding PrivateKey", index, extraTypeName, TYPE_PRIVATE_KEY, 133 extraValueName, value); 134 intent.putExtra(extraTypeName, TYPE_PRIVATE_KEY); 135 intent.putExtra(extraValueName, (Serializable) value); 136 return; 137 } 138 if ((value instanceof Parcelable)) { 139 logMarshalling("Adding Parcelable", index, extraTypeName, TYPE_PARCELABLE, 140 extraValueName, value); 141 intent.putExtra(extraTypeName, TYPE_PARCELABLE); 142 intent.putExtra(extraValueName, (Parcelable) value); 143 return; 144 } 145 146 if (value instanceof Certificate) { 147 if (!(value instanceof Serializable)) { 148 throw new IllegalArgumentException("Certificate is not Serializable: " + value); 149 } 150 logMarshalling("Adding Certificate", index, extraTypeName, TYPE_CERTIFICATE, 151 extraValueName, value); 152 intent.putExtra(extraTypeName, TYPE_CERTIFICATE); 153 intent.putExtra(extraValueName, (Serializable) value); 154 return; 155 } 156 157 if ((value instanceof List<?>)) { 158 List<?> list = (List<?>) value; 159 160 String type = null; 161 if (list.isEmpty()) { 162 Log.w(TAG, "Empty list at index " + index + "; assuming it's List<String>"); 163 type = TYPE_ARRAY_LIST_STRING; 164 } else { 165 Object firstItem = list.get(0); 166 if (firstItem instanceof String) { 167 type = TYPE_ARRAY_LIST_STRING; 168 } else if (firstItem instanceof Parcelable) { 169 type = TYPE_ARRAY_LIST_PARCELABLE; 170 } else if (firstItem instanceof byte[]) { 171 type = TYPE_ARRAY_LIST_BYTE_ARRAY; 172 } else { 173 throw new IllegalArgumentException("Unsupported List type at index " + index 174 + ": " + firstItem); 175 } 176 } 177 178 logMarshalling("Adding " + type, index, extraTypeName, type, extraValueName, value); 179 intent.putExtra(extraTypeName, type); 180 switch (type) { 181 case TYPE_ARRAY_LIST_STRING: 182 @SuppressWarnings("unchecked") 183 ArrayList<String> arrayListString = (value instanceof ArrayList) 184 ? (ArrayList<String>) list 185 : new ArrayList<>((List<String>) list); 186 intent.putStringArrayListExtra(extraValueName, arrayListString); 187 break; 188 case TYPE_ARRAY_LIST_PARCELABLE: 189 @SuppressWarnings("unchecked") 190 ArrayList<Parcelable> arrayListParcelable = (value instanceof ArrayList) 191 ? (ArrayList<Parcelable>) list 192 : new ArrayList<>((List<Parcelable>) list); 193 intent.putParcelableArrayListExtra(extraValueName, arrayListParcelable); 194 break; 195 case TYPE_ARRAY_LIST_BYTE_ARRAY: 196 @SuppressWarnings("unchecked") 197 ArrayList<byte[]> arrayListByteArray = (value instanceof ArrayList) 198 ? (ArrayList<byte[]>) list 199 : new ArrayList<>((List<byte[]>) list); 200 int listSize = arrayListByteArray.size(); 201 intent.putExtra(extraValueName, listSize); 202 for (int i = 0; i < listSize; i++) { 203 intent.putExtra(getExtraNameForArrayListElement(extraValueName, i), 204 arrayListByteArray.get(i)); 205 } 206 break; 207 default: 208 // should never happen because type is checked above 209 throw new AssertionError("invalid type conversion: " + type); 210 } 211 return; 212 } 213 214 // TODO(b/176993670): ArraySet<> is encapsulate as ArrayList<>, so most of the code below 215 // could be reused (right now it was copy-and-paste from ArrayList<>, minus the Parcelable 216 // part. 217 if ((value instanceof Set<?>)) { 218 Set<?> set = (Set<?>) value; 219 220 String type = null; 221 if (set.isEmpty()) { 222 Log.w(TAG, "Empty set at index " + index + "; assuming it's Set<String>"); 223 type = TYPE_SET_STRING; 224 } else { 225 Object firstItem = set.iterator().next(); 226 if (firstItem instanceof String) { 227 type = TYPE_SET_STRING; 228 } else { 229 throw new IllegalArgumentException("Unsupported Set type at index " 230 + index + ": " + firstItem); 231 } 232 } 233 234 logMarshalling("Adding " + type, index, extraTypeName, type, extraValueName, value); 235 intent.putExtra(extraTypeName, type); 236 switch (type) { 237 case TYPE_SET_STRING: 238 @SuppressWarnings("unchecked") 239 Set<String> stringSet = (Set<String>) value; 240 intent.putStringArrayListExtra(extraValueName, new ArrayList<>(stringSet)); 241 break; 242 default: 243 // should never happen because type is checked above 244 throw new AssertionError("invalid type conversion: " + type); 245 } 246 return; 247 } 248 249 if ((value instanceof Serializable)) { 250 logMarshalling("Adding Serializable", index, extraTypeName, TYPE_SERIALIZABLE, 251 extraValueName, value); 252 intent.putExtra(extraTypeName, TYPE_SERIALIZABLE); 253 intent.putExtra(extraValueName, (Serializable) value); 254 return; 255 } 256 257 throw new IllegalArgumentException("Unsupported value type at index " + index + ": " 258 + (value == null ? "null" : value.getClass())); 259 } 260 getExtraNameForArrayListElement(String baseExtraName, int index)261 private static String getExtraNameForArrayListElement(String baseExtraName, int index) { 262 return baseExtraName + "_" + index; 263 } 264 getArg(Bundle extras, Object[] args, @Nullable Class<?>[] parameterTypes, int index)265 static void getArg(Bundle extras, Object[] args, @Nullable Class<?>[] parameterTypes, 266 int index) { 267 String extraTypeName = getArgExtraTypeName(index); 268 String extraValueName = getArgExtraValueName(index); 269 String type = extras.getString(extraTypeName); 270 if (VERBOSE) { 271 Log.v(TAG, "getArg(" + index + "): typeName= " + extraTypeName + ", type=" + type 272 + ", valueName= " + extraValueName); 273 } 274 Object value = null; 275 switch (type) { 276 case TYPE_NULL: 277 logMarshalling("Got null", index, extraTypeName, type, extraValueName, value); 278 break; 279 case TYPE_SET_STRING: 280 @SuppressWarnings("unchecked") 281 ArrayList<String> list = (ArrayList<String>) extras.get(extraValueName); 282 value = new ArraySet<String>(list); 283 logMarshalling("Got ArraySet<String>", index, extraTypeName, type, extraValueName, 284 value); 285 break; 286 case TYPE_CPU_USAFE_INFO_ARRAY: 287 Parcelable[] raw = (Parcelable[]) extras.get(extraValueName); 288 CpuUsageInfo[] cast = new CpuUsageInfo[raw.length]; 289 for (int i = 0; i < raw.length; i++) { 290 cast[i] = (CpuUsageInfo) raw[i]; 291 } 292 value = cast; 293 logMarshalling("Got CpuUsageInfo[]", index, extraTypeName, type, extraValueName, 294 value); 295 break; 296 case TYPE_ARRAY_LIST_BYTE_ARRAY: 297 int size = extras.getInt(extraValueName); 298 ArrayList<byte[]> array = new ArrayList<>(size); 299 for (int i = 0; i < size; i++) { 300 String extraName = getExtraNameForArrayListElement(extraValueName, i); 301 array.add(extras.getByteArray(extraName)); 302 } 303 value = array; 304 break; 305 case TYPE_ARRAY_LIST_STRING: 306 case TYPE_ARRAY_LIST_PARCELABLE: 307 case TYPE_BYTE_ARRAY: 308 case TYPE_FLOAT_ARRAY: 309 case TYPE_BOOLEAN: 310 case TYPE_INT: 311 case TYPE_LONG: 312 case TYPE_STRING_OR_CHAR_SEQUENCE: 313 case TYPE_PARCELABLE: 314 case TYPE_SERIALIZABLE: 315 case TYPE_CERTIFICATE: 316 case TYPE_PRIVATE_KEY: 317 value = extras.get(extraValueName); 318 logMarshalling("Got generic", index, extraTypeName, type, extraValueName, value); 319 break; 320 default: 321 throw new IllegalArgumentException("Unsupported value type at index " + index + ": " 322 + extraTypeName); 323 } 324 if (parameterTypes != null) { 325 Class<?> parameterType = null; 326 // Must convert special types (like primitive to Object, generic list to list, etc...), 327 // but not those that can be inferred from getClass() (like String or array) 328 switch (type) { 329 case TYPE_NULL: 330 break; 331 case TYPE_BOOLEAN: 332 parameterType = boolean.class; 333 break; 334 case TYPE_INT: 335 parameterType = int.class; 336 break; 337 case TYPE_LONG: 338 parameterType = long.class; 339 break; 340 case TYPE_STRING_OR_CHAR_SEQUENCE: 341 // A String is a CharSequence, but most methods take String, so we're assuming 342 // a string and handle the exceptional cases on findMethod() 343 parameterType = String.class; 344 break; 345 case TYPE_ARRAY_LIST_STRING: 346 parameterType = List.class; 347 break; 348 case TYPE_SET_STRING: 349 parameterType = Set.class; 350 break; 351 case TYPE_PRIVATE_KEY: 352 parameterType = PrivateKey.class; 353 break; 354 case TYPE_CERTIFICATE: 355 parameterType = Certificate.class; 356 break; 357 default: 358 parameterType = value.getClass(); 359 } 360 parameterTypes[index] = parameterType; 361 } 362 args[index] = value; 363 } 364 getArgExtraTypeName(int index)365 static String getArgExtraTypeName(int index) { 366 return EXTRA_ARG_PREFIX + index + "_type"; 367 } 368 getArgExtraValueName(int index)369 static String getArgExtraValueName(int index) { 370 return EXTRA_ARG_PREFIX + index + "_value"; 371 } 372 logMarshalling(String operation, int index, String typeName, String type, String valueName, Object value)373 private static void logMarshalling(String operation, int index, String typeName, 374 String type, String valueName, Object value) { 375 if (VERBOSE) { 376 Log.v(TAG, operation + " on " + index + ": typeName=" + typeName + ", type=" + type 377 + ", valueName=" + valueName + ", value=" + value); 378 } 379 } 380 DataFormatter()381 private DataFormatter() { 382 throw new UnsupportedOperationException("contains only static methods"); 383 } 384 } 385