diff options
author | Chao Yan <aceyansf@google.com> | 2017-07-13 15:09:16 -0700 |
---|---|---|
committer | Chao Yan <aceyansf@google.com> | 2017-07-14 15:18:34 -0700 |
commit | 7582f2e1cc5df01f51b97554a00d44f393c922b6 (patch) | |
tree | 62c4033b49e61e352f93b666656083fb4ab19cbf /tests/CarDiagnosticVerifier | |
parent | 9f6f2546729b204c69195e0908a8ea750e7ce9dd (diff) | |
download | Car-7582f2e1cc5df01f51b97554a00d44f393c922b6.tar.gz |
Added car diagnostic API test application
Added the test application which verify if received car diagnostic
events are correct by checking against the true (golden) event list.
Test: manual with preparing golden event list while injecting wrong ones
Change-Id: I7ea85f2558fd1cbc7e302eb424f40301402a58a7
Diffstat (limited to 'tests/CarDiagnosticVerifier')
7 files changed, 711 insertions, 0 deletions
diff --git a/tests/CarDiagnosticVerifier/Android.mk b/tests/CarDiagnosticVerifier/Android.mk new file mode 100644 index 0000000000..98758a5a24 --- /dev/null +++ b/tests/CarDiagnosticVerifier/Android.mk @@ -0,0 +1,44 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := CarDiagnosticVerifier + +LOCAL_JAVA_VERSION := 1.8 + +LOCAL_PROGUARD_ENABLED := disabled + +LOCAL_DEX_PREOPT := false + +LOCAL_PRIVILEGED_MODULE := true + +LOCAL_STATIC_JAVA_LIBRARIES += vehicle-hal-support-lib + +LOCAL_JAVA_LIBRARIES += android.car + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/CarDiagnosticVerifier/AndroidManifest.xml b/tests/CarDiagnosticVerifier/AndroidManifest.xml new file mode 100644 index 0000000000..443825a577 --- /dev/null +++ b/tests/CarDiagnosticVerifier/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.google.android.car.diagnosticverifier"> + + <uses-permission android:name="android.car.permission.DIAGNOSTIC_READ_ALL" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <application android:label="Car Diagnostic Verification"> + <activity android:name=".MainActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml new file mode 100644 index 0000000000..7edf5bce30 --- /dev/null +++ b/tests/CarDiagnosticVerifier/res/layout/verifier_activity.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="16dp" + android:paddingRight="16dp" > + + <TextView + android:id="@+id/status_bar" + android:layout_width="fill_parent" + android:layout_height="wrap_content" /> + + <TextView + android:id="@+id/results" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@id/status_bar" + android:layout_marginTop="16dp" /> + +</RelativeLayout> diff --git a/tests/CarDiagnosticVerifier/res/values/strings.xml b/tests/CarDiagnosticVerifier/res/values/strings.xml new file mode 100644 index 0000000000..ca6b33a1e8 --- /dev/null +++ b/tests/CarDiagnosticVerifier/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <string name="status_receiving">Receiving car diagnostic events...</string> + <string name="status_verifying">Verifying car diagnostic events...</string> + <string name="status_done">Done with verification</string> +</resources> diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java new file mode 100644 index 0000000000..fd78c7f24a --- /dev/null +++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticJsonConverter.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.car.diagnosticverifier; + +import android.car.hardware.CarDiagnosticEvent; +import android.util.JsonReader; + +import com.android.car.vehiclehal.DiagnosticJson; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides method to convert JSON into car diagnostic event object. + */ +public class DiagnosticJsonConverter { + + public static List<CarDiagnosticEvent> readFromJson(InputStream in) throws IOException { + JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); + + try { + return readEventsArray(reader); + } finally { + reader.close(); + } + } + + private static List<CarDiagnosticEvent> readEventsArray(JsonReader reader) throws IOException { + List<CarDiagnosticEvent> events = new ArrayList<>(); + + reader.beginArray(); + while (reader.hasNext()) { + events.add(readEventAndCanonicalize(reader)); + } + reader.endArray(); + return events; + } + + public static CarDiagnosticEvent readEventAndCanonicalize(InputStream in) throws IOException { + JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8")); + return readEventAndCanonicalize(reader); + } + + /** + * This method convert JSON to a car diagnostic event object. + * Note: it will always set timestamp to 0 and set dtc to null if it is empty string. + */ + private static CarDiagnosticEvent readEventAndCanonicalize(JsonReader reader) + throws IOException { + DiagnosticJson diagnosticJson = DiagnosticJson.build(reader); + //Build event + CarDiagnosticEvent.Builder builder = "freeze".equals(diagnosticJson.type) ? + CarDiagnosticEvent.Builder.newFreezeFrameBuilder() : + CarDiagnosticEvent.Builder.newLiveFrameBuilder(); + //Always skip timestamp because it is not useful for test + builder.atTimestamp(0); + for (int i = 0; i < diagnosticJson.intValues.size(); i++) { + builder.withIntValue(diagnosticJson.intValues.keyAt(i), + diagnosticJson.intValues.valueAt(i)); + } + for (int i = 0; i < diagnosticJson.floatValues.size(); i++) { + builder.withFloatValue(diagnosticJson.floatValues.keyAt(i), + diagnosticJson.floatValues.valueAt(i)); + } + //Always set dtc to null if it is empty string + builder.withDTC("".equals(diagnosticJson.dtc) ? null : diagnosticJson.dtc); + + return builder.build(); + } +} diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java new file mode 100644 index 0000000000..8f547b1877 --- /dev/null +++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/DiagnosticVerifier.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.car.diagnosticverifier; + +import android.car.hardware.CarDiagnosticEvent; +import android.util.JsonWriter; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * DiagVerifier implements verification logic for car diagnostic events. + * + * The main idea for the verification is similar to a "diff" command on two files, whereas here + * is a diff on two event lists. The available diff operations are: "add", "delete", "modify" + * + * For example, think about doing a diff on two sequences: + * + * Truth: A B C D E + * Received: A C D E F + * + * The goal is to find the minimal number of diff operations applied on Truth list in order to + * become Received list. It is the same problem to find edit distance between two sequences and keep + * track of the corresponding edit operations. This verifier applies dynamic programming algorithm + * to find the minimal set of diff operations. And the result would be: + * + * Truth: A - C D E + + * Received: A C D E F + * + * It means in order to become Received list, "B" will be missing and an extra "F" will be added + * at the end of list. + */ +public class DiagnosticVerifier { + + private static final String TAG = "DiagnosticVerifier"; + /** + * Below are 4 diff operations when comparing two event lists + */ + private static final int DELETE = 0; + private static final int ADD = 1; + private static final int MODIFY = 2; + private static final int KEEP = 3; + + /** + * A list of truth diagnostic events for comparison. + */ + private final List<CarDiagnosticEvent> mTruthEventList = new ArrayList<>(); + /** + * A list of received diagnostic events from car service. + */ + private final List<CarDiagnosticEvent> mReceivedEventList = new ArrayList<>(); + + /** + * Definition of the verification result + */ + static class VerificationResult { + public final String testCase; + public final boolean success; + public final String errorMessage; + + private VerificationResult(String testCase, boolean success, String errorMessage) { + this.testCase = testCase; + this.success = success; + this.errorMessage = errorMessage; + } + + public static VerificationResult fromMessage(String testCase, String message) { + return new VerificationResult(testCase, message.length() == 0, message); + } + + public void writeToJson(JsonWriter jsonWriter) throws IOException { + jsonWriter.beginObject(); + + jsonWriter.name("testCase"); + jsonWriter.value(this.testCase); + + jsonWriter.name("success"); + jsonWriter.value(this.success); + + jsonWriter.name("errorMessage"); + jsonWriter.value(this.errorMessage); + + jsonWriter.endObject(); + } + } + + public DiagnosticVerifier(List<CarDiagnosticEvent> truthEvents) { + if (truthEvents != null) { + for (CarDiagnosticEvent event : truthEvents) { + CarDiagnosticEvent canonicalEvent = canonicalize(event); + mTruthEventList.add(canonicalEvent); + } + } + } + + public void receiveEvent(CarDiagnosticEvent event) { + CarDiagnosticEvent newEvent = canonicalize(event); + mReceivedEventList.add(newEvent); + } + + public List<VerificationResult> verify() { + List<Integer> diff = calculateDiffOperations(); + StringBuilder missingEventMsgBuilder = new StringBuilder(); + StringBuilder extraEventMsgBuilder = new StringBuilder(); + StringBuilder mismatchEventMsgBuilder = new StringBuilder(); + for (int i = 0, j = 0, k = diff.size() - 1; k >= 0; k--) { + if (diff.get(k) == DELETE) { + missingEventMsgBuilder.append(String.format( + "Missing event at position %d: %s\n", i, mTruthEventList.get(i))); + i++; + } else if (diff.get(k) == ADD) { + extraEventMsgBuilder.append(String.format( + "Extra event at position %d: %s\n", i, mReceivedEventList.get(j))); + j++; + } else if (diff.get(k) == MODIFY) { + mismatchEventMsgBuilder.append(String.format( + "Mismatched event pair at position %d:\n" + + "True event -- %s\nWrong event -- %s\n", + i, mTruthEventList.get(i), mReceivedEventList.get(j))); + i++; + j++; + } else { + i++; + j++; + } + } + List<VerificationResult> results = new ArrayList<>(); + results.add(VerificationResult.fromMessage( + "test_mismatched_event", mismatchEventMsgBuilder.toString())); + results.add(VerificationResult.fromMessage( + "test_missing_event", missingEventMsgBuilder.toString())); + results.add(VerificationResult.fromMessage( + "test_extra_event", extraEventMsgBuilder.toString())); + return results; + } + + /** + * The function applies a dynamic programming algorithm to find the minimal set of diff + * operations that applied on truth event list in order to become received event list + */ + private List<Integer> calculateDiffOperations() { + final int n = mTruthEventList.size(); + final int m = mReceivedEventList.size(); + + int[][] diffTable = new int[n + 1][m + 1]; + int[][] costTable = new int[n + 1][m + 1]; + + for (int i = 1; i <= n; i++) { + costTable[i][0] = i; + diffTable[i][0] = DELETE; + } + + for (int i = 1; i <= m; i++) { + costTable[0][i] = i; + diffTable[0][i] = ADD; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + int deleteCost = costTable[i - 1][j] + 1; + int addCost = costTable[i][j - 1] + 1; + int modifyCost = costTable[i - 1][j - 1]; + + CarDiagnosticEvent trueEvent = mTruthEventList.get(i - 1); + CarDiagnosticEvent receivedEvent = mReceivedEventList.get(j - 1); + + //TODO: Use a more meaningful comparison. Instead of strict object level equality, + //can check logical equality and allow an acceptable difference. + boolean isEqual = trueEvent.equals(receivedEvent); + modifyCost += isEqual ? 0 : 1; + + int minCost = modifyCost; + int move = isEqual ? KEEP : MODIFY; + if (minCost > addCost) { + minCost = addCost; + move = ADD; + } + if (minCost > deleteCost) { + minCost = deleteCost; + move = DELETE; + } + + costTable[i][j] = minCost; + diffTable[i][j] = move; + } + } + List<Integer> diff = new ArrayList<>(); + + for (int i = n, j = m; i > 0 || j > 0; ) { + diff.add(diffTable[i][j]); + if (diffTable[i][j] == DELETE) { + i--; + } else if (diffTable[i][j] == ADD) { + j--; + } else { + i--; + j--; + } + } + return diff; + } + + /** + * The function will canonicalize a given event by using JSON converter which will reset event + * timestamp to 0 and set DTC field with empty string to null. Doing JSON conversion is because + * CarDiagnosticEvent does not provide direct accessor for intValues and floatValues. + */ + private CarDiagnosticEvent canonicalize(CarDiagnosticEvent event) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + JsonWriter writer = new JsonWriter(new OutputStreamWriter(out)); + CarDiagnosticEvent newEvent = event; + try { + event.writeToJson(writer); + writer.flush(); + writer.close(); + byte[] rawJson = out.toByteArray(); + ByteArrayInputStream in = new ByteArrayInputStream(rawJson); + newEvent = DiagnosticJsonConverter.readEventAndCanonicalize(in); + in.close(); + out.close(); + } catch (IOException e) { + Log.w(TAG, "Failed to clear timestamp "); + } + return newEvent; + } +} diff --git a/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java new file mode 100644 index 0000000000..14c5e076f3 --- /dev/null +++ b/tests/CarDiagnosticVerifier/src/com/google/android/car/diagnosticverifier/MainActivity.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.car.diagnosticverifier; + +import android.app.Activity; +import android.car.Car; +import android.car.CarNotConnectedException; +import android.car.hardware.CarDiagnosticEvent; +import android.car.hardware.CarDiagnosticManager; +import android.car.hardware.CarSensorManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Environment; +import android.os.IBinder; +import android.util.JsonWriter; +import android.util.Log; +import android.widget.TextView; + +import com.google.android.car.diagnosticverifier.DiagnosticVerifier.VerificationResult; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; + +/** + * The test app that does the verification of car diagnostic event data. It first reads the + * truth (golden) event data from a JSON file upon starting. Then a broadcast intent such as: + * + * am broadcast -a com.google.android.car.diagnosticverifier.action.START_LISTEN + * + * will activate the car diagnostics listener. The test app will receive events from diagnostic API. + * Once it receives all the events, a broadcast intent with "stop" action such as: + * + * am broadcast -a com.google.android.car.diagnosticverifier.action.STOP_LISTEN + * + * will deactivate the listener and start the verification process (see {@link DiagnosticVerifier}). + * + * Verification result will be output to a JSON file on device. + */ +public class MainActivity extends Activity { + public static final String TAG = "DiagnosticVerifier"; + + public static final String ACTION_START_LISTEN = + "com.google.android.car.diagnosticverifier.action.START_LISTEN"; + public static final String ACTION_STOP_LISTEN = + "com.google.android.car.diagnosticverifier.action.STOP_LISTEN"; + + private static final String DEFAULT_JSON_PATH = "/data/local/tmp/diag.json"; + + private static final String JSON_PATH_KEY = "jsonPath"; + private static final String JSON_RESULT = "verification_result.json"; + + private Car mCar; + private CarDiagnosticManager mCarDiagnosticManager; + private DiagnosticListener mDiagnosticListener; + private BroadcastReceiver mBroadcastReceiver; + private DiagnosticVerifier mVerifier; + private TextView mStatusBar; + private boolean mListening = false; + + private final ServiceConnection mCarConnectionListener = + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder iBinder) { + Log.d(TAG, "Connected to " + name.flattenToString()); + try { + mCarDiagnosticManager = + (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Failed to get a connection", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + Log.d(TAG, "Disconnected from " + name.flattenToString()); + + mCar = null; + mCarDiagnosticManager = null; + } + }; + + class DiagnosticListener implements CarDiagnosticManager.OnDiagnosticEventListener { + + @Override + public void onDiagnosticEvent(CarDiagnosticEvent carDiagnosticEvent) { + Log.v(TAG, "Received Car Diagnostic Event: " + carDiagnosticEvent.toString()); + mVerifier.receiveEvent(carDiagnosticEvent); + } + } + + class VerifierMsgReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + Log.d(TAG, "Received intent with action: " + action); + if (ACTION_START_LISTEN.equals(action)) { + try { + startListen(); + } catch (CarNotConnectedException e) { + Log.e(TAG, "Failed to listen for car diagnostic event", e); + } + } else if (ACTION_STOP_LISTEN.equals(action)) { + stopListen(); + verify(); + } + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.verifier_activity); + + mStatusBar = (TextView) findViewById(R.id.status_bar); + + mCar = Car.createCar(this, mCarConnectionListener); + mCar.connect(); + + mBroadcastReceiver = new VerifierMsgReceiver(); + IntentFilter filter = new IntentFilter(ACTION_START_LISTEN); + filter.addAction(ACTION_STOP_LISTEN); + this.registerReceiver(mBroadcastReceiver, filter); + + String jsonPath = this.getIntent().getStringExtra(JSON_PATH_KEY); + if (jsonPath == null || jsonPath.isEmpty()) { + jsonPath = DEFAULT_JSON_PATH; + } + + List<CarDiagnosticEvent> events; + try { + events = DiagnosticJsonConverter.readFromJson(new FileInputStream(jsonPath)); + } catch (IOException e) { + throw new RuntimeException("Failed to read diagnostic JSON file", e); + } + Log.d(TAG, String.format("Read %d events from JSON file %s.", events.size(), jsonPath)); + + mVerifier = new DiagnosticVerifier(events); + } + + @Override + protected void onDestroy() { + if (mCar != null) { + mCar.disconnect(); + } + mVerifier = null; + this.unregisterReceiver(mBroadcastReceiver); + } + + private void startListen() throws CarNotConnectedException { + if (mListening) { + return; + } + if (mDiagnosticListener == null) { + mDiagnosticListener = new DiagnosticListener(); + } + Log.i(TAG, "Start listening for car diagnostics events"); + mCarDiagnosticManager.registerListener( + mDiagnosticListener, + CarDiagnosticManager.FRAME_TYPE_LIVE, + CarSensorManager.SENSOR_RATE_NORMAL); + mCarDiagnosticManager.registerListener( + mDiagnosticListener, + CarDiagnosticManager.FRAME_TYPE_FREEZE, + CarSensorManager.SENSOR_RATE_NORMAL); + + mListening = true; + mStatusBar.setText(R.string.status_receiving); + } + + private void stopListen() { + Log.i(TAG, "Stop listening for car diagnostics events"); + mCarDiagnosticManager.unregisterListener(mDiagnosticListener); + mListening = false; + } + + private boolean isExternalStorageWritable() { + String state = Environment.getExternalStorageState(); + return Environment.MEDIA_MOUNTED.equals(state); + } + + private File getResultJsonFile() throws IOException { + if (!isExternalStorageWritable()) { + throw new IOException("External storage is not writable. Cannot save content"); + } + + File resultJson = new File(Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOCUMENTS), JSON_RESULT); + if (!resultJson.getParentFile().mkdirs()) { + Log.w(TAG, "Parent directory may already exist"); + } + return resultJson; + } + + private void verify() { + Log.d(TAG, "Start verifying car diagnostics events"); + mStatusBar.setText(R.string.status_verifying); + List<VerificationResult> results = mVerifier.verify(); + mStatusBar.setText(R.string.status_done); + + if (results.isEmpty()) { + Log.d(TAG, "Verification result is empty."); + return; + } + + //TODO: Use a scrollable view + TextView resultView = (TextView) findViewById(R.id.results); + resultView.setText(""); + + try { + File resultJson = getResultJsonFile(); + JsonWriter writer = new JsonWriter( + new OutputStreamWriter(new FileOutputStream(resultJson))); + + writer.beginArray(); + for (VerificationResult result : results) { + resultView.append("Test case: " + result.testCase + "\n"); + resultView.append("Result: " + result.success + "\n"); + resultView.append(result.errorMessage); + resultView.append("\n"); + result.writeToJson(writer); + } + writer.endArray(); + writer.flush(); + writer.close(); + Log.i(TAG, "Verification result: " + resultJson.getAbsolutePath()); + } catch (IOException e) { + Log.e(TAG, "Failed to save verification result.", e); + } + } +} + |