Android7/10/11 system realizes virtual Bluetooth and APP communication

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.

Guess you like

Origin blog.csdn.net/CGTAIHYQ/article/details/124864049