// -*- mode: java; c-basic-offset: 2; -*- // Copyright 2009-2011 Google, All Rights reserved // Copyright 2011-2012 MIT, All rights reserved // Released under the Apache License, Version 2.0 // http://www.apache.org/licenses/LICENSE-2.0 package com.google.appinventor.components.runtime.util; // Even though the code doesn't need these imports, javadoc does. import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Set; import java.util.UUID; /** * Provides access to Android Bluetooth classes via Java reflection. * * @author lizlooney@google.com (Liz Looney) */ public class BluetoothReflection { private static final int BOND_BONDED = 0xC; // from android.bluetooth.BluetoothDevice private BluetoothReflection() { } // BluetoothAdapter methods /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter()}. * * @return a {@link android.bluetooth.BluetoothAdapter} object, or null if * Bluetooth is not available */ public static Object getBluetoothAdapter() { // return BluetoothAdapter.getDefaultAdapter(); Class bluetoothAdapterClass; try { bluetoothAdapterClass = Class.forName("android.bluetooth.BluetoothAdapter"); } catch (ClassNotFoundException e) { // Bluetooth is not available on this Android device. return null; } return invokeStaticMethod(getMethod(bluetoothAdapterClass, "getDefaultAdapter")); } /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#isEnabled()}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @return true if Bluetooth is enabled, false otherwise */ public static boolean isBluetoothEnabled(Object bluetoothAdapter) { // boolean enabled = bluetoothAdapter.isEnabled(); return (Boolean) invokeMethod(getMethod(bluetoothAdapter.getClass(), "isEnabled"), bluetoothAdapter); } /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#getBondedDevices()}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @return a Set of {@link android.bluetooth.BluetoothDevice} objects */ public static Set getBondedDevices(Object bluetoothAdapter) { // return bluetoothAdapter.getBondedDevices(); return (Set) invokeMethod(getMethod(bluetoothAdapter.getClass(), "getBondedDevices"), bluetoothAdapter); } /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#checkBluetoothAddress(String)}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @param address a string that might be a bluetooth MAC address * @return true if the address is valid, false otherwise */ public static boolean checkBluetoothAddress(Object bluetoothAdapter, String address) { // return bluetoothAdapter.checkBluetoothAddress(address); return (Boolean) invokeMethod( getMethod(bluetoothAdapter.getClass(), "checkBluetoothAddress", String.class), bluetoothAdapter, address); } /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#getRemoteDevice(String)}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @param address the bluetooth MAC address of the device * @return a {@link android.bluetooth.BluetoothDevice} object */ public static Object getRemoteDevice(Object bluetoothAdapter, String address) throws IllegalArgumentException { // return bluetoothAdapter.getRemoteDevice(address); return invokeMethodThrowsIllegalArgumentException( getMethod(bluetoothAdapter.getClass(), "getRemoteDevice", String.class), bluetoothAdapter, address); } /** * Invokes the method * {@link android.bluetooth.BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM {@link android.bluetooth.BluetoothServerSocket} */ public static Object listenUsingRfcommWithServiceRecord(Object bluetoothAdapter, String name, UUID uuid) throws IOException { // return bluetoothAdapter.listenUsingRfcommWithServiceRecord(name, uuid); return invokeMethodThrowsIOException(getMethod(bluetoothAdapter.getClass(), "listenUsingRfcommWithServiceRecord", String.class, UUID.class), bluetoothAdapter, name, uuid); } // Note: The following @link generates a Javadoc warning because we build against a // version of the jar that doesn't yet have the method, which was added in API level 10. /** * Invokes the method {@link * android.bluetooth.BluetoothAdapter#listenUsingInsecureRfcommWithServiceRecord(String, UUID)}. * * @param bluetoothAdapter a {@link android.bluetooth.BluetoothAdapter} object * @param name service name for SDP record * @param uuid uuid for SDP record * @return a listening RFCOMM {@link android.bluetooth.BluetoothServerSocket} */ public static Object listenUsingInsecureRfcommWithServiceRecord( Object bluetoothAdapter, String name, UUID uuid) throws IOException { // return bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(name, uuid); return invokeMethodThrowsIOException(getMethod(bluetoothAdapter.getClass(), "listenUsingInsecureRfcommWithServiceRecord", String.class, UUID.class), bluetoothAdapter, name, uuid); } // BluetoothDevice methods /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#getName()}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @return the name of the given {@link android.bluetooth.BluetoothDevice} */ public static String getBluetoothDeviceName(Object bluetoothDevice) { // return bluetoothDevice.getName(); return (String) invokeMethod(getMethod(bluetoothDevice.getClass(), "getName"), bluetoothDevice); } /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#getAddress()}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @return the address of the given {@link android.bluetooth.BluetoothDevice} */ public static String getBluetoothDeviceAddress(Object bluetoothDevice) { // return bluetoothDevice.getAddress(); return (String) invokeMethod(getMethod(bluetoothDevice.getClass(), "getAddress"), bluetoothDevice); } /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#getBondState()}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @return the bond state of the given {@link android.bluetooth.BluetoothDevice} */ public static boolean isBonded(Object bluetoothDevice) { // return bluetoothDevice.getBondState() == BOND_BONDED; int bondState = (Integer) invokeMethod(getMethod(bluetoothDevice.getClass(), "getBondState"), bluetoothDevice); return bondState == BOND_BONDED; } /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#getBluetoothClass()}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @return the BluetoothClass of the given {@link android.bluetooth.BluetoothDevice} */ public static Object getBluetoothClass(Object bluetoothDevice) { // return bluetoothDevice.getBluetoothClass(); return invokeMethod(getMethod(bluetoothDevice.getClass(), "getBluetoothClass"), bluetoothDevice); } /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(UUID)}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @param uuid the service record uuid * @return a {@link android.bluetooth.BluetoothSocket} object */ public static Object createRfcommSocketToServiceRecord(Object bluetoothDevice, UUID uuid) throws IOException { // return bluetoothDevice.createRfcommSocketToServiceRecord(uuid); return invokeMethodThrowsIOException( getMethod(bluetoothDevice.getClass(), "createRfcommSocketToServiceRecord", UUID.class), bluetoothDevice, uuid); } // Note: The following @link generates a Javadoc warning because we build against a // version of the jar that doesn't yet have the method, which was added in API level 10. /** * Invokes the method * {@link android.bluetooth.BluetoothDevice#createInsecureRfcommSocketToServiceRecord(Object, UUID)}. * * @param bluetoothDevice a {@link android.bluetooth.BluetoothDevice} object * @param uuid the service record uuid * @return a {@link android.bluetooth.BluetoothSocket} object */ public static Object createInsecureRfcommSocketToServiceRecord(Object bluetoothDevice, UUID uuid) throws IOException { // return bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid); return invokeMethodThrowsIOException( getMethod(bluetoothDevice.getClass(), "createInsecureRfcommSocketToServiceRecord", UUID.class), bluetoothDevice, uuid); } // BluetoothClass methods /** * Invokes the method * {@link android.bluetooth.BluetoothClass#getDeviceClass()}. * * @param bluetoothClass a {@link android.bluetooth.BluetoothClass} object * @return the (major and minor) device class component of the given * {@link android.bluetooth.BluetoothClass} */ public static int getDeviceClass(Object bluetoothClass) { // return bluetoothClass.getBluetoothClass(); return (Integer) invokeMethod(getMethod(bluetoothClass.getClass(), "getDeviceClass"), bluetoothClass); } // BluetoothSocket methods /** * Invokes the method * {@link android.bluetooth.BluetoothSocket#connect()}. * * @param bluetoothSocket a {@link android.bluetooth.BluetoothSocket} object */ public static void connectToBluetoothSocket(Object bluetoothSocket) throws IOException { // bluetoothSocket.connect(); invokeMethodThrowsIOException(getMethod(bluetoothSocket.getClass(), "connect"), bluetoothSocket); } /** * Invokes the method * {@link android.bluetooth.BluetoothSocket#getInputStream()}. * * @param bluetoothSocket a {@link android.bluetooth.BluetoothSocket} object * @return the {@link InputStream} */ public static InputStream getInputStream(Object bluetoothSocket) throws IOException { // return bluetoothSocket.getInputStream(); return (InputStream) invokeMethodThrowsIOException( getMethod(bluetoothSocket.getClass(), "getInputStream"), bluetoothSocket); } /** * Invokes the method * {@link android.bluetooth.BluetoothSocket#getOutputStream()}. * * @param bluetoothSocket a {@link android.bluetooth.BluetoothSocket} object * @return the {@link OutputStream} */ public static OutputStream getOutputStream(Object bluetoothSocket) throws IOException { // return bluetoothSocket.getOutputStream(); return (OutputStream) invokeMethodThrowsIOException( getMethod(bluetoothSocket.getClass(), "getOutputStream"), bluetoothSocket); } /** * Invokes the method * {@link android.bluetooth.BluetoothSocket#close()}. * * @param bluetoothSocket a {@link android.bluetooth.BluetoothSocket} object */ public static void closeBluetoothSocket(Object bluetoothSocket) throws IOException { // bluetoothSocket.close(); invokeMethodThrowsIOException(getMethod(bluetoothSocket.getClass(), "close"), bluetoothSocket); } // BluetoothServerSocket methods /** * Invokes the method {@link android.bluetooth.BluetoothServerSocket#accept()}. * * @param bluetoothServerSocket a {@link android.bluetooth.BluetoothServerSocket} object * @return a {@link android.bluetooth.BluetoothSocket} object */ public static Object accept(Object bluetoothServerSocket) throws IOException { // return bluetoothServerSocket.accept(); return invokeMethodThrowsIOException(getMethod(bluetoothServerSocket.getClass(), "accept"), bluetoothServerSocket); } /** * Invokes the method {@link android.bluetooth.BluetoothServerSocket#close()}. * * @param bluetoothServerSocket a {@link android.bluetooth.BluetoothServerSocket} object */ public static void closeBluetoothServerSocket(Object bluetoothServerSocket) throws IOException { // bluetoothServerSocket.close(); invokeMethodThrowsIOException(getMethod(bluetoothServerSocket.getClass(), "close"), bluetoothServerSocket); } // Reflection helper methods private static Method getMethod(Class clazz, String name) { try { return clazz.getMethod(name, new Class[0]); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } private static Method getMethod(Class clazz, String name, Class<?>... parameterTypes) { try { return clazz.getMethod(name, parameterTypes); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } private static Object invokeStaticMethod(Method method) { try { return method.invoke(null); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); cause.printStackTrace(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new RuntimeException(cause); } } } private static Object invokeMethod(Method method, Object thisObject, Object... args) { try { return method.invoke(thisObject, args); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); cause.printStackTrace(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new RuntimeException(cause); } } } private static Object invokeMethodThrowsIllegalArgumentException(Method method, Object thisObject, Object... args) throws IllegalArgumentException { try { return method.invoke(thisObject, args); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); cause.printStackTrace(); if (cause instanceof IllegalArgumentException) { throw (IllegalArgumentException) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new RuntimeException(e); } } } private static Object invokeMethodThrowsIOException(Method method, Object thisObject, Object... args) throws IOException { try { return method.invoke(thisObject, args); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); cause.printStackTrace(); if (cause instanceof IOException) { throw (IOException) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { throw new RuntimeException(e); } } } }