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