I. Introduction
First of all, let's think about a problem. Ordinary APP can realize the transmission between devices by connecting to the specified Bluetooth.
So how to implement the resident Bluetooth link to transmit data? In fact, the idea is very simple, it is to imitate the system
Bluetooth implementation, grafting a virtual Bluetooth on the android native Bluetooth, the APP can have a long link with the system Bluetooth, this article
It is mainly divided into two parts to explain and implement, namely the APP side and the system side.
2. System side
2.1, the classes involved
frameworks/base/core/java/android/bluetooth/BluetoothDevice.java
frameworks/base/core/java/android/bluetooth/BluetoothSocket.java
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterProperties.java
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java
packages/apps/Settings/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragment.java
packages/apps/Settings/src/com/android/settings/bluetooth/SavedBluetoothDeviceUpdater.java
2.1.1, framework layer modification
1. First add the method of creating a Bluetooth device in the BluetoothDevice class
/** @hide */
public static BluetoothDevice createVirtualBluetoothDevice(String address) {
return new BluetoothDevice(address);
}
2. Then create a virtual bluetooth socket according to the address in the connect method in the BluetoothSocket (API class for connecting devices) class.
Tooth, the current demonstration address is named: 00:11:22:33:44:55, and the Bluetooth name is: BluetoothPrinter.
/**
* Attempt to connect to a remote device.
* <p>This method will block until a connection is made or the connection
* fails. If this method returns without an exception then this socket
* is now connected.
* <p>Creating new connections to
* remote Bluetooth devices should not be attempted while device discovery
* is in progress. Device discovery is a heavyweight procedure on the
* Bluetooth adapter and will significantly slow a device connection.
* Use {@link BluetoothAdapter#cancelDiscovery()} to cancel an ongoing
* discovery. Discovery is not managed by the Activity,
* but is run as a system service, so an application should always call
* {@link BluetoothAdapter#cancelDiscovery()} even if it
* did not directly request a discovery, just to be sure.
* <p>{@link #close} can be used to abort this call from another thread.
*
* @throws IOException on error, for example connection failure
*/
public void connect() throws IOException {
if (mDevice == null) throw new IOException("Connect is called on null device");
Log.d("dzm","BluetoothSocket connect mDevice.getAddress() = " + mDevice.getAddress());
try {
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
IBluetooth bluetoothProxy =
BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
mPfd = bluetoothProxy.getSocketManager().connectSocket(mDevice, mType,
mUuid, mPort, getSecurityFlags());
synchronized (this) {
if (DBG) Log.d(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
if (mPfd == null) throw new IOException("bt socket connect failed");
//add by cgt start
String VirtualBluetoothAddress = "00:11:22:33:44:55";
String VirtualBluetoothName = "BluetoothPrinter";
if( mDevice.getAddress().equals(VirtualBluetoothAddress)){
Log.d("dzm","BluetoothSocket connect() -new LocalSocket if ");
mSocket = new LocalSocket();
mSocket.connect(new LocalSocketAddress(VirtualBluetoothName));
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
Log.d("dzm","BluetoothSocket connect mSocketIS = " + mSocketIS);
Log.d("dzm","BluetoothSocket connect mSocketOS = " + mSocketOS);
mSocketState = SocketState.CONNECTED;
return ;
}
//add by cgt end
FileDescriptor fd = mPfd.getFileDescriptor();
mSocket = LocalSocket.createConnectedLocalSocket(fd);
mSocketIS = mSocket.getInputStream();
mSocketOS = mSocket.getOutputStream();
}
int channel = readInt(mSocketIS);
if (channel <= 0) {
throw new IOException("bt socket connect failed");
}
mPort = channel;
waitSocketSignal(mSocketIS);
synchronized (this) {
if (mSocketState == SocketState.CLOSED) {
throw new IOException("bt socket closed");
}
mSocketState = SocketState.CONNECTED;
}
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
throw new IOException("unable to send RPC: " + e.getMessage());
}
}
3. The modification of sepolicy permission, because ordinary APP cannot directly operate the added virtual Bluetooth device, it is necessary to modify the permission APP
In order to connect to Bluetooth, there may be differences in the places where the permissions are added between platforms, but the same goal is the same. This article only shows the permission modification of the RK platform
device/rockchip/common/sepolicy/vendor/untrusted_app.te
device/rockchip/common/sepolicy/vendor/untrusted_app_25.te
device/rockchip/common/sepolicy/vendor/untrusted_app_27.te
system/sepolicy/prebuilts/api/30.0/private/untrusted_app_29.te
system/sepolicy/private/untrusted_app_29.te
Add permissions to the above class
allow untrusted_app platform_app:unix_stream_socket connectto;
2.1.2, native Bluetooth modification
1. First add virtual Bluetooth to the remote device list in the RemoteDevices class
//add by cgt start
public static final String VirtualBluetoothAddress = "00:11:22:33:44:55";
public static final byte[] VirtualBluetoothAddressbyte = new byte[]{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
public void addVirtualBluetoothDeviceProperties(){
Log.d("cgt","RemoteDevices addVirtualBluetoothDeviceProperties + " +SystemProperties.get("persist.sys.btname", "BluetoothPrinter"));
DeviceProperties prop = new DeviceProperties();
BluetoothDevice virtualDevice = BluetoothDevice.createVirtualBluetoothDevice(VirtualBluetoothAddress);
prop.mDevice = virtualDevice;
prop.mName = SystemProperties.get("persist.sys.btname", "BluetoothPrinter"); //"BluetoothPrinter";
prop.mAddress = VirtualBluetoothAddressbyte;
prop.mBluetoothClass = 0x0600;
prop.mDeviceType = 2;
prop.mBondState = 10;
synchronized (mDevices) {
DeviceProperties device = mDevices.get(virtualDevice);
if(device == null){
mDevices.put(VirtualBluetoothAddress, prop);
}
}
}
//add by cgt end
2. The internal class DeviceProperties of the RemoteDevices class provides a method for setting the virtual Bluetooth name
void setVirtualName(String name) {
synchronized (mObject) {
this.mName = name;
}
}
3. Add a virtual Bluetooth device to the startDiscovery method in the AdapterService class and send a broadcast notification
boolean startDiscovery(String callingPackage, @Nullable String callingFeatureId) {
UserHandle callingUser = UserHandle.of(UserHandle.getCallingUserId());
debugLog("startDiscovery");
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
boolean isQApp = Utils.isQApp(this, callingPackage);
String permission = null;
if (Utils.checkCallerHasNetworkSettingsPermission(this)) {
permission = android.Manifest.permission.NETWORK_SETTINGS;
} else if (Utils.checkCallerHasNetworkSetupWizardPermission(this)) {
permission = android.Manifest.permission.NETWORK_SETUP_WIZARD;
} else if (isQApp) {
if (!Utils.checkCallerHasFineLocation(this, mAppOps, callingPackage, callingFeatureId,
callingUser)) {
return false;
}
permission = android.Manifest.permission.ACCESS_FINE_LOCATION;
} else {
if (!Utils.checkCallerHasCoarseLocation(this, mAppOps, callingPackage, callingFeatureId,
callingUser)) {
return false;
}
permission = android.Manifest.permission.ACCESS_COARSE_LOCATION;
}
synchronized (mDiscoveringPackages) {
mDiscoveringPackages.add(new DiscoveringPackage(callingPackage, permission));
}
//add by cgt start
Log.d("cgt","AdapterService startDiscovery() +");
mRemoteDevices.addVirtualBluetoothDeviceProperties();
BluetoothDevice virtualDevice = BluetoothDevice.createVirtualBluetoothDevice(RemoteDevices.VirtualBluetoothAddress);
Log.d("cgt","AdapterService startDiscovery() -VirtualBluetoothAddress = " + RemoteDevices.VirtualBluetoothAddress);
Intent intentVirtual = new Intent(BluetoothDevice.ACTION_FOUND);
intentVirtual.putExtra(BluetoothDevice.EXTRA_DEVICE,virtualDevice);
intentVirtual.putExtra(BluetoothDevice.EXTRA_NAME, SystemProperties.get("persist.sys.btname", "BluetoothPrinter"));
intentVirtual.putExtra(BluetoothDevice.EXTRA_CLASS, new BluetoothClass(BluetoothClass.Device.Major.IMAGING));
this.sendBroadcast(intentVirtual);
Log.d("cgt","AdapterService startDiscovery() - sendBroadcast BluetoothDevice.ACTION_FOUND");
//add by cgt end
return startDiscoveryNative();
}
4. Create a Bluetooth connection bond in the createBond method in the AdapterService class, and connect the virtual Bluetooth
boolean createBond(BluetoothDevice device, int transport, OobData oobData) {
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
add by cgt start
if(Utils.getAddressStringFromByte(deviceProp.getAddress()).equals(RemoteDevices.VirtualBluetoothAddress)){
deviceProp.setBondState(12);
return true;
}
add by cgt end
return false;
}
mRemoteDevices.setBondingInitiatedLocally(Utils.getByteAddress(device));
// Pairing is unreliable while scanning, so cancel discovery
// Note, remove this when native stack improves
cancelDiscoveryNative();
Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
msg.obj = device;
msg.arg1 = transport;
if (oobData != null) {
Bundle oobDataBundle = new Bundle();
oobDataBundle.putParcelable(BondStateMachine.OOBDATA, oobData);
msg.setData(oobDataBundle);
}
mBondStateMachine.sendMessage(msg);
return true;
}
5. Add the virtual Bluetooth device option to the list in the AdapterProperties Bluetooth device adapter
/**
* @return the mBondedDevices
*/
BluetoothDevice[] getBondedDevices() {
Log.d("cgt","getBondedDevices + ");
BluetoothDevice[] bondedDeviceList = new BluetoothDevice[0];
try {
//add by cgt start
Log.d("cgt","AdapterProperties getBondedDevices() -VirtualBluetoothAddress = " + RemoteDevices.VirtualBluetoothAddress);
BluetoothDevice device = BluetoothDevice.createVirtualBluetoothDevice(RemoteDevices.VirtualBluetoothAddress);
if(mRemoteDevices != null){
DeviceProperties prop = mRemoteDevices.getDeviceProperties(device);
if(prop == null){
Log.d("cgt","AdapterProperties getBondedDevices() - prop = null");
mRemoteDevices.addVirtualBluetoothDeviceProperties();
prop = mRemoteDevices.getDeviceProperties(device);
}else{
if (prop.getAddress() != null){
Log.d("cgt","BondedDevices is VirtualBluetooth Address: " + Arrays.equals(prop.getAddress(),RemoteDevices.VirtualBluetoothAddressbyte));
if(Arrays.equals(prop.getAddress(),RemoteDevices.VirtualBluetoothAddressbyte)){
prop.setVirtualName(SystemProperties.get("persist.sys.btname", "BluetoothPrinter"));
}
}
}
Log.d("cgt","AdapterProperties getBondedDevices() - setBondState=12");
prop.setBondState(12);
mBondedDevices.add(device);
}
//add by cgt end
bondedDeviceList = mBondedDevices.toArray(bondedDeviceList);
} catch (ArrayStoreException ee) {
errorLog("Error retrieving bonded device array");
}
infoLog("getBondedDevices: length=" + bondedDeviceList.length);
return bondedDeviceList;
}
2.1.3, Native Settings modification
1. Set the virtual Bluetooth name in the setDeviceName method in the RemoteDeviceNameDialogFragment class
@Override
protected void setDeviceName(String deviceName) {
if (mDevice != null) {
Log.d("cgt","RemoteDeviceNameDialogFragment mDevice.getAddress() = " + mDevice.getAddress() + " , deviceName = " + deviceName + " , deviceName.length: " + deviceName.length());
if(mDevice.getAddress().equals("00:11:22:33:44:55")){
/*if(deviceName.length()>85){
Toast.makeText(getContext(),"exceed the maximum length,pls input again",Toast.LENGTH_LONG).show();
return;
}*/
try {
SystemProperties.set("persist.sys.btname", deviceName);
} catch (Exception e) {
e.printStackTrace();
Log.e("cgt","setDeviceName error : " + e.getMessage());
Toast.makeText(getContext(),"exceed the maximum length,pls input again",Toast.LENGTH_LONG).show();
return;
}
}
mDevice.setName(deviceName);
}
}
2. In the update method in the SavedBluetoothDeviceUpdater class, add the virtual Bluetooth item to the set Bluetooth
List
@Override
public void update(CachedBluetoothDevice cachedDevice) {
Log.d(TAG, "cgt update cachedDevice getAddress : " + cachedDevice.getAddress() + " , name : " + cachedDevice.getName());
if (isFilterMatched(cachedDevice) || ("00:11:22:33:44:55".equals(cachedDevice.getAddress()))) {
// Add the preference if it is new one
addPreference(cachedDevice, BluetoothDevicePreference.SortType.TYPE_NO_SORT);
} else {
removePreference(cachedDevice);
}
}
At this point, the content on the system side has been added, and there will be a virtual Bluetooth item we created in the set Bluetooth
BluetoothPrinter, APP connects to this virtual Bluetooth to transmit data after turning on Bluetooth.
3. APP
In fact, the APP side only needs to do two steps, one is to connect to the corresponding virtual Bluetooth, get the socket object and then transfer the data
It's okay to lose, the example is as follows
private BluetoothSocket mSocket;
private OutputStream mOutputStream = null;
public BluetoothSocket connectDevice() {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice("00:11:22:33:44:55");
BluetoothSocket socket = null;
try {
socket = device.createRfcommSocketToServiceRecord(
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
socket.connect();
mOutputStream = socket.getOutputStream();
} catch (IOException e) {
try {
socket.close();
} catch (IOException closeException) {
return null;
}
return null;
}
return socket;
}
public void sendDataSocket(byte[] bs){
try {
mOutputStream.write(bs);
} catch (IOException e) {
e.printStackTrace();
}
}
Four. Summary
The introduction of virtual Bluetooth is basically completed here. In fact, only when there is a need in the project will we think about how to implement it. If
Where there is no practical application, even powerful technologies will not be adopted. Whether it is a system or an APP, they are inseparable.
The breadth and depth of creative thinking can only be achieved through the accumulation of a large number of projects and technologies. The next update will achieve static
The default installation method.