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.android.car.obd2.connections; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothSocket; 22 import android.util.Log; 23 import com.android.car.obd2.Obd2Connection; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.OutputStream; 27 import java.util.Objects; 28 import java.util.UUID; 29 30 public class BluetoothConnection implements Obd2Connection.UnderlyingTransport { 31 32 /** 33 * This is the well-known UUID for the Bluetooth SPP (Serial Port Profile) 34 */ 35 private static final UUID SERIAL_PORT_PROFILE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 36 37 private final BluetoothDevice mDevice; 38 private BluetoothSocket mSocket = null; 39 40 public static final String TAG = BluetoothConnection.class.getSimpleName(); 41 BluetoothConnection(String bluetoothAddress)42 public BluetoothConnection(String bluetoothAddress) { 43 this(BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bluetoothAddress)); 44 } 45 BluetoothConnection(BluetoothDevice device)46 public BluetoothConnection(BluetoothDevice device) { 47 mDevice = Objects.requireNonNull(device); 48 connect(); 49 } 50 51 @Override getAddress()52 public String getAddress() { 53 return mDevice.getAddress(); 54 } 55 56 /** 57 * Establish an RFCOMM connection to the remote device. 58 * 59 * Assumes there is no existing connection. 60 * 61 * This method may take time to return (or even not return in pathological cases). 62 * It is a good idea to wrap it in some kind of Promise-like object. 63 * 64 * @return true if it could connect, false otherwise 65 */ connect()66 private boolean connect() { 67 try { 68 mSocket = mDevice.createRfcommSocketToServiceRecord(SERIAL_PORT_PROFILE); 69 mSocket.connect(); 70 } catch (IOException e) { 71 Log.w(TAG, "BluetoothConnection couldn't be established due to an exception: " + e); 72 mSocket = null; 73 return false; 74 } 75 return mSocket.isConnected(); 76 } 77 78 @Override isConnected()79 public boolean isConnected() { 80 return mSocket != null && mSocket.isConnected(); 81 } 82 close()83 private void close() { 84 if (isConnected()) { 85 try { 86 mSocket.close(); 87 } catch (IOException e) { 88 // we are letting go of the connection anyway, so log and continue 89 Log.w(TAG, "IOException during BluetoothSocket close(): " + e); 90 } finally { 91 mSocket = null; 92 } 93 } 94 } 95 96 @Override reconnect()97 public boolean reconnect() { 98 close(); 99 return connect(); 100 } 101 102 @Override getInputStream()103 public InputStream getInputStream() { 104 if (isConnected()) { 105 try { 106 return mSocket.getInputStream(); 107 } catch (IOException e) { 108 Log.w(TAG, "failed to get Bluetooth input stream: " + e); 109 } 110 } 111 return null; 112 } 113 114 @Override getOutputStream()115 public OutputStream getOutputStream() { 116 if (isConnected()) { 117 try { 118 return mSocket.getOutputStream(); 119 } catch (IOException e) { 120 Log.w(TAG, "failed to get Bluetooth output stream: " + e); 121 } 122 } 123 return null; 124 } 125 } 126