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