/* * Copyright 2017 rootkiwi * * AN2Linux-client is licensed under GNU General Public License 3. * * See LICENSE for more details. */ package kiwi.root.an2linuxclient.network; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.graphics.Bitmap; import android.os.Build; import android.os.SystemClock; import android.util.Log; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.util.UUID; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import kiwi.root.an2linuxclient.crypto.TlsHelper; import kiwi.root.an2linuxclient.data.Notification; import kiwi.root.an2linuxclient.data.NotificationSettings; import kiwi.root.an2linuxclient.utils.ConnectionHelper; class BluetoothNotificationConnection extends NotificationConnection implements Runnable { private String serverMacAddress; private SSLEngine tlsEngine; private ByteBuffer appDataBuf; private ByteBuffer netDataBuf; BluetoothNotificationConnection(Context c, Notification n, Certificate serverCert, String serverMacAddress){ super(c, n, serverCert); this.serverMacAddress = serverMacAddress; } private void createTlsEngine(){ tlsEngine = TlsHelper.getNotificationTlsContext(c, serverCert).createSSLEngine(); tlsEngine.setUseClientMode(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH){ tlsEngine.setEnabledProtocols(TlsHelper.TLS_VERSIONS); tlsEngine.setEnabledCipherSuites(TlsHelper.TLS_CIPHERS); } else { tlsEngine.setEnabledProtocols(TlsHelper.TLS_VERSIONS_COMPAT_BT); tlsEngine.setEnabledCipherSuites(TlsHelper.TLS_CIPHERS_COMPAT_BT); } } private void createBuffers(){ appDataBuf = ByteBuffer.allocate(tlsEngine.getSession().getApplicationBufferSize()); netDataBuf = ByteBuffer.allocate(tlsEngine.getSession().getPacketBufferSize()); } @Override public void run(){ try { // hardcoded uuid generated from https://www.uuidgenerator.net/ BluetoothSocket bs = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(serverMacAddress) .createRfcommSocketToServiceRecord(UUID.fromString("a97fbf21-2ef3-4daf-adfb-2a53ffa87b8e")); try { bs.connect(); } catch (IOException connectException) { try { bs.close(); } catch (IOException closeException) {} /*I dont know why but I need sleep() so there are no more new connections than every about 0.5 sec, apparently with too many sockets in short time android throws some connection error when using bluetooth.*/ SystemClock.sleep(500); return; } OutputStream out = bs.getOutputStream(); InputStream in = bs.getInputStream(); out.write(NOTIF_CONN); createTlsEngine(); createBuffers(); if (TlsHelper.doHandshake(tlsEngine, netDataBuf, out, in) != SSLEngineResult.HandshakeStatus.FINISHED){ try { out.close(); in.close(); bs.close(); } catch (IOException e){} SystemClock.sleep(500); return; } NotificationSettings ns = n.getNotificationSettings(); byte[] encryptedNotificationFlags = TlsHelper.tlsEncrypt(tlsEngine, appDataBuf, netDataBuf, new byte[]{ns.getNotificationFlags()}); out.write(ConnectionHelper.intToByteArray(encryptedNotificationFlags.length)); out.write(encryptedNotificationFlags); if (ns.includeTitle() || ns.includeMessage()){ String title = ""; if (ns.includeTitle()){ title = n.getTitle(); } String message = ""; if (ns.includeMessage()){ message = "|||" + n.getMessage(); } byte[] titleAndOrMessage = (title + message).getBytes(); byte[] encryptedTitleAndOrMessage = TlsHelper.tlsEncrypt(tlsEngine, appDataBuf, netDataBuf, titleAndOrMessage); out.write(ConnectionHelper.intToByteArray(encryptedTitleAndOrMessage.length)); out.write(encryptedTitleAndOrMessage); } if (ns.includeIcon()){ ByteArrayOutputStream bos = new ByteArrayOutputStream(); n.getIcon().compress(Bitmap.CompressFormat.PNG, 100, bos); byte[] encryptedImage = TlsHelper.tlsEncrypt(tlsEngine, appDataBuf, netDataBuf, bos.toByteArray()); out.write(ConnectionHelper.intToByteArray(encryptedImage.length)); BufferedInputStream bis = new BufferedInputStream(new ByteArrayInputStream(encryptedImage), 8192); byte[] buffer = new byte[8192]; int len; while ((len = bis.read(buffer)) != -1) { out.write(buffer, 0, len); } bos.close(); bis.close(); } out.close(); in.close(); bs.close(); } catch (IOException e) { Log.e("BluetoothNotificatio...", "run"); Log.e("StackTrace", Log.getStackTraceString(e)); } } }