1 /* 2 * Copyright 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.googlecode.android_scripting.facade.bluetooth; 18 19 import android.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothServerSocket; 21 import android.bluetooth.BluetoothSocket; 22 import android.os.ParcelFileDescriptor; 23 24 import com.googlecode.android_scripting.Log; 25 26 import java.io.BufferedReader; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.InputStreamReader; 30 import java.io.OutputStream; 31 import java.lang.reflect.Field; 32 33 /** 34 * Class with Bluetooth Connection helper functions. 35 * 36 */ 37 38 class BluetoothConnection { 39 private BluetoothSocket mSocket; 40 private BluetoothDevice mDevice; 41 private OutputStream mOutputStream; 42 private InputStream mInputStream; 43 private BufferedReader mReader; 44 private BluetoothServerSocket mServerSocket; 45 private String mUuid; 46 BluetoothConnection(BluetoothSocket mSocket)47 BluetoothConnection(BluetoothSocket mSocket) throws IOException { 48 this(mSocket, null); 49 } 50 BluetoothConnection(BluetoothSocket mSocket, BluetoothServerSocket mServerSocket)51 BluetoothConnection(BluetoothSocket mSocket, BluetoothServerSocket mServerSocket) 52 throws IOException { 53 this.mSocket = mSocket; 54 mOutputStream = mSocket.getOutputStream(); 55 mInputStream = mSocket.getInputStream(); 56 mDevice = mSocket.getRemoteDevice(); 57 mReader = new BufferedReader(new InputStreamReader(mInputStream, "ASCII")); 58 this.mServerSocket = mServerSocket; 59 } 60 61 /** 62 * Set the UUID of this connection 63 * 64 * @param uuid the new UUID 65 */ setUUID(String uuid)66 public void setUUID(String uuid) { 67 this.mUuid = uuid; 68 } 69 70 /** 71 * Get this connection UUID 72 * 73 * @return String this connection UUID 74 */ getUUID()75 public String getUUID() { 76 return mUuid; 77 } 78 79 /** 80 * Get the MAC address of remote device 81 * 82 * @return String the remote device MAC address 83 */ getRemoteBluetoothAddress()84 public String getRemoteBluetoothAddress() { 85 return mDevice.getAddress(); 86 } 87 88 /** 89 * Get the connected state 90 * 91 * @return boolean TRUE if connected 92 */ isConnected()93 public boolean isConnected() { 94 if (mSocket == null) { 95 return false; 96 } 97 try { 98 mSocket.getRemoteDevice(); 99 mInputStream.available(); 100 mReader.ready(); 101 return true; 102 } catch (Exception e) { 103 return false; 104 } 105 } 106 107 /** 108 * Write an array of bytes to output stream 109 * 110 * @param out the array of bytes to write 111 */ write(byte[] out)112 public void write(byte[] out) throws IOException { 113 if (mOutputStream != null) { 114 mOutputStream.write(out); 115 } else { 116 throw new IOException("write: Bluetooth not ready."); 117 } 118 } 119 120 /** 121 * Write a string of bytes to output stream 122 * 123 * @param out the String of bytes to write 124 */ write(String out)125 public void write(String out) throws IOException { 126 this.write(out.getBytes()); 127 } 128 129 /** 130 * Write one byte to output stream 131 * 132 * @param out the byte to write 133 */ write(int out)134 public void write(int out) throws IOException { 135 if (mOutputStream != null) { 136 mOutputStream.write(out); 137 } else { 138 throw new IOException("write: Bluetooth not ready."); 139 } 140 } 141 142 /** 143 * To test if the next data is ready to be read 144 * 145 * @return Boolean TRUE if data is ready to be read 146 */ readReady()147 public Boolean readReady() throws IOException { 148 if (mReader != null) { 149 return mReader.ready(); 150 } 151 throw new IOException("Bluetooth not ready."); 152 } 153 154 /** 155 * Read an array of data 156 * 157 * @return byte[] the buffer read 158 */ readBinary()159 public byte[] readBinary() throws IOException { 160 return this.readBinary(4096); 161 } 162 163 /** 164 * Read an array of data of given size 165 * 166 * @param bufferSize the size to read 167 * 168 * @return byte[] the buffer containing read data 169 */ readBinary(int bufferSize)170 public byte[] readBinary(int bufferSize) throws IOException { 171 if (mReader != null) { 172 byte[] buffer = new byte[bufferSize]; 173 int bytesRead = mInputStream.read(buffer); 174 if (bytesRead == -1) { 175 Log.e("Read failed."); 176 throw new IOException("Read failed."); 177 } 178 byte[] truncatedBuffer = new byte[bytesRead]; 179 System.arraycopy(buffer, 0, truncatedBuffer, 0, bytesRead); 180 return truncatedBuffer; 181 } 182 183 throw new IOException("Bluetooth not ready."); 184 185 } 186 187 /** 188 * Read a string of data 189 * 190 * @return String the read string 191 */ read()192 public String read() throws IOException { 193 return this.read(4096); 194 } 195 196 /** 197 * Read a string of data of given size 198 * 199 * @param bufferSize number of bytes to read 200 * 201 * @return String the read string 202 */ read(int bufferSize)203 public String read(int bufferSize) throws IOException { 204 if (mReader != null) { 205 char[] buffer = new char[bufferSize]; 206 int bytesRead = mReader.read(buffer); 207 if (bytesRead == -1) { 208 Log.e("Read failed."); 209 throw new IOException("Read failed."); 210 } 211 return new String(buffer, 0, bytesRead); 212 } 213 throw new IOException("Bluetooth not ready."); 214 } 215 216 /** 217 * To read one byte 218 * 219 * @return int byte read 220 */ readbyte()221 public int readbyte() throws IOException { 222 if (mReader != null) { 223 int byteRead = mReader.read(); 224 if (byteRead == -1) { 225 Log.e("Read failed."); 226 throw new IOException("Read failed."); 227 } 228 return byteRead; 229 } 230 throw new IOException("readbyte: Bluetooth not ready."); 231 } 232 233 /** 234 * To read one line 235 * 236 * @return String line read 237 */ readLine()238 public String readLine() throws IOException { 239 if (mReader != null) { 240 return mReader.readLine(); 241 } 242 throw new IOException("Bluetooth not ready."); 243 } 244 245 /** 246 * Returns the name of the connected device 247 * 248 * @return String name of connected device 249 */ getConnectedDeviceName()250 public String getConnectedDeviceName() { 251 return mDevice.getName(); 252 } 253 254 clearFileDescriptor()255 private synchronized void clearFileDescriptor() { 256 try { 257 Field field = BluetoothSocket.class.getDeclaredField("mPfd"); 258 field.setAccessible(true); 259 ParcelFileDescriptor mPfd = (ParcelFileDescriptor) field.get(mSocket); 260 Log.d("Closing mPfd: " + mPfd); 261 if (mPfd == null) return; 262 mPfd.close(); 263 mPfd = null; 264 try { 265 field.set(mSocket, mPfd); 266 } catch (Exception e) { 267 Log.d("Exception setting mPfd = null in cleanCloseFix(): " + e.toString()); 268 } 269 } catch (Exception e) { 270 Log.w("ParcelFileDescriptor could not be cleanly closed.", e); 271 } 272 } 273 274 /** 275 * To stop this connection 276 */ stop()277 public void stop() { 278 if (mSocket != null) { 279 try { 280 mSocket.close(); 281 clearFileDescriptor(); 282 } catch (IOException e) { 283 Log.e(e); 284 } 285 } 286 mSocket = null; 287 if (mServerSocket != null) { 288 try { 289 mServerSocket.close(); 290 } catch (IOException e) { 291 Log.e(e); 292 } 293 } 294 mServerSocket = null; 295 296 if (mInputStream != null) { 297 try { 298 mInputStream.close(); 299 } catch (IOException e) { 300 Log.e(e); 301 } 302 } 303 mInputStream = null; 304 if (mOutputStream != null) { 305 try { 306 mOutputStream.close(); 307 } catch (IOException e) { 308 Log.e(e); 309 } 310 } 311 mOutputStream = null; 312 if (mReader != null) { 313 try { 314 mReader.close(); 315 } catch (IOException e) { 316 Log.e(e); 317 } 318 } 319 mReader = null; 320 } 321 } 322