Home | History | Annotate | Download | only in diagnosticverifier
      1 /*
      2  * Copyright (C) 2017 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.google.android.car.diagnosticverifier;
     17 
     18 import android.app.Activity;
     19 import android.car.Car;
     20 import android.car.CarNotConnectedException;
     21 import android.car.diagnostic.CarDiagnosticEvent;
     22 import android.car.diagnostic.CarDiagnosticManager;
     23 import android.car.hardware.CarSensorManager;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ComponentName;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.content.ServiceConnection;
     30 import android.os.Bundle;
     31 import android.os.Environment;
     32 import android.os.IBinder;
     33 import android.util.JsonWriter;
     34 import android.util.Log;
     35 import android.widget.TextView;
     36 
     37 import androidx.recyclerview.widget.LinearLayoutManager;
     38 import androidx.recyclerview.widget.RecyclerView;
     39 
     40 import com.google.android.car.diagnosticverifier.DiagnosticVerifier.VerificationResult;
     41 
     42 import java.io.File;
     43 import java.io.FileInputStream;
     44 import java.io.FileOutputStream;
     45 import java.io.IOException;
     46 import java.io.OutputStreamWriter;
     47 import java.util.ArrayList;
     48 import java.util.List;
     49 
     50 /**
     51  * The test app that does the verification of car diagnostic event data. It first reads the
     52  * truth (golden) event data from a JSON file upon starting. Then a broadcast intent such as:
     53  *
     54  *     am broadcast -a com.google.android.car.diagnosticverifier.action.START_LISTEN
     55  *
     56  * will activate the car diagnostics listener. The test app will receive events from diagnostic API.
     57  * Once it receives all the events, a broadcast intent with "stop" action such as:
     58  *
     59  *     am broadcast -a com.google.android.car.diagnosticverifier.action.STOP_LISTEN
     60  *
     61  * will deactivate the listener and start the verification process (see {@link DiagnosticVerifier}).
     62  *
     63  * Verification result will be output to a JSON file on device.
     64  */
     65 public class MainActivity extends Activity {
     66     public static final String TAG = "DiagnosticVerifier";
     67 
     68     public static final String ACTION_START_LISTEN =
     69             "com.google.android.car.diagnosticverifier.action.START_LISTEN";
     70     public static final String ACTION_STOP_LISTEN =
     71             "com.google.android.car.diagnosticverifier.action.STOP_LISTEN";
     72 
     73     private static final String DEFAULT_JSON_PATH = "/data/local/tmp/diag.json";
     74 
     75     private static final String JSON_PATH_KEY = "jsonPath";
     76     private static final String JSON_RESULT = "verification_result.json";
     77 
     78     private Car mCar;
     79     private CarDiagnosticManager mCarDiagnosticManager;
     80     private DiagnosticListener mDiagnosticListener;
     81     private BroadcastReceiver mBroadcastReceiver;
     82     private DiagnosticVerifier mVerifier;
     83     private TextView mStatusBar;
     84     private RecyclerView mRecyclerView;
     85     private VerificationResultAdapter mResultAdapter;
     86     private boolean mListening = false;
     87 
     88     private final ServiceConnection mCarConnectionListener =
     89             new ServiceConnection() {
     90                 @Override
     91                 public void onServiceConnected(ComponentName name, IBinder iBinder) {
     92                     Log.d(TAG, "Connected to " + name.flattenToString());
     93                     try {
     94                         mCarDiagnosticManager =
     95                                 (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
     96                     } catch (CarNotConnectedException e) {
     97                         Log.e(TAG, "Failed to get a connection", e);
     98                     }
     99                 }
    100 
    101                 @Override
    102                 public void onServiceDisconnected(ComponentName name) {
    103                     Log.d(TAG, "Disconnected from " + name.flattenToString());
    104 
    105                     mCar = null;
    106                     mCarDiagnosticManager = null;
    107                 }
    108             };
    109 
    110     class DiagnosticListener implements CarDiagnosticManager.OnDiagnosticEventListener {
    111 
    112         @Override
    113         public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) {
    114             Log.v(TAG, "Received Car Diagnostic Event: " + carDiagnosticEvent.toString());
    115             mVerifier.receiveEvent(carDiagnosticEvent);
    116         }
    117     }
    118 
    119     class VerifierMsgReceiver extends BroadcastReceiver {
    120 
    121         @Override
    122         public void onReceive(Context context, Intent intent) {
    123             String action = intent.getAction();
    124             Log.d(TAG, "Received intent with action: " + action);
    125             if (ACTION_START_LISTEN.equals(action)) {
    126                 try {
    127                     startListen();
    128                 } catch (CarNotConnectedException e) {
    129                     Log.e(TAG, "Failed to listen for car diagnostic event", e);
    130                 }
    131             } else if (ACTION_STOP_LISTEN.equals(action)) {
    132                 stopListen();
    133                 verify();
    134             }
    135         }
    136     }
    137 
    138     @Override
    139     public void onCreate(Bundle savedInstanceState) {
    140         super.onCreate(savedInstanceState);
    141         setContentView(R.layout.verifier_activity);
    142 
    143         mStatusBar = (TextView) findViewById(R.id.status_bar);
    144 
    145         //Setting up RecyclerView to show verification result messages
    146         mRecyclerView = (RecyclerView) findViewById(R.id.verification_results);
    147         LinearLayoutManager layoutManager =
    148                 new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
    149         mRecyclerView.setLayoutManager(layoutManager);
    150         mResultAdapter = new VerificationResultAdapter();
    151         mRecyclerView.setAdapter(mResultAdapter);
    152 
    153         //Connect to car service
    154         mCar = Car.createCar(this, mCarConnectionListener);
    155         mCar.connect();
    156 
    157         //Initialize broadcast intent receiver
    158         mBroadcastReceiver = new VerifierMsgReceiver();
    159         IntentFilter filter = new IntentFilter(ACTION_START_LISTEN);
    160         filter.addAction(ACTION_STOP_LISTEN);
    161         this.registerReceiver(mBroadcastReceiver, filter);
    162 
    163         //Read golden diagnostics JSON file
    164         String jsonPath = this.getIntent().getStringExtra(JSON_PATH_KEY);
    165         if (jsonPath == null || jsonPath.isEmpty()) {
    166             jsonPath = DEFAULT_JSON_PATH;
    167         }
    168         List<CarDiagnosticEvent> events;
    169         try {
    170             events = DiagnosticJsonConverter.readFromJson(new FileInputStream(jsonPath));
    171         } catch (IOException e) {
    172             throw new RuntimeException("Failed to read diagnostic JSON file", e);
    173         }
    174         Log.d(TAG, String.format("Read %d events from JSON file %s.", events.size(), jsonPath));
    175 
    176         mVerifier = new DiagnosticVerifier(events);
    177     }
    178 
    179     @Override
    180     protected void onDestroy() {
    181         if (mCar != null) {
    182             mCar.disconnect();
    183         }
    184         mVerifier = null;
    185         this.unregisterReceiver(mBroadcastReceiver);
    186     }
    187 
    188     private void startListen() throws CarNotConnectedException {
    189         if (mListening) {
    190             return;
    191         }
    192         if (mDiagnosticListener == null) {
    193             mDiagnosticListener = new DiagnosticListener();
    194         }
    195         Log.i(TAG, "Start listening for car diagnostics events");
    196         mCarDiagnosticManager.registerListener(
    197                 mDiagnosticListener,
    198                 CarDiagnosticManager.FRAME_TYPE_LIVE,
    199                 CarSensorManager.SENSOR_RATE_NORMAL);
    200         mCarDiagnosticManager.registerListener(
    201                 mDiagnosticListener,
    202                 CarDiagnosticManager.FRAME_TYPE_FREEZE,
    203                 CarSensorManager.SENSOR_RATE_NORMAL);
    204 
    205         mListening = true;
    206         mStatusBar.setText(R.string.status_receiving);
    207     }
    208 
    209     private void stopListen() {
    210         Log.i(TAG, "Stop listening for car diagnostics events");
    211         mCarDiagnosticManager.unregisterListener(mDiagnosticListener);
    212         mListening = false;
    213     }
    214 
    215     private boolean isExternalStorageWritable() {
    216         String state = Environment.getExternalStorageState();
    217         return Environment.MEDIA_MOUNTED.equals(state);
    218     }
    219 
    220     private File getResultJsonFile() throws IOException {
    221         if (!isExternalStorageWritable()) {
    222             throw new IOException("External storage is not writable. Cannot save content");
    223         }
    224 
    225         File resultJson = new File(Environment.getExternalStoragePublicDirectory(
    226                 Environment.DIRECTORY_DOCUMENTS), JSON_RESULT);
    227         if (!resultJson.getParentFile().mkdirs()) {
    228             Log.w(TAG, "Parent directory may already exist");
    229         }
    230         return resultJson;
    231     }
    232 
    233     private void verify() {
    234         Log.d(TAG, "Start verifying car diagnostics events");
    235         mStatusBar.setText(R.string.status_verifying);
    236         List<VerificationResult> results = mVerifier.verify();
    237         mStatusBar.setText(R.string.status_done);
    238 
    239         if (results.isEmpty()) {
    240             Log.d(TAG, "Verification result is empty.");
    241             return;
    242         }
    243 
    244         List<String> resultMessages = new ArrayList<>();
    245         try {
    246             File resultJson = getResultJsonFile();
    247             JsonWriter writer = new JsonWriter(
    248                 new OutputStreamWriter(new FileOutputStream(resultJson)));
    249 
    250             writer.beginArray();
    251             for (VerificationResult result : results) {
    252                 resultMessages.add("Test case: " + result.testCase);
    253                 resultMessages.add("Result: " + result.success);
    254                 resultMessages.add(result.errorMessage);
    255                 result.writeToJson(writer);
    256             }
    257             writer.endArray();
    258             writer.flush();
    259             writer.close();
    260             Log.i(TAG, "Verification result: " + resultJson.getAbsolutePath());
    261         } catch (IOException e) {
    262             Log.e(TAG, "Failed to save verification result.", e);
    263         }
    264         mResultAdapter.setResultMessages(resultMessages);
    265     }
    266 }
    267 
    268