1 /* 2 * Copyright (C) 2015 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.compatibility.common.deviceinfo; 17 18 import android.app.Activity; 19 import android.content.Intent; 20 import android.net.Uri; 21 import android.os.Bundle; 22 import android.os.Environment; 23 import android.text.TextUtils; 24 import android.util.JsonWriter; 25 import android.util.Log; 26 27 import java.io.File; 28 import java.io.FileOutputStream; 29 import java.io.OutputStreamWriter; 30 import java.nio.charset.StandardCharsets; 31 import java.util.Arrays; 32 import java.util.concurrent.CountDownLatch; 33 34 /** 35 * Collect device information on target device and write to a JSON file. 36 */ 37 public abstract class DeviceInfoActivity extends Activity { 38 39 /** Device info result code: collector failed to complete. */ 40 private static final int DEVICE_INFO_RESULT_FAILED = -2; 41 /** Device info result code: collector completed with error. */ 42 private static final int DEVICE_INFO_RESULT_ERROR = -1; 43 /** Device info result code: collector has started but not completed. */ 44 private static final int DEVICE_INFO_RESULT_STARTED = 0; 45 /** Device info result code: collector completed success. */ 46 private static final int DEVICE_INFO_RESULT_OK = 1; 47 48 private static final int MAX_STRING_VALUE_LENGTH = 1000; 49 private static final int MAX_ARRAY_LENGTH = 1000; 50 51 private static final String LOG_TAG = "DeviceInfoActivity"; 52 53 private CountDownLatch mDone = new CountDownLatch(1); 54 private JsonWriter mJsonWriter = null; 55 private String mResultFilePath = null; 56 private String mErrorMessage = "Collector has started."; 57 private int mResultCode = DEVICE_INFO_RESULT_STARTED; 58 59 @Override onCreate(Bundle savedInstanceState)60 public void onCreate(Bundle savedInstanceState) { 61 super.onCreate(savedInstanceState); 62 if (createFilePath()) { 63 createJsonWriter(); 64 startJsonWriter(); 65 collectDeviceInfo(); 66 closeJsonWriter(); 67 68 if (mResultCode == DEVICE_INFO_RESULT_STARTED) { 69 mResultCode = DEVICE_INFO_RESULT_OK; 70 } 71 } 72 73 Intent data = new Intent(); 74 if (mResultCode == DEVICE_INFO_RESULT_OK) { 75 data.setData(Uri.parse(mResultFilePath)); 76 setResult(RESULT_OK, data); 77 } else { 78 data.setData(Uri.parse(mErrorMessage)); 79 setResult(RESULT_CANCELED, data); 80 } 81 82 mDone.countDown(); 83 finish(); 84 } 85 86 /** 87 * Method to collect device information. 88 */ collectDeviceInfo()89 protected abstract void collectDeviceInfo(); 90 waitForActivityToFinish()91 void waitForActivityToFinish() { 92 try { 93 mDone.await(); 94 } catch (Exception e) { 95 failed("Exception while waiting for activity to finish: " + e.getMessage()); 96 } 97 } 98 99 /** 100 * Returns the error message if collector did not complete successfully. 101 */ getErrorMessage()102 String getErrorMessage() { 103 if (mResultCode == DEVICE_INFO_RESULT_OK) { 104 return null; 105 } 106 return mErrorMessage; 107 } 108 109 /** 110 * Returns the path to the json file if collector completed successfully. 111 */ getResultFilePath()112 String getResultFilePath() { 113 if (mResultCode == DEVICE_INFO_RESULT_OK) { 114 return mResultFilePath; 115 } 116 return null; 117 } 118 error(String message)119 private void error(String message) { 120 mResultCode = DEVICE_INFO_RESULT_ERROR; 121 mErrorMessage = message; 122 Log.e(LOG_TAG, message); 123 } 124 failed(String message)125 private void failed(String message) { 126 mResultCode = DEVICE_INFO_RESULT_FAILED; 127 mErrorMessage = message; 128 Log.e(LOG_TAG, message); 129 } 130 createFilePath()131 private boolean createFilePath() { 132 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 133 failed("External storage is not mounted"); 134 return false; 135 } 136 final File dir = new File(Environment.getExternalStorageDirectory(), "device-info-files"); 137 if (!dir.mkdirs() && !dir.isDirectory()) { 138 failed("Cannot create directory for device info files"); 139 return false; 140 } 141 142 // Create file at /sdcard/device-info-files/<class_name>.deviceinfo.json 143 final File jsonFile = new File(dir, getClass().getSimpleName() + ".deviceinfo.json"); 144 try { 145 jsonFile.createNewFile(); 146 } catch (Exception e) { 147 failed("Cannot create file to collect device info"); 148 return false; 149 } 150 mResultFilePath = jsonFile.getAbsolutePath(); 151 return true; 152 } 153 createJsonWriter()154 private void createJsonWriter() { 155 try { 156 FileOutputStream out = new FileOutputStream(mResultFilePath); 157 mJsonWriter = new JsonWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); 158 // TODO(agathaman): remove to make json output less pretty 159 mJsonWriter.setIndent(" "); 160 } catch (Exception e) { 161 failed("Failed to create JSON writer: " + e.getMessage()); 162 } 163 } 164 startJsonWriter()165 private void startJsonWriter() { 166 try { 167 mJsonWriter.beginObject(); 168 } catch (Exception e) { 169 failed("Failed to begin JSON object: " + e.getMessage()); 170 } 171 } 172 closeJsonWriter()173 private void closeJsonWriter() { 174 try { 175 mJsonWriter.endObject(); 176 mJsonWriter.close(); 177 } catch (Exception e) { 178 failed("Failed to close JSON object: " + e.getMessage()); 179 } 180 } 181 182 /** 183 * Start a new group of result. 184 */ startGroup()185 public void startGroup() { 186 try { 187 mJsonWriter.beginObject(); 188 } catch (Exception e) { 189 error("Failed to begin JSON group: " + e.getMessage()); 190 } 191 } 192 193 /** 194 * Start a new group of result with specified name. 195 */ startGroup(String name)196 public void startGroup(String name) { 197 try { 198 mJsonWriter.name(name); 199 mJsonWriter.beginObject(); 200 } catch (Exception e) { 201 error("Failed to begin JSON group: " + e.getMessage()); 202 } 203 } 204 205 /** 206 * Complete adding result to the last started group. 207 */ endGroup()208 public void endGroup() { 209 try { 210 mJsonWriter.endObject(); 211 } catch (Exception e) { 212 error("Failed to end JSON group: " + e.getMessage()); 213 } 214 } 215 216 /** 217 * Start a new array of result. 218 */ startArray()219 public void startArray() { 220 try { 221 mJsonWriter.beginArray(); 222 } catch (Exception e) { 223 error("Failed to begin JSON array: " + e.getMessage()); 224 } 225 } 226 227 /** 228 * Start a new array of result with specified name. 229 */ startArray(String name)230 public void startArray(String name) { 231 checkName(name); 232 try { 233 mJsonWriter.name(name); 234 mJsonWriter.beginArray(); 235 } catch (Exception e) { 236 error("Failed to begin JSON array: " + e.getMessage()); 237 } 238 } 239 240 /** 241 * Complete adding result to the last started array. 242 */ endArray()243 public void endArray() { 244 try { 245 mJsonWriter.endArray(); 246 } catch (Exception e) { 247 error("Failed to end JSON group: " + e.getMessage()); 248 } 249 } 250 251 /** 252 * Add a double value result. 253 */ addResult(String name, double value)254 public void addResult(String name, double value) { 255 checkName(name); 256 try { 257 mJsonWriter.name(name).value(value); 258 } catch (Exception e) { 259 error("Failed to add result for type double: " + e.getMessage()); 260 } 261 } 262 263 /** 264 * Add a long value result. 265 */ addResult(String name, long value)266 public void addResult(String name, long value) { 267 checkName(name); 268 try { 269 mJsonWriter.name(name).value(value); 270 } catch (Exception e) { 271 error("Failed to add result for type long: " + e.getMessage()); 272 } 273 } 274 275 /** 276 * Add an int value result. 277 */ addResult(String name, int value)278 public void addResult(String name, int value) { 279 checkName(name); 280 try { 281 mJsonWriter.name(name).value((Number) value); 282 } catch (Exception e) { 283 error("Failed to add result for type int: " + e.getMessage()); 284 } 285 } 286 287 /** 288 * Add a boolean value result. 289 */ addResult(String name, boolean value)290 public void addResult(String name, boolean value) { 291 checkName(name); 292 try { 293 mJsonWriter.name(name).value(value); 294 } catch (Exception e) { 295 error("Failed to add result for type boolean: " + e.getMessage()); 296 } 297 } 298 299 /** 300 * Add a String value result. 301 */ addResult(String name, String value)302 public void addResult(String name, String value) { 303 checkName(name); 304 try { 305 mJsonWriter.name(name).value(checkString(value)); 306 } catch (Exception e) { 307 error("Failed to add result for type String: " + e.getMessage()); 308 } 309 } 310 311 /** 312 * Add a double array result. 313 */ addArray(String name, double[] list)314 public void addArray(String name, double[] list) { 315 checkName(name); 316 try { 317 mJsonWriter.name(name); 318 mJsonWriter.beginArray(); 319 for (double value : checkArray(list)) { 320 mJsonWriter.value(value); 321 } 322 mJsonWriter.endArray(); 323 } catch (Exception e) { 324 error("Failed to add result array for type double: " + e.getMessage()); 325 } 326 } 327 328 /** 329 * Add a long array result. 330 */ addArray(String name, long[] list)331 public void addArray(String name, long[] list) { 332 checkName(name); 333 try { 334 mJsonWriter.name(name); 335 mJsonWriter.beginArray(); 336 for (long value : checkArray(list)) { 337 mJsonWriter.value(value); 338 } 339 mJsonWriter.endArray(); 340 } catch (Exception e) { 341 error("Failed to add result array for type long: " + e.getMessage()); 342 } 343 } 344 345 /** 346 * Add an int array result. 347 */ addArray(String name, int[] list)348 public void addArray(String name, int[] list) { 349 checkName(name); 350 try { 351 mJsonWriter.name(name); 352 mJsonWriter.beginArray(); 353 for (int value : checkArray(list)) { 354 mJsonWriter.value((Number) value); 355 } 356 mJsonWriter.endArray(); 357 } catch (Exception e) { 358 error("Failed to add result array for type int: " + e.getMessage()); 359 } 360 } 361 362 /** 363 * Add a boolean array result. 364 */ addArray(String name, boolean[] list)365 public void addArray(String name, boolean[] list) { 366 checkName(name); 367 try { 368 mJsonWriter.name(name); 369 mJsonWriter.beginArray(); 370 for (boolean value : checkArray(list)) { 371 mJsonWriter.value(value); 372 } 373 mJsonWriter.endArray(); 374 } catch (Exception e) { 375 error("Failed to add result array for type boolean: " + e.getMessage()); 376 } 377 } 378 379 /** 380 * Add a String array result. 381 */ addArray(String name, String[] list)382 public void addArray(String name, String[] list) { 383 checkName(name); 384 try { 385 mJsonWriter.name(name); 386 mJsonWriter.beginArray(); 387 for (String value : checkArray(list)) { 388 mJsonWriter.value(checkString(value)); 389 } 390 mJsonWriter.endArray(); 391 } catch (Exception e) { 392 error("Failed to add result array for type Sting: " + e.getMessage()); 393 } 394 } 395 checkArray(boolean[] values)396 private static boolean[] checkArray(boolean[] values) { 397 if (values.length > MAX_ARRAY_LENGTH) { 398 return Arrays.copyOf(values, MAX_ARRAY_LENGTH); 399 } else { 400 return values; 401 } 402 } 403 checkArray(double[] values)404 private static double[] checkArray(double[] values) { 405 if (values.length > MAX_ARRAY_LENGTH) { 406 return Arrays.copyOf(values, MAX_ARRAY_LENGTH); 407 } else { 408 return values; 409 } 410 } 411 checkArray(int[] values)412 private static int[] checkArray(int[] values) { 413 if (values.length > MAX_ARRAY_LENGTH) { 414 return Arrays.copyOf(values, MAX_ARRAY_LENGTH); 415 } else { 416 return values; 417 } 418 } 419 checkArray(long[] values)420 private static long[] checkArray(long[] values) { 421 if (values.length > MAX_ARRAY_LENGTH) { 422 return Arrays.copyOf(values, MAX_ARRAY_LENGTH); 423 } else { 424 return values; 425 } 426 } 427 checkArray(String[] values)428 private static String[] checkArray(String[] values) { 429 if (values.length > MAX_ARRAY_LENGTH) { 430 return Arrays.copyOf(values, MAX_ARRAY_LENGTH); 431 } else { 432 return values; 433 } 434 } 435 checkString(String value)436 private static String checkString(String value) { 437 if (value.length() > MAX_STRING_VALUE_LENGTH) { 438 return value.substring(0, MAX_STRING_VALUE_LENGTH); 439 } 440 return value; 441 } 442 checkName(String value)443 private static String checkName(String value) { 444 if (TextUtils.isEmpty(value)) { 445 throw new NullPointerException(); 446 } 447 return value; 448 } 449 } 450 451