package andraus.bluetoothhidemu.spoof; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import andraus.bluetoothhidemu.BluetoothHidEmuActivity; import andraus.bluetoothhidemu.spoof.Spoof.SpoofMode; import andraus.bluetoothhidemu.util.DoLog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.os.ParcelUuid; /** * Same as BluetoothAdapterSpooferMotoImpl, except that it calls framework functions * through Reflection */ public class BluetoothAdapterSpooferMotoReflect extends BluetoothAdapterSpoofer { private static final String TAG = BluetoothHidEmuActivity.TAG; private int mHidSdpHandle; /** * * @param adapter */ BluetoothAdapterSpooferMotoReflect(BluetoothAdapter adapter) { super(null, adapter); } /** * Returns current bluetooth device class number. * * format: 0xaabbcc, where: * 0xaa -> service class number * 0xbb -> major device number * 0xcc -> minor device number * * @return */ protected int getBluetoothDeviceClass() { Integer devClass = 0; try { Method getAdapterClassMethod = BluetoothAdapter.class.getMethod("getAdapterClass", (Class<?>[]) null); getAdapterClassMethod.setAccessible(true); devClass = (Integer) getAdapterClassMethod.invoke(mAdapter, (Object[]) null); } catch (SecurityException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (NoSuchMethodException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalArgumentException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalAccessException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (InvocationTargetException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } return Integer.valueOf(devClass); } /** * Spoof the bluetooth device class number. Format of <i>deviceClass</i> follows the same pattern from method * <i>getBluetoothDeviceClass</i>. * * @param deviceClass * @return */ protected int spoofBluetoothDeviceClass(int deviceClass) { mOriginalDeviceClass = getBluetoothDeviceClass(); Integer newClass = 0; try { final Method spoofAdapterClassMethod = BluetoothAdapter.class.getMethod("spoofAdapterClass", new Class<?>[] { int.class }); spoofAdapterClassMethod.setAccessible(true); newClass = (Integer) spoofAdapterClassMethod.invoke(mAdapter, new Object[] { deviceClass }); } catch (SecurityException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (NoSuchMethodException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalArgumentException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalAccessException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (InvocationTargetException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } return Integer.valueOf(newClass); } /** * Adds a custom SDP record to enable HID emulation over bluetooth. * * @return */ public int addHidDeviceSdpRecord(SpoofMode mode) { if (mHidSdpHandle != 0) { DoLog.w(TAG, String.format("HID SDP record already present. Handle: 0x%06X",mHidSdpHandle)); return mHidSdpHandle; } final String methodName = ( mode == SpoofMode.HID_GENERIC) ? "addHidKeybSdpRecord" : "addHidBdRemoteSdpRecord"; Integer handle = 0; try { Method addHidSdpRecordMethod = BluetoothAdapter.class.getMethod(methodName, (Class<?>[]) null); addHidSdpRecordMethod.setAccessible(true); handle = (Integer) addHidSdpRecordMethod.invoke(mAdapter, (Object[]) null); } catch (SecurityException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (NoSuchMethodException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalArgumentException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalAccessException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (InvocationTargetException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } mHidSdpHandle = Integer.valueOf(handle); return mHidSdpHandle; } /** * Removes a SDP record identified by <i>handle</i>. * implementation. * * @param adapter */ protected void delHidDeviceSdpRecord() { if (mHidSdpHandle == 0) { DoLog.w(TAG, "No HID SDP record handle present."); return; } try { Method removeServiceRecordMethod = BluetoothAdapter.class.getMethod("removeServiceRecord", new Class<?>[] { int.class }); removeServiceRecordMethod.setAccessible(true); removeServiceRecordMethod.invoke(mAdapter, new Object[] { mHidSdpHandle }); mHidSdpHandle = 0; } catch (SecurityException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (NoSuchMethodException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalArgumentException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (IllegalAccessException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } catch (InvocationTargetException e) { DoLog.e(TAG, "reflection error: ", e); throw new IllegalStateException(e); } } @Override public BluetoothSocket connectL2capSocket(BluetoothDevice device, int port, boolean auth, boolean encrypt) throws IOException { final int TYPE_L2CAP = 3; BluetoothSocket socket = null; Class<?>[] argsClasses = new Class[] { int.class /* type */, int.class /* fd */, boolean.class /* auth */, boolean.class /* encrypt */, BluetoothDevice.class /* device */, int.class /* port */, ParcelUuid.class /* uuid */ }; Object[] args = new Object[] { Integer.valueOf(TYPE_L2CAP), Integer.valueOf(-1), Boolean.valueOf(auth), Boolean.valueOf(encrypt), device, Integer.valueOf(port), null }; try { Class<BluetoothSocket> bluetoothSocketClass = BluetoothSocket.class; Constructor<BluetoothSocket> bluetoothSocketConstructor; bluetoothSocketConstructor = bluetoothSocketClass.getDeclaredConstructor(argsClasses); bluetoothSocketConstructor.setAccessible(true); socket = bluetoothSocketConstructor.newInstance(args); } catch (SecurityException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } catch (NoSuchMethodException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } catch (IllegalArgumentException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } catch (InstantiationException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } catch (IllegalAccessException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } catch (InvocationTargetException e) { DoLog.e(TAG, "Reflection error:", e); throw new IOException("Reflection error", e); } return socket; } @Override public boolean requirementsCheck() { // TODO implement check for frameworks support return true; } }