Android 4.4.2上与BLE 蓝牙锁设备的通讯

Android 4.4.2上与BLE 蓝牙锁设备的通讯

Android从4.3(Api level 18)开始支持BLE的开发,本文记录了Android 4.4.2设备与BLE设备通讯的流程。

权限需求:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

步骤:
1.获取蓝牙服务
BluetoothManager btManager= (BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
2.获取蓝牙设备管理器(适配器)
BluetoothAdapter mBluetoothAdapter = btManager.getAdapter();
3.判断设备是否支持BLE蓝牙通讯,支持则判断蓝牙是否已开启,未开启蓝牙的设置开启。
if (mBluetoothAdapter != null && (!mBluetoothAdapter.isEnabled())){
mBluetoothAdapter.enable();//打开蓝牙
}
4.扫描当前附近的蓝牙BLE设备(这个操作比较耗电,要适时地停止)
5.停止设备扫描
6.与指定BLE设备建立Gatt连接
7.在连接成功后,设置扫描BLE设备支持的service和characteristic,并设置。这里需要注意,需要开启通知的,需要设置相应描述符。
8.读写操作
9.断开Gatt连接
10.关闭Gatt连接

在开发过程中,遇到的主要问题是onCharacteristicWrite失败回调,这个可能跟低功耗蓝牙外设有关,需要断开并关闭Gatt连接重新建立

 下面是主要参考代码

复制代码

package com.rollup.bluetoothlock;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import com.android.peephole.MyApplication;
import com.rollup.bluetoothlock.MyLog;
import com.rollup.bluetoothlock.Utils;
import com.rollup.bluetoothlock.Value;



import android.R.integer;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAdapter.LeScanCallback;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Handler;
import android.os.Message;
import android.widget.EditText;
import android.widget.Toast;

public class BleManager {
    private static final String TAG = "BleManager";
    public static final int ERROR_NO_ERROR = 0;//成功
    public static final int ERROR_BTDEVICE_EXC_FAIL = 1;// 蓝牙锁端执行错误
    public static final int ERROR_PSD_ERROR = 2; // 钥匙错误
    public static final int ERROR_CONNECT_FAIL = 3;
    public static final int ERROR_DEFAULT = 4;
    public static final int ERROR_NOT_FOUND_DEVICE = 5;// 未扫描到指定编码的锁
    public static final int ERROR_HAVE_CONNECTED = 6;//上次连接未执行完毕
    public static final int ERROR_NO_DEVICE = 7;//设备没有初始化
    public static final int ERROR_CREATE_FAIL = 8;

    private static final int STOP_LESCAN = 1;
    private final int OVER_TIMER = 60 * 1000;// 扫描超时时间

    private Context mContext = null;
    private BluetoothAdapter mBluetoothAdapter = null;
    private BluetoothManager mBluetoothManager = null;
    public BluetoothDevice mBluetoothDevice = null;
    private BluetoothGatt mBluetoothGatt;
    private static boolean isScanning = false;
    private BluetoothGattCharacteristic writeCharacteristic = null;
    private static boolean isConnectToDevice = false;
    private String btName = null;
    private String psd = null;
    private boolean hasExcuteOpenDoor = false;// 开门命令已执行
    private byte[] devceKey;

    private static boolean haveRetry = false;
    private static BleManager instance = null;
    private MyHandler mHandler = null;

    public static long lastOpendoorTime = 0;

    private OnBleScanOpenDoorCallback openDoorCallback = null;

    public interface OnBleScanOpenDoorCallback {
        public void openDoorSuc();
        public void openDoorFail(int error);
    }

    private BleManager(Context context, String btName, String psd) {
        this.mContext = context;
        initBluetooth();
        if (mBluetoothManager == null) {
            MyLog.e(TAG, "Unable to initialize BluetoothManager.");
            return;
        }
        this.btName = btName;
        this.psd = psd;
        devceKey = new byte[16];
    }

    public static BleManager getInstance(String btName, String psd) {
        if (instance == null) {
            instance = new BleManager(MyApplication.getMyApplication(), btName,
                    psd);
        } else {
            instance.setBTinfo(btName, psd);
        }
        return instance;
    }

    private void setBTinfo(String name, String psd) {
        this.btName = name;
        this.psd = psd;
    }

    private void initBluetooth() {
        mHandler = new MyHandler(this);
        mBluetoothManager = (BluetoothManager) mContext
                .getSystemService(Context.BLUETOOTH_SERVICE);
        if (mBluetoothManager != null) {
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter != null) {
                if (!mBluetoothAdapter.isEnabled()) {
                    mBluetoothAdapter.enable(); // 打开蓝牙
                }
            }
        }
    }
    
    /**
     * 是否空闲可用
     * @return
     */
    public static boolean isFree(){
        return (!isConnectToDevice)&&(!isScanning);
    }

    public void startLeScan(boolean autoConnect) {
        if (mBluetoothAdapter == null) {
            return;
        }
        if (btName == null || btName.length() == 0) {
            return;
        }

        if (isScanning) {
            return;
        }
        isScanning = true;

        if (autoConnect) {
            mBluetoothAdapter.startLeScan(mLeScanCallback2);
        } else {
            mBluetoothAdapter.startLeScan(mLeScanCallback); // 此mLeScanCallback为回调函数
        }

        mHandler.sendEmptyMessageDelayed(STOP_LESCAN, OVER_TIMER); // 这个搜索30秒,如果搜索不到则停止搜索
    }

    private void stopLeScan() {
        if (mHandler != null && mHandler.hasMessages(STOP_LESCAN)) {
            mHandler.removeMessages(STOP_LESCAN);
        }
        if (mBluetoothAdapter == null) {
            return;
        }

        if (!isScanning) {
            return;
        }
        mBluetoothAdapter.stopLeScan(mLeScanCallback);
        mBluetoothAdapter.stopLeScan(mLeScanCallback2);
        isScanning = false;
    }

    private LeScanCallback mLeScanCallback = new LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
            MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
            if (device.getAddress() != null) {
                if (device.getName().toLowerCase().replace("_", "")
                        .contentEquals(btName.toLowerCase().replace(" ", ""))) {
                    mBluetoothDevice = device;
                    stopLeScan();
                }
            }
        }
    };

    private LeScanCallback mLeScanCallback2 = new LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int arg1, byte[] arg2) {
            MyLog.i(TAG, "onLeScan() DeviceName------>" + device.getName()); // 在这里可通过device这个对象来获取到搜索到的ble设备名称和一些相关信息
            if (device.getAddress() != null) {
                if (device.getName().toLowerCase().replace("_", "")
                        .contentEquals(btName.toLowerCase().replace(" ", ""))) {
                    mBluetoothDevice = device;
                    stopLeScan();
                    connect();
                }
            }
        }
    };

    private static class MyHandler extends Handler {
        WeakReference<BleManager> wf = null;

        public MyHandler(BleManager manager) {
            // TODO Auto-generated constructor stub
            wf = new WeakReference<BleManager>(manager);
        }

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            switch (msg.what) {
            case STOP_LESCAN:
                MyLog.i(TAG,  "MyHandler handleMessage STOP_LESCAN");
                if (wf.get()!=null){
                    wf.get().stopLeScan();
                }
                MyLog.i(TAG, "release(), Scan time is up");
                break;
            }
        }
    }

    public int connect() {
        if (mBluetoothDevice == null) {
            MyLog.i(TAG, "BluetoothDevice is null.");
            return ERROR_NO_DEVICE;
        }
        
        if(isConnectToDevice){
            MyLog.i(TAG, "Have connected to device");
            return ERROR_HAVE_CONNECTED;
        }
        isConnectToDevice = true;
        hasExcuteOpenDoor = false;

        // 两个设备通过BLE通信,首先需要建立GATT连接。这里我们讲的是Android设备作为client端,连接GATT Server

        mBluetoothGatt = mBluetoothDevice.connectGatt(mContext, false,
                mGattCallback); // mGattCallback为回调接口

        if (mBluetoothGatt != null) {

            if (mBluetoothGatt.connect()) {
                MyLog.d(TAG, "Connect succeed.");
                return ERROR_NO_ERROR;
            } else {
                MyLog.d(TAG, "Connect fail.");
                return ERROR_CONNECT_FAIL;
            }
        } else {
            MyLog.d(TAG, "BluetoothGatt null.");
            return ERROR_CREATE_FAIL;
        }
    }

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                isConnectToDevice = true;
                gatt.discoverServices(); // 执行到这里其实蓝牙已经连接成功了
                MyLog.i(TAG, "Connected to GATT server.");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                if (!hasExcuteOpenDoor) {
                    disconnect();
                    retryUnlock();
                    if (!haveRetry) {
                        //已失败重试过
                        if (openDoorCallback != null) {
                            openDoorCallback.openDoorFail(ERROR_CONNECT_FAIL);
                        }
                    } 
                } else {
                    MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED  hasExcuteOpenDoor=TRUE");
                    haveRetry = false;
                    disconnect();
                }
                MyLog.i(TAG, "BluetoothProfile.STATE_DISCONNECTED --release()");
            }
        }

        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                MyLog.i(TAG, "onServicesDiscovered");
                List<BluetoothGattService> services = gatt.getServices();
                for (int i = 0; i < services.size(); i++) {
                    MyLog.i(TAG, "[Service " + i + "] uuid:"
                            + services.get(i).getUuid());
                    if (services.get(i).getUuid().equals(Value.uuid)) {
                        List<BluetoothGattCharacteristic> characteristics = services
                                .get(i).getCharacteristics();
                        for (int j = 0; j < characteristics.size(); j++) {
                            MyLog.i(TAG, "[Characteristic]"
                                    + characteristics.get(j).getUuid());
                            if (characteristics.get(j).getUuid()
                                    .equals(Value.CHARACTERISTIC_READ)) {
                                boolean res = mBluetoothGatt
                                        .setCharacteristicNotification(
                                                characteristics.get(j), true);
                                // 打开通知描述
                                for (BluetoothGattDescriptor descriptor : characteristics
                                        .get(j).getDescriptors()) {
                                    descriptor
                                            .setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                                    gatt.writeDescriptor(descriptor);
                                    MyLog.i(TAG,
                                            "Characteristic set notification is Success!");
                                }
                                MyLog.i(TAG,
                                        "onServicesDiscovered setCharacteristicNotification result="
                                                + res);
                                break;
                            }
                        }
                        characteristics = null;
                    }
                }

                for (int i = 0; i < services.size(); i++) {
                    MyLog.i(TAG, "[Service " + i + "] uuid:"
                            + services.get(i).getUuid());
                    if (services.get(i).getUuid().equals(Value.uuid)) {
                        List<BluetoothGattCharacteristic> characteristics = services
                                .get(i).getCharacteristics();
                        for (int j = 0; j < characteristics.size(); j++) {
                            MyLog.i(TAG, "[Characteristic]"
                                    + characteristics.get(j).getUuid());
                            if (characteristics.get(j).getUuid()
                                    .equals(Value.CHARACTERISTIC_WRITE)) {
                                writeCharacteristic = characteristics.get(j);
                                final int charaProp = characteristics.get(j)
                                        .getProperties();
                                // 如果该char可写
                                if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                                    byte[] value = new byte[] { 0x01, 0x00,
                                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                            0x00, 0x00, 0x00 };
                                    characteristics.get(j).setValue(value);
                                    writeCharacteristic
                                            .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
                                    mBluetoothGatt
                                            .writeCharacteristic(characteristics
                                                    .get(j));
                                    value = null;
                                }
                                break;
                            }
                        }
                        characteristics = null;
                    }
                }
                services = null;
            } else {
                MyLog.i(TAG, "onServicesDiscovered status------>" + status);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            MyLog.i(TAG,
                    "onCharacteristicRead------>"
                            + Utils.bytesToHexString(characteristic.getValue()));

        }

        

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic) {
            MyLog.i(TAG,
                    "onCharacteristicChanged------>"
                            + Utils.bytesToHexString(characteristic.getValue()));
            MyLog.i(TAG, "UUID------>" + characteristic.getUuid().toString());

            if (Value.CHARACTERISTIC_READ.equals(characteristic.getUuid())) {
                byte[] value = characteristic.getValue();
                if ((value[0] & 0xFF) == 0x81) {
                    for (int i = 0; i < 16; i++) {
                        devceKey[i] = value[i + 1];
                    }
                    // MyLog.i(TAG, "加密秘钥为:"+Utils.bytesToHexString(devceKey));
                    if (writeCharacteristic != null) {
                        byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                0x00, 0x00, 0x00 };
                        if (psd.length() == 8) {
                            int j = 0;
                            for (int i = 0; i < 4; i++) {
                                cmd[5 + i] = (byte) (((((Integer.valueOf(psd
                                        .charAt(j)) - 48) << 4) & (0xF0)) + ((Integer
                                        .valueOf(psd.charAt(j + 1)) - 48) & 0x0F)) & 0xFF);
                                MyLog.i(TAG, "cmd" + (9 + i) + "=" + cmd[9 + i]);
                                j = j + 2;
                            }
                        }
                        byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
                        if (cmd_encrypt != null) {
                            byte[] request = new byte[17];
                            request[0] = 0x03;
                            for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
                                    : request.length); i++) {
                                request[i] = cmd_encrypt[i - 1];
                            }
                            writeCharacteristic.setValue(request);
                            writeCharacteristic
                                    .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
                            // MyLog.i(TAG,
                            // "发送开锁命令:"+Utils.bytesToHexString(request));
                            mBluetoothGatt
                                    .writeCharacteristic(writeCharacteristic);
                            request = null;
                        }
                        cmd_encrypt  = null;
                        cmd = null;
                    }

                } else if ((value[0] & 0xFF) == 0x83) {
                    byte[] res = new byte[16];
                    for (int i = 0; i < 16; i++) {
                        res[i] = value[i + 1];
                    }
                    // MyLog.i(TAG, "解密秘钥为:"+Utils.bytesToHexString(devceKey));
                    // MyLog.i(TAG, "待解密数据为:"+Utils.bytesToHexString(res));
                    byte[] decode_res = Utils.deCode(devceKey, res);
                    hasExcuteOpenDoor = true;
                    if (decode_res != null) {
                        MyLog.i(TAG,
                                "获取到蓝牙开锁返回数据:"
                                        + Utils.bytesToHexString(decode_res));
                        if ((decode_res[0] & 0xFF) == 0x01) {
                            if ((decode_res[1] & 0xFF) == 0x00) {
                                MyLog.i(TAG, "开锁成功");

                                if (openDoorCallback != null) {
                                    openDoorCallback.openDoorSuc();
                                }
                                lastOpendoorTime = System.currentTimeMillis();
                            } else if ((decode_res[1] & 0xFF) == 0x01) {
                                MyLog.i(TAG, "开锁失败:命令执行失败");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_BTDEVICE_EXC_FAIL);
                                }
                            } else if ((decode_res[1] & 0xFF) == 0x02) {
                                MyLog.i(TAG, "开锁失败:密钥解密失败,无效用户密钥");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_PSD_ERROR);
                                }
                            } else {
                                MyLog.e(TAG, "开锁返回异常");
                                if (openDoorCallback != null) {
                                    openDoorCallback
                                            .openDoorFail(ERROR_DEFAULT);
                                }
                            }

                            new Timer().schedule(new TimerTask() {

                                @Override
                                public void run() {
                                    // TODO Auto-generated method stub
                                    sendEndCmd();
                                }
                            }, 500);

                        } else {
                            MyLog.e(TAG, "开锁返回动作标志位异常,此时锁还会再次返回其他数据");
                            if (openDoorCallback != null) {
                                openDoorCallback.openDoorFail(ERROR_DEFAULT);
                            }
                        }

                    } else {
                        MyLog.e(TAG, "蓝牙开锁数据获取失败");
                        if (openDoorCallback != null) {
                            openDoorCallback.openDoorFail(ERROR_DEFAULT);
                        }
//                        disconnect(true);
                    }
                    decode_res = null;
                    res = null;
                } else {
                    MyLog.e(TAG, " value[0] =  " + value[0]
                            + " ===release()    11111111");
//                    disconnect(true);
                }
                value = null;
            }
        }

        // 接受Characteristic被写的通知,收到蓝牙模块的数据后会触发onCharacteristicWrite
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic, int status) {
            MyLog.i(TAG,
                    "onCharacteristicWrite status = " + status
                            + ",onCharacteristicWrite------>"
                            + Utils.bytesToHexString(characteristic.getValue()));
            if (status != 0) {
//                disconnect(true);
            }
        }
    };
    
    private void sendEndCmd() {
        if (writeCharacteristic != null) {
            byte[] cmd = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00 };
            byte[] cmd_encrypt = Utils.enCode(devceKey, cmd);
            if (cmd_encrypt != null) {
                byte[] request = new byte[17];
                request[0] = 0x08;
                for (int i = 1; i < (cmd_encrypt.length < request.length ? cmd_encrypt.length
                        : request.length); i++) {
                    request[i] = cmd_encrypt[i - 1];
                }
                writeCharacteristic.setValue(request);
                writeCharacteristic
                        .setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
                mBluetoothGatt.writeCharacteristic(writeCharacteristic);
                request = null;
            }
            cmd = null;
            cmd_encrypt = null;
        }
        // release();
    }

    private void retryUnlock() {
        if (!haveRetry) {
            haveRetry = true;
            connect();
        } else {
            haveRetry = false;
            MyLog.i(TAG, "retryUnlock  haveRetry");
        }
    }

    public void setOpenDoorCallback(OnBleScanOpenDoorCallback callback) {
        openDoorCallback = callback;
    }
    
    public synchronized void disconnect() {
        MyLog.i(TAG, "disconnect");
        if (mBluetoothGatt != null) {
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
        isConnectToDevice = false;
    }
}

复制代码

复制代码

package com.example.common;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class Utils {

     /** Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。  
     * @param src byte[] data  
     * @return hex string  
     */     
    public static String bytesToHexString(byte[] src){  
        StringBuilder stringBuilder = new StringBuilder("");  
        if (src == null || src.length <= 0) {  
            return null;  
        }  
        for (int i = 0; i < src.length; i++) {  
            int v = src[i] & 0xFF;  
            String hv = Integer.toHexString(v);  
            if (hv.length() < 2) {  
                stringBuilder.append(0);  
            }  
            stringBuilder.append(hv);  
        }  
        return stringBuilder.toString();  
    }  
    /** 
     * Convert hex string to byte[] 
     * @param hexString the hex string 
     * @return byte[] 
     */  
    public static byte[] hexStringToBytes(String hexString) {  
        if (hexString == null || hexString.equals("")) {  
            return null;  
        }  
        hexString = hexString.toUpperCase();  
        int length = hexString.length() / 2;  
        char[] hexChars = hexString.toCharArray();  
        byte[] d = new byte[length];  
        for (int i = 0; i < length; i++) {  
            int pos = i * 2;  
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));  
        }  
        return d;  
    }  
    
    /** 
     * Convert char to byte 
     * @param c char 
     * @return byte 
     */  
     private static byte charToByte(char c) {  
        return (byte) "0123456789ABCDEF".indexOf(c);  
    }  

    //将指定byte数组以16进制的形式打印到控制台  
    public static void printHexString( byte[] b) {    
       for (int i = 0; i < b.length; i++) {   
         String hex = Integer.toHexString(b[i] & 0xFF);   
         if (hex.length() == 1) {   
           hex = '0' + hex;   
         }   
         System.out.print(hex.toUpperCase() );   
       }   
      
    }  
    
    /**
     * 真正的加密过程
     * 1.通过密钥得到一个密钥专用的对象SecretKeySpec
     * 2.Cipher 加密算法,加密模式和填充方式三部分或指定加密算 (可以只用写算法然后用默认的其他方式)Cipher.getInstance("AES");
     * @param key
     * @param src
     * @return
     * @throws Exception
     */
    public static byte[] enCode(byte[] key, byte[] src) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding"); 
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(src);
            return encrypted;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
        
    }
    
     /**
     * 解密
     * @param view
     */
    public static byte[] deCode(byte[] key, byte[] src){ 
        try {
            //获取AESkey
            SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES/ECB/ZeroBytePadding"); //AES/ECB/ZeroBytePadding
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] decrypted = cipher.doFinal(src);
            byte[] result;
            if (decrypted.length<16){
                result = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
                for (int i=0; i<decrypted.length; i++){
                    result[i]=decrypted[i];
                }
            }else{
                result = decrypted;
            }
            return result;
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            return null;
        }
    }
}

复制代码

复制代码

package com.example.common;

import java.util.UUID;

public class Value {
    public static final String BLE_LOCK_SP_NAME = "BleLockConfig";
    public static final String KEY_BLE_LOCK_ID = "kEY_BLE_LOCK_ID";
    public static final String KEY_BLE_LOCK_KEY = "KEY_BLE_LOCK_KEY";
    
    public final static UUID uuid = UUID.fromString("0000ff12-0000-1000-8000-00805f9b34fb");
    public final static UUID CHARACTERISTIC_WRITE = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb");//主机BLE发送给BLE模块数据使用
    public final static UUID CHARACTERISTIC_READ = UUID.fromString("0000ff04-0000-1000-8000-00805f9b34fb");//BLE模块发送给主机BLE数据使用,NOTIFY功能发送,前提是双方连接上后,需要主机BLE主动打开BLE模块的UUID=0XFF04(NOTIFY)功能。
}

复制代码

复制代码

package com.example.bluetoothdemo;

import com.example.common.BleManager;
import com.example.common.Utils;
import com.example.common.BleManager.OnBleScanOpenDoorCallback;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class MainActivity extends Activity implements OnClickListener, OnBleScanOpenDoorCallback{
    
    private final String TAG = "MainActivity";
    private final String KEY_BLE_LOCK_ID = "kEY_BLE_LOCK_ID";
    private final String KEY_BLE_LOCK_KEY = "KEY_BLE_LOCK_KEY";
    
    private BleManager mBleManager = null;
    
    private RelativeLayout relativeLayout = null;
    private EditText idEditText = null;
    private EditText psdEditText = null;
    private Button scanButton = null;
    private Button connectButton = null;
    private ProgressDialog progressDialog = null;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        
        relativeLayout = (RelativeLayout) findViewById(R.id.relativelayout);
        scanButton = (Button) findViewById(R.id.button_scan);    
        connectButton = (Button) findViewById(R.id.button_connect);
        idEditText = (EditText) findViewById(R.id.edittext_lock_id);
        psdEditText = (EditText) findViewById(R.id.edittext_psd);
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage(getString(R.string.waiting));
        
        SharedPreferences sp = getPreferences(MODE_PRIVATE);
        String bleName = sp.getString(KEY_BLE_LOCK_ID, "ZY121711240013");
        String blePsd = sp.getString(KEY_BLE_LOCK_KEY, "12345678");
        idEditText.setText(bleName);
        psdEditText.setText(blePsd);
        
        relativeLayout.setOnClickListener(this);
        scanButton.setOnClickListener(this);        
        connectButton.setOnClickListener(this);
        
        mBleManager = BleManager.getInstance(bleName, blePsd);  //这个this是一个上下文,只要在上面的BleManager工具类定义一个有参数就好
        mBleManager.setOpenDoorCallback(this);
    }
    
    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
    }
    
    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        SharedPreferences sp = getPreferences(MODE_PRIVATE);
        Editor editable = sp.edit();
        editable.putString(KEY_BLE_LOCK_ID, idEditText.getText().toString().trim());
        editable.putString(KEY_BLE_LOCK_KEY, psdEditText.getText().toString().trim());
        editable.commit();
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch (v.getId()) {
        case R.id.button_scan:
            break;
        case R.id.relativelayout:
            InputMethodManager m = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            m .hideSoftInputFromWindow(relativeLayout.getWindowToken(), 0);//比如EditView
            break;
        case R.id.button_connect:
            progressDialog.show();
            int res = mBleManager.connect();
            
            if (res == BleManager.ERROR_NO_ERROR){
            }else if (res == BleManager.ERROR_HAVE_CONNECTED){
                //已经连接不能再次连
                progressDialog.dismiss();
            }else{
                mBleManager.startLeScan(true);
            }

            break;
        
        default:            
            break;
        }
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        
        if (mBleManager != null){
            mBleManager.setOpenDoorCallback(null);
            mBleManager.disconnect();
            mBleManager = null;
        }
        super.onDestroy();
    }


    @Override
    public void openDoorSuc() {
        // TODO Auto-generated method stub
        runOnUiThread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                if (progressDialog.isShowing()){
                    progressDialog.dismiss();
                }
                Toast.makeText(MainActivity.this, getString(R.string.open_door_suc), Toast.LENGTH_LONG).show();
            }
        });
        
    }

    @Override
    public void openDoorFail(final int error) {
        // TODO Auto-generated method stub
        runOnUiThread(new Runnable() {
            
            @Override
            public void run() {
                // TODO Auto-generated method stub
                if (progressDialog.isShowing()){
                    progressDialog.dismiss();
                }
                int resId  = R.string.error_default;
                if (error==BleManager.ERROR_BTDEVICE_EXC_FAIL){
                    resId = R.string.error_btdevice_exc_fail;
                }else if (error == BleManager.ERROR_CONNECT_FAIL){
                    resId = R.string.error_not_connect;
                }else if (error == BleManager.ERROR_PSD_ERROR){
                    resId = R.string.error_psd_error;
                }else if (error == BleManager.ERROR_NOT_FOUND_DEVICE){
                    resId = R.string.error_device_not_found;
                }
                Toast.makeText(MainActivity.this, resId, Toast.LENGTH_LONG).show();
            }
        });
        
    }
}

复制代码

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ble audio补丁原理是利用hidraw节点捕捉协议栈发送的语音数据,目前Android Blueroid将ble语音数据和按键信息通过hid发送出去,通过建立hidraw节点,可以从中捕捉到语音数据。目前通过ble hal实现从hidraw中读取遥控器语音数据,在Android框架层上就通过配置文件将ble hal导入到音频框架中,并通过绑定Android原生已有的耳麦设备来完成audio音频策略选择,通过apk检测ble连接状态,通知audio服务耳麦设备的状态就可以使得录音通路切换至ble hal,实现从ble获取录音数据功能。 打补丁前最好使用干净的环境,不要有别家方案ble补丁,否则可能会有不兼容问题。 补丁如若不能使用首先检查节点是否存在和其权限,正常节点权限如下: ls -l /dev/hidraw* crw-rw---- 1 system audio 241, 0 2018-12-18 13:42 /dev/hidraw0 audio用户组有读写权限。 2、如果selinux模式为Enforcing,可以通过logcat搜索avc关键字。有如下类似提示则为异常,提示进程没有权限,检查sepolicy是否设置正常: avc: denied { read } for name="/" dev="tmpfs" ino=6145 scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 5.0和6.0版本,audio hal被mediaserver进程加载 avc: denied { read } for name="/" dev="tmpfs" ino=8125 scontext=u:r:audioserver:s0 tcontext=u:object_r:device:s0 tclass=dir permissive=0 //Android 7.0版本,audio hal被audioserver进程加载 avc: denied { read } for name="hidraw" dev="sysfs" ino=16332 scontext=u:r:hal_audio_default:s0 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0 //Android 8.0和9.0版本,audio hal被android[email protected]进程加载 3、检查audio的配置,打上patch后,首先确认小机上文件是否有修改到,目前文件可能位于/vendor/etc或/system/etc目录下,其中/vendor/etc下的配置文件是优先解析的。确保文件无误后,通过dumpsys media.audio_policy查看ble hal是否正常加载。 以下是相关说明: AudioPolicyManager: 0xf20c5200 Command Thread: 0xf20af140 Tones Thread: 0xf20af020 ... - Available input devices: Device 1: - id: 3 - type: AUDIO_DEVICE_IN_BUILTIN_MIC - Profiles: Profile 0: - format: AUDIO_FORMAT_PCM_16_BIT - sampling rates:8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000 - channel masks:0x000c, 0x0010 Device 2: - id: 20 - type: AUDIO_DEVICE_IN_WIRED_HEADSET //对应的数值是0x80000010 - name: RemoteDM1204 - Profiles: Available input devices指示当前可用设备,目前ble hal是和AUDIO_DEVICE_IN_WIRED_HEADSET设备绑定,如果需要录音ble hal,AUDIO_DEVICE_IN_WIRED_HEADSET设备必须出现在可用设备中,如果没有,就可能是补丁中hidaudio.apk的问题。 HW Modules dump: ... - H

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值