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