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 17 package com.google.android.car.obd2app; 18 19 import android.annotation.Nullable; 20 import android.content.Context; 21 import android.os.Environment; 22 import android.os.SystemClock; 23 import android.util.JsonWriter; 24 import android.util.Log; 25 import com.android.car.obd2.Obd2Connection; 26 import com.android.car.obd2.Obd2FreezeFrameGenerator; 27 import com.android.car.obd2.Obd2LiveFrameGenerator; 28 import com.android.car.obd2.connections.BluetoothConnection; 29 import java.io.File; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.OutputStreamWriter; 33 import java.util.Objects; 34 import java.util.TimerTask; 35 36 public class Obd2CollectionTask extends TimerTask { 37 private final Obd2Connection mConnection; 38 private final Obd2LiveFrameGenerator mLiveFrameGenerator; 39 private final Obd2FreezeFrameGenerator mFreezeFrameGenerator; 40 private final StatusNotification mStatusNotification; 41 private final JsonWriter mJsonWriter; 42 create( Context context, StatusNotification statusNotification, String deviceAddress)43 public static @Nullable Obd2CollectionTask create( 44 Context context, StatusNotification statusNotification, String deviceAddress) { 45 try { 46 return new Obd2CollectionTask( 47 Objects.requireNonNull(context), 48 Objects.requireNonNull(statusNotification), 49 Objects.requireNonNull(deviceAddress)); 50 } catch (IOException | InterruptedException | IllegalStateException e) { 51 Log.i(MainActivity.TAG, "Connection failed due to exception", e); 52 return null; 53 } 54 } 55 56 @Override cancel()57 public boolean cancel() { 58 synchronized (mJsonWriter) { 59 try { 60 mJsonWriter.endArray(); 61 mJsonWriter.flush(); 62 mJsonWriter.close(); 63 } catch (IOException e) { 64 Log.w(MainActivity.TAG, "IOException during close", e); 65 } 66 return super.cancel(); 67 } 68 } 69 70 @Override run()71 public void run() { 72 if (!mConnection.isConnected()) { 73 if (!mConnection.reconnect()) { 74 mStatusNotification.notifyDisconnected(); 75 return; 76 } 77 } 78 79 try { 80 synchronized (mJsonWriter) { 81 mLiveFrameGenerator.generate(mJsonWriter); 82 mFreezeFrameGenerator.generate(mJsonWriter); 83 mJsonWriter.flush(); 84 } 85 mStatusNotification.notifyDataCapture(); 86 } catch (Exception e) { 87 mStatusNotification.notifyException(e); 88 } 89 } 90 Obd2CollectionTask(Context context, StatusNotification statusNotification, String deviceAddress)91 Obd2CollectionTask(Context context, StatusNotification statusNotification, String deviceAddress) 92 throws IOException, InterruptedException { 93 if (!isExternalStorageWriteable()) 94 throw new IOException("Cannot write data to external storage"); 95 mStatusNotification = statusNotification; 96 BluetoothConnection bluetoothConnection = new BluetoothConnection(deviceAddress); 97 if (!bluetoothConnection.isConnected()) { 98 statusNotification.notifyConnectionFailed(); 99 throw new IllegalStateException("Unable to connect to remote end."); 100 } 101 mConnection = new Obd2Connection(bluetoothConnection); 102 mLiveFrameGenerator = new Obd2LiveFrameGenerator(mConnection); 103 mFreezeFrameGenerator = new Obd2FreezeFrameGenerator(mConnection); 104 mJsonWriter = 105 new JsonWriter( 106 new OutputStreamWriter( 107 new FileOutputStream(getFilenameForStorage(context)))); 108 mJsonWriter.beginArray(); 109 } 110 isExternalStorageWriteable()111 private static boolean isExternalStorageWriteable() { 112 String state = Environment.getExternalStorageState(); 113 return (Environment.MEDIA_MOUNTED.equals(state)); 114 } 115 getFilenameForStorage(Context context)116 private static File getFilenameForStorage(Context context) { 117 String basename = String.format("obd2app.capture.%d", SystemClock.elapsedRealtimeNanos()); 118 return new File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), basename); 119 } 120 } 121