package andraus.bluetoothhidemu.spoof;
import java.io.IOException;
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.content.Context;
/**
* Abstraction of a class that will implement the necessary spoofing for the bluetooth adapter.
*/
public abstract class BluetoothAdapterSpoofer {
protected static final String TAG = BluetoothHidEmuActivity.TAG;
protected boolean mSpoofed = false;
protected String mSetupErrorMsg;
protected Context mContext;
protected BluetoothAdapter mAdapter;
protected int mOriginalDeviceClass;
protected BluetoothAdapterSpoofer(Context appContext, BluetoothAdapter adapter) {
mContext = appContext;
mAdapter = adapter;
}
/**
* Returns a BluetoothSocket connected using L2CAP protocol.
*
* @param device
* @param port
* @param auth
* @param encrypt
* @return
* @throws IOException
*/
public abstract BluetoothSocket connectL2capSocket(BluetoothDevice device, int port,
boolean auth, boolean encrypt) throws IOException;
/**
* Returns current bluetooth device class number. Depending on the implementation, a <i>adapter</i> may
* or may not be required.
*
* format: 0xaabbcc, where:
* 0xaa -> service class number
* 0xbb -> major device number
* 0xcc -> minor device number
*
* @param adapter
* @return
*/
protected abstract int getBluetoothDeviceClass();
/**
* Spoof the bluetooth adapter device class
*
* @param deviceClass
* @return 0 if success
*/
protected abstract int spoofBluetoothDeviceClass(int deviceClass);
/**
* Add the SDP record for the selected spoofing mode.
*
* @param mode
* @return handle for the SDP record entry.
*/
protected abstract int addHidDeviceSdpRecord(SpoofMode mode);
/**
* Removes the SDP record of mHidSdpHandle
*/
protected abstract void delHidDeviceSdpRecord();
/**
* Performs tear-up actions.
*
* @param mode
*/
public void tearUpSpoofing(SpoofMode mode) throws IllegalStateException {
if (mSpoofed) {
throw new IllegalStateException("Bluetooth device already spoofed");
}
DoLog.d(TAG, "original class = 0x" + Integer.toHexString(getBluetoothDeviceClass()));
final int newClass = Spoof.getBluetoothDeviceClass(mode);
if (newClass != 0) {
int err = spoofBluetoothDeviceClass(newClass);
DoLog.d(TAG, "set class ret = " + err);
}
final int sdpRecHandle = addHidDeviceSdpRecord(mode);
DoLog.d(TAG, "SDP record handle = " + Integer.toHexString(sdpRecHandle));
mSpoofed = true;
}
/**
* Performs tear-down actions and necessary clean-up actions.
*/
public void tearDownSpoofing() throws IllegalStateException {
if (!mSpoofed) {
throw new IllegalStateException("Bluetooth device not spoofed");
}
if (mOriginalDeviceClass != 0) {
spoofBluetoothDeviceClass(mOriginalDeviceClass);
}
delHidDeviceSdpRecord();
mSpoofed = false;
}
/**
* Sets bluetooth adapter in discoverable mode for <b>duration</b> seconds.
*
* Implemented through reflection, since BluetoothAdapter.setScanMode() is not visible.
*
* Note: setScanMode enforces android.permission.WRITE_SECURE_SETTINGS which is only availabel
* for applications signed on platform, so this method is deprecated for now.
*
* @deprecated
*
* @param duration
* @return
*/
public boolean setDiscoverableScanMode(final int duration) {
Boolean success;
try {
final Method setScanModeMethod = BluetoothAdapter.class
.getMethod("setScanMode", new Class<?>[] { int.class, int.class });
setScanModeMethod.setAccessible(true);
success = (Boolean) setScanModeMethod
.invoke(mAdapter, new Object[] { BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, duration });
} 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 Boolean.valueOf(success);
}
/**
*
* @return
*/
public boolean isSpoofed() {
return mSpoofed;
}
/**
* Perform a requirements check for necessary for the underlying implementation to work, like
* being root, specific vendor, etc.
*
* @return
*/
public abstract boolean requirementsCheck();
/**
* Retrieves get failure message if setup() call failed.
* @return
*/
public String getSetupErrorMsg() {
return mSetupErrorMsg;
}
}