17582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan/* 27582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * Copyright (C) 2017 The Android Open Source Project 37582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 47582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * Licensed under the Apache License, Version 2.0 (the "License"); 57582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * you may not use this file except in compliance with the License. 67582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * You may obtain a copy of the License at 77582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 87582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * http://www.apache.org/licenses/LICENSE-2.0 97582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 107582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * Unless required by applicable law or agreed to in writing, software 117582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * distributed under the License is distributed on an "AS IS" BASIS, 127582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * See the License for the specific language governing permissions and 147582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * limitations under the License. 157582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan */ 167582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanpackage com.google.android.car.diagnosticverifier; 177582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 187582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.app.Activity; 197582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.car.Car; 207582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.car.CarNotConnectedException; 2125e89468119cd0b109f62c751446dfdd8e903143Enrico Granataimport android.car.diagnostic.CarDiagnosticEvent; 2225e89468119cd0b109f62c751446dfdd8e903143Enrico Granataimport android.car.diagnostic.CarDiagnosticManager; 237582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.car.hardware.CarSensorManager; 247582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.BroadcastReceiver; 257582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.ComponentName; 267582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.Context; 277582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.Intent; 287582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.IntentFilter; 297582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.content.ServiceConnection; 307582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.os.Bundle; 317582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.os.Environment; 327582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.os.IBinder; 3313df19ec3c22c58ee910aba2ab6536463178e22aChao Yanimport android.support.v7.widget.LinearLayoutManager; 3413df19ec3c22c58ee910aba2ab6536463178e22aChao Yanimport android.support.v7.widget.RecyclerView; 357582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.util.JsonWriter; 367582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.util.Log; 377582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport android.widget.TextView; 387582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 397582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport com.google.android.car.diagnosticverifier.DiagnosticVerifier.VerificationResult; 407582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 417582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.io.File; 427582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.io.FileInputStream; 437582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.io.FileOutputStream; 447582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.io.IOException; 457582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.io.OutputStreamWriter; 4613df19ec3c22c58ee910aba2ab6536463178e22aChao Yanimport java.util.ArrayList; 477582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanimport java.util.List; 487582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 497582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan/** 507582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * The test app that does the verification of car diagnostic event data. It first reads the 517582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * truth (golden) event data from a JSON file upon starting. Then a broadcast intent such as: 527582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 537582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * am broadcast -a com.google.android.car.diagnosticverifier.action.START_LISTEN 547582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 557582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * will activate the car diagnostics listener. The test app will receive events from diagnostic API. 567582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * Once it receives all the events, a broadcast intent with "stop" action such as: 577582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 587582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * am broadcast -a com.google.android.car.diagnosticverifier.action.STOP_LISTEN 597582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 607582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * will deactivate the listener and start the verification process (see {@link DiagnosticVerifier}). 617582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * 627582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan * Verification result will be output to a JSON file on device. 637582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan */ 647582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yanpublic class MainActivity extends Activity { 657582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public static final String TAG = "DiagnosticVerifier"; 667582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 677582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public static final String ACTION_START_LISTEN = 687582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan "com.google.android.car.diagnosticverifier.action.START_LISTEN"; 697582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public static final String ACTION_STOP_LISTEN = 707582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan "com.google.android.car.diagnosticverifier.action.STOP_LISTEN"; 717582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 727582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private static final String DEFAULT_JSON_PATH = "/data/local/tmp/diag.json"; 737582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 747582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private static final String JSON_PATH_KEY = "jsonPath"; 757582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private static final String JSON_RESULT = "verification_result.json"; 767582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 777582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private Car mCar; 787582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private CarDiagnosticManager mCarDiagnosticManager; 797582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private DiagnosticListener mDiagnosticListener; 807582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private BroadcastReceiver mBroadcastReceiver; 817582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private DiagnosticVerifier mVerifier; 827582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private TextView mStatusBar; 8313df19ec3c22c58ee910aba2ab6536463178e22aChao Yan private RecyclerView mRecyclerView; 8413df19ec3c22c58ee910aba2ab6536463178e22aChao Yan private VerificationResultAdapter mResultAdapter; 857582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private boolean mListening = false; 867582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 877582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private final ServiceConnection mCarConnectionListener = 887582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan new ServiceConnection() { 897582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 907582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public void onServiceConnected(ComponentName name, IBinder iBinder) { 917582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, "Connected to " + name.flattenToString()); 927582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan try { 937582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCarDiagnosticManager = 947582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE); 957582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } catch (CarNotConnectedException e) { 967582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.e(TAG, "Failed to get a connection", e); 977582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 987582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 997582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1007582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 1017582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public void onServiceDisconnected(ComponentName name) { 1027582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, "Disconnected from " + name.flattenToString()); 1037582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1047582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCar = null; 1057582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCarDiagnosticManager = null; 1067582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1077582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan }; 1087582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1097582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan class DiagnosticListener implements CarDiagnosticManager.OnDiagnosticEventListener { 1107582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1117582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 1127582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) { 1137582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.v(TAG, "Received Car Diagnostic Event: " + carDiagnosticEvent.toString()); 1147582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mVerifier.receiveEvent(carDiagnosticEvent); 1157582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1167582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1177582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1187582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan class VerifierMsgReceiver extends BroadcastReceiver { 1197582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1207582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 1217582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public void onReceive(Context context, Intent intent) { 1227582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan String action = intent.getAction(); 1237582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, "Received intent with action: " + action); 1247582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (ACTION_START_LISTEN.equals(action)) { 1257582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan try { 1267582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan startListen(); 1277582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } catch (CarNotConnectedException e) { 1287582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.e(TAG, "Failed to listen for car diagnostic event", e); 1297582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1307582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } else if (ACTION_STOP_LISTEN.equals(action)) { 1317582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan stopListen(); 1327582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan verify(); 1337582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1347582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1357582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1367582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1377582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 1387582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan public void onCreate(Bundle savedInstanceState) { 1397582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan super.onCreate(savedInstanceState); 1407582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan setContentView(R.layout.verifier_activity); 1417582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1427582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mStatusBar = (TextView) findViewById(R.id.status_bar); 1437582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 14413df19ec3c22c58ee910aba2ab6536463178e22aChao Yan //Setting up RecyclerView to show verification result messages 14513df19ec3c22c58ee910aba2ab6536463178e22aChao Yan mRecyclerView = (RecyclerView) findViewById(R.id.verification_results); 14613df19ec3c22c58ee910aba2ab6536463178e22aChao Yan LinearLayoutManager layoutManager = 14713df19ec3c22c58ee910aba2ab6536463178e22aChao Yan new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); 14813df19ec3c22c58ee910aba2ab6536463178e22aChao Yan mRecyclerView.setLayoutManager(layoutManager); 14913df19ec3c22c58ee910aba2ab6536463178e22aChao Yan mResultAdapter = new VerificationResultAdapter(); 15013df19ec3c22c58ee910aba2ab6536463178e22aChao Yan mRecyclerView.setAdapter(mResultAdapter); 15113df19ec3c22c58ee910aba2ab6536463178e22aChao Yan 15213df19ec3c22c58ee910aba2ab6536463178e22aChao Yan //Connect to car service 1537582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCar = Car.createCar(this, mCarConnectionListener); 1547582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCar.connect(); 1557582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 15613df19ec3c22c58ee910aba2ab6536463178e22aChao Yan //Initialize broadcast intent receiver 1577582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mBroadcastReceiver = new VerifierMsgReceiver(); 1587582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan IntentFilter filter = new IntentFilter(ACTION_START_LISTEN); 1597582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan filter.addAction(ACTION_STOP_LISTEN); 1607582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan this.registerReceiver(mBroadcastReceiver, filter); 1617582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 16213df19ec3c22c58ee910aba2ab6536463178e22aChao Yan //Read golden diagnostics JSON file 1637582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan String jsonPath = this.getIntent().getStringExtra(JSON_PATH_KEY); 1647582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (jsonPath == null || jsonPath.isEmpty()) { 1657582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan jsonPath = DEFAULT_JSON_PATH; 1667582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1677582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan List<CarDiagnosticEvent> events; 1687582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan try { 1697582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan events = DiagnosticJsonConverter.readFromJson(new FileInputStream(jsonPath)); 1707582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } catch (IOException e) { 1717582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan throw new RuntimeException("Failed to read diagnostic JSON file", e); 1727582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1737582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, String.format("Read %d events from JSON file %s.", events.size(), jsonPath)); 1747582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1757582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mVerifier = new DiagnosticVerifier(events); 1767582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1777582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1787582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan @Override 1797582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan protected void onDestroy() { 1807582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (mCar != null) { 1817582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCar.disconnect(); 1827582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1837582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mVerifier = null; 1847582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan this.unregisterReceiver(mBroadcastReceiver); 1857582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1867582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 1877582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private void startListen() throws CarNotConnectedException { 1887582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (mListening) { 1897582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan return; 1907582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1917582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (mDiagnosticListener == null) { 1927582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mDiagnosticListener = new DiagnosticListener(); 1937582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 1947582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.i(TAG, "Start listening for car diagnostics events"); 1957582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCarDiagnosticManager.registerListener( 1967582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mDiagnosticListener, 1977582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan CarDiagnosticManager.FRAME_TYPE_LIVE, 1987582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan CarSensorManager.SENSOR_RATE_NORMAL); 1997582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCarDiagnosticManager.registerListener( 2007582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mDiagnosticListener, 2017582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan CarDiagnosticManager.FRAME_TYPE_FREEZE, 2027582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan CarSensorManager.SENSOR_RATE_NORMAL); 2037582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2047582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mListening = true; 2057582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mStatusBar.setText(R.string.status_receiving); 2067582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2077582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2087582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private void stopListen() { 2097582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.i(TAG, "Stop listening for car diagnostics events"); 2107582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mCarDiagnosticManager.unregisterListener(mDiagnosticListener); 2117582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mListening = false; 2127582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2137582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2147582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private boolean isExternalStorageWritable() { 2157582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan String state = Environment.getExternalStorageState(); 2167582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan return Environment.MEDIA_MOUNTED.equals(state); 2177582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2187582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2197582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private File getResultJsonFile() throws IOException { 2207582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (!isExternalStorageWritable()) { 2217582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan throw new IOException("External storage is not writable. Cannot save content"); 2227582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2237582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2247582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan File resultJson = new File(Environment.getExternalStoragePublicDirectory( 2257582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Environment.DIRECTORY_DOCUMENTS), JSON_RESULT); 2267582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (!resultJson.getParentFile().mkdirs()) { 2277582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.w(TAG, "Parent directory may already exist"); 2287582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2297582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan return resultJson; 2307582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2317582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2327582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan private void verify() { 2337582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, "Start verifying car diagnostics events"); 2347582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mStatusBar.setText(R.string.status_verifying); 2357582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan List<VerificationResult> results = mVerifier.verify(); 2367582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan mStatusBar.setText(R.string.status_done); 2377582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2387582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan if (results.isEmpty()) { 2397582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.d(TAG, "Verification result is empty."); 2407582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan return; 2417582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2427582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 24313df19ec3c22c58ee910aba2ab6536463178e22aChao Yan List<String> resultMessages = new ArrayList<>(); 2447582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan try { 2457582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan File resultJson = getResultJsonFile(); 2467582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan JsonWriter writer = new JsonWriter( 2477582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan new OutputStreamWriter(new FileOutputStream(resultJson))); 2487582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 2497582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan writer.beginArray(); 2507582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan for (VerificationResult result : results) { 25113df19ec3c22c58ee910aba2ab6536463178e22aChao Yan resultMessages.add("Test case: " + result.testCase); 25213df19ec3c22c58ee910aba2ab6536463178e22aChao Yan resultMessages.add("Result: " + result.success); 25313df19ec3c22c58ee910aba2ab6536463178e22aChao Yan resultMessages.add(result.errorMessage); 2547582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan result.writeToJson(writer); 2557582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2567582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan writer.endArray(); 2577582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan writer.flush(); 2587582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan writer.close(); 2597582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.i(TAG, "Verification result: " + resultJson.getAbsolutePath()); 2607582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } catch (IOException e) { 2617582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan Log.e(TAG, "Failed to save verification result.", e); 2627582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 26313df19ec3c22c58ee910aba2ab6536463178e22aChao Yan mResultAdapter.setResultMessages(resultMessages); 2647582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan } 2657582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan} 2667582f2e1cc5df01f51b97554a00d44f393c922b6Chao Yan 267