com.ec.android.module.bluetooth40.base.BaseBluetoothControlWithProtocolActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.ec.android.module.bluetooth40.base.BaseBluetoothControlWithProtocolActivity.java

Source

/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.ec.android.module.bluetooth40.base;

import android.bluetooth.BluetoothGattCharacteristic;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.ec.android.module.bluetooth40.BluetoothLeService;
import com.ec.android.module.bluetooth40.protocol.Packet;
import com.ec.android.module.bluetooth40.protocol.PacketTools;
import com.ec.android.module.bluetooth40.protocol.ProtocolUtils;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;

import org.apache.commons.lang.ArrayUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * For a given BLE device, this Activity provides the user interface to connect, display data,
 * and display GATT services and characteristics supported by the device.  The Activity
 * communicates with {@code BluetoothLeService}, which in turn interacts with the
 * Bluetooth LE API.
 * <p/>
 * ???
 */
public abstract class BaseBluetoothControlWithProtocolActivity extends BaseBluetoothIsOpenActivity {
    private final static String TAG = BaseBluetoothControlWithProtocolActivity.class.getSimpleName();
    //?
    private static final int RETRY_TIMES = 3;
    //
    private static final int OVERTIME = 3000;
    //
    private LocalBroadcastManager mLocalBroadcastManager;
    //
    public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
    public static final String EXTRAS_DEVICE_NICK_NAME = "DEVICE_NICK_NAME";
    public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

    //    private TextView mConnectionState;
    //    private TextView mDataField;
    protected String mDeviceName;
    protected String mNickName;
    protected String mDeviceAddress;
    //    private ExpandableListView mGattServicesList;
    protected BluetoothLeService mBluetoothLeService;

    protected boolean mConnected = false;
    //
    protected BluetoothGattCharacteristic mNotifyCharacteristic;
    //
    protected BluetoothGattCharacteristic mWriteCharacteristic;
    //
    //    protected BluetoothGattCharacteristic mNotifyCharacteristic;

    // Code to manage Service lifecycle.
    private final ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            // Automatically connects to the device upon successful start-up initialization.
            mBluetoothLeService.connect(mDeviceAddress);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

    // Handles various events fired by the Service.
    // ACTION_GATT_CONNECTED: connected to a GATT server.
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
    //                        or notification operations.
    /*
    */
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_ERROR.equals(action)) {
                //                mConnected = true;
                onActionGattError(intent);
            } else if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                onActionGattConnected(intent);
                //                updateConnectionState(R.string.connected);
                //                invalidateOptionsMenu();
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                onActionGattDisConnected(intent);
                //                updateConnectionState(R.string.disconnected);
                //                invalidateOptionsMenu();
                //                clearUI();
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the user interface.
                //FIXME
                //                displayGattServices(mBluetoothLeService.getSupportedGattServices());
                onActionGattServicesDiscovered(intent);
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                byte[] data = intent.getByteArrayExtra(BluetoothLeService.EXTRA_DATA);
                //FIXME
                onReceiveData(data);
                //                onActionDataAvailable(intent);
            }
        }
    };

    protected abstract void onActionGattError(Intent intent);

    protected abstract void onActionGattConnected(Intent intent);

    protected abstract void onActionGattDisConnected(Intent intent);

    protected abstract void onActionGattServicesDiscovered(Intent intent);

    protected abstract void onActionDataAvailable(Byte[] data);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(getApplicationContext());
        //
        final Intent intent = getIntent();
        mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
        mNickName = intent.getStringExtra(EXTRAS_DEVICE_NICK_NAME);
        mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

        //
        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
        initFuture();
    }

    @Override
    protected void onResume() {
        super.onResume();
        mLocalBroadcastManager.registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        if (mBluetoothLeService != null) {
            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
            Log.d(TAG, "Connect request result=" + result);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        mLocalBroadcastManager.unregisterReceiver(mGattUpdateReceiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
        mBluetoothLeService = null;
    }

    private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_ERROR);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }

    /**
     * 
     * FIXME
     *
     * @param content
     */
    protected void writeContent(final String content) {
        //
        ListenableFuture<Object> future = mListeningExecutorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                processSendContent(content);
                return null;
            }
        });
        //
    }

    /**
     * ExecutorService
     */
    private ListeningExecutorService mListeningExecutorService;

    private void initFuture() {
        //?
        ExecutorService mSingleExecutorService = Executors.newSingleThreadExecutor();

        mListeningExecutorService = MoreExecutors.listeningDecorator(mSingleExecutorService);
    }

    ///////
    private ProtocolDataUtils mProtocolDataUtils = new ProtocolDataUtils();

    /**
     * ????
     *
     * @param content
     */
    private void processSendContent(String content) {
        try {
            //????
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //
        PacketTools packetTools = new PacketTools();
        ////
        mProtocolDataUtils.putPacketTools(packetTools.getSequenceId(), packetTools);
        ////
        packetTools.setSendContent(content);

        List<Byte[]> list = packetTools.translateBytes();

        if (!list.isEmpty()) {
            Packet.Builder builder = null;
            //
            //            int retryTimes = 0;
            loops: for (int retry = 0; retry < RETRY_TIMES; retry++) {
                //
                //                if (retryTimes > 3) {
                //                    break loops;
                //                }
                //
                for (int i = 0; i < list.size(); i++) {
                    //
                    Byte[] payloadBytes = list.get(i);

                    if (i == 0) {
                        builder = packetTools.newMorePacketFirstBuilder();
                        //
                    } else if (i == (list.size() - 1)) {
                        builder = packetTools.newMorePacketFinalBuilder();
                    } else {
                        builder = packetTools.newMorePacketCommonBuilder();
                    }
                    //
                    int length = payloadBytes.length;
                    builder.setPayloadLength((byte) length);
                    //
                    Short crc = ProtocolUtils.getCrc(builder);
                    builder.setCrc(crc);
                    //
                    builder.setPayloadBytes(payloadBytes);
                    //
                    Packet packet = builder.create();
                    //
                    if (mWriteCharacteristic != null) {
                        mWriteCharacteristic.setValue(ArrayUtils.toPrimitive(packet.translateBytes()));
                        mBluetoothLeService.writeCharacteristic(mWriteCharacteristic);
                    }
                    //
                    int overTime = 0;
                    while (true) {
                        Boolean errFlag = mProtocolDataUtils.getErrFlag(packetTools.getSequenceId());

                        if (errFlag == null) {
                            //errFlag
                            overTime += 100;
                            // FIXME
                            if (overTime >= OVERTIME) {
                                //FIXME ?
                                //                                retryTimes++;
                                mProtocolDataUtils.putErrFlag(packetTools.getSequenceId(), null);
                                continue loops;
                            }
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        } else if (errFlag) {
                            //FIXME ?
                            //                            retryTimes++;
                            mProtocolDataUtils.putErrFlag(packetTools.getSequenceId(), null);
                            continue loops;
                        } else if (!errFlag) {
                            // errFlag
                            mProtocolDataUtils.putErrFlag(packetTools.getSequenceId(), null);
                            break;
                        }
                    }
                    //

                    //                try {
                    //                    Thread.sleep(500);
                    //                } catch (InterruptedException e) {
                    //                    e.printStackTrace();
                    //                }
                }
                //
                break loops;
            }

        }
    }

    protected void writeContent(Byte[] bytes) {
        writeContent(new String(ArrayUtils.toPrimitive(bytes)));
    }

    /**
     * ?
     * TODO ?
     *
     * @param data
     */
    private void onReceiveData(byte[] data) {
        if (data.length < 7) {
            //?7header??
            return;
        }
        //
        byte magic = data[0];

        if (magic != Packet.MAGIC_BYTE) {
            //?
            return;
        }
        //
        byte dataMuti = data[1];
        byte errFlag = (byte) (dataMuti & 0x08);
        //
        int payloadLength = data[2];

        if (payloadLength < 0) {
            payloadLength = 256 + payloadLength;
        }
        ////
        byte sequenceIdHigh8 = data[3];
        byte sequenceIdLow8 = data[4];
        //
        int sequenceIdHigh8Int = sequenceIdHigh8;
        int sequenceIdLow8Int = sequenceIdLow8;
        //
        if (sequenceIdHigh8 < 0) {
            sequenceIdHigh8Int = (256 + sequenceIdHigh8);
        }
        if (sequenceIdLow8 < 0) {
            sequenceIdLow8Int = (256 + sequenceIdLow8);
        }
        //
        //        short sequenceIdTemp = 0;
        //sequenceIdpacket
        short sequenceId = (short) ((sequenceIdHigh8Int << 8) + sequenceIdLow8Int);
        //
        if (errFlag == 0x08) {
            //??1
            mProtocolDataUtils.putErrFlag(sequenceId, true);
            return;
        } else {
            //???0
            mProtocolDataUtils.putErrFlag(sequenceId, false);
        }
        //
        byte ackFlag = (byte) (dataMuti & 0x04);
        if (ackFlag == 0x04) {
            //??1
            //packet?

        } else {
            //???
            //??
            //
            byte beginFlag = (byte) (dataMuti & 0x02);
            byte endFlag = (byte) (dataMuti & 0x01);
            //?packet
            //
            int myCrc = magic ^ data[1] ^ payloadLength ^ sequenceId;

            int crcHigh = data[5];
            int crcLow = data[6];

            if (crcHigh < 0) {
                crcHigh = 256 + crcHigh;
            }
            if (crcLow < 0) {
                crcLow = 256 + crcLow;
            }

            //            int crcTemp = 0x00;
            //
            int crc = (crcHigh << 8) + crcLow;

            if (crc != myCrc) {
                //?
                mProtocolDataUtils.removeGetDataMap(sequenceId);
                return;
            }
            //?
            byte[] bodyBytes = ArrayUtils.subarray(data, 7, data.length);
            //
            if (beginFlag == 0x02) {
                //??1?packet
                //packet??sequenceId??????
                mProtocolDataUtils.removeGetDataMap(sequenceId);
                mProtocolDataUtils.plusGetDataMap(sequenceId, ArrayUtils.toObject(bodyBytes));
            } else {
                //??0??packet
                //???sequenceId???
                ArrayList<Byte[]> getDataMapList = mProtocolDataUtils.getGetDataMap(sequenceId);
                if (getDataMapList == null || getDataMapList.isEmpty()) {
                    return;
                } else {
                    //??
                    mProtocolDataUtils.plusGetDataMap(sequenceId, ArrayUtils.toObject(bodyBytes));
                }
            }
            //
            if (endFlag == 0x01) {
                //??1??packet
                //??
                ArrayList<Byte[]> getDataMapList = mProtocolDataUtils.getGetDataMap(sequenceId);

                if (getDataMapList != null) {
                    Byte[] myAllBytes = null;
                    for (Byte[] bytes : getDataMapList) {
                        myAllBytes = (Byte[]) ArrayUtils.addAll(myAllBytes, bytes);
                    }
                    //?
                    mProtocolDataUtils.removeGetDataMap(sequenceId);
                    //
                    onActionDataAvailable(myAllBytes);
                }
            }
        }
        //
    }

    /**
     * ?  writeCharacteristic
     */
    protected void initNotifyCharacteristic(BluetoothGattCharacteristic characteristic) {
        if (characteristic == null) {
            return;
        }
        mNotifyCharacteristic = characteristic;
        mBluetoothLeService.setCharacteristicNotification(mNotifyCharacteristic, true);
    }

    /**
     * ?  writeCharacteristic
     */
    protected void initWriteCharacteristic(BluetoothGattCharacteristic characteristic) {
        mWriteCharacteristic = characteristic;
    }

    private static class ProtocolDataUtils {
        //
        private Map<Short, PacketTools> mPacketToolsMap = new HashMap<>();
        //ack
        private Map<Short, Integer> mPacketToolsAckLengthMap = new HashMap<>();
        //Err
        private Map<Short, Boolean> mPacketToolsErrFlagMap = new HashMap<>();
        //?
        private Map<Short, Integer> mPacketToolsRetryMap = new HashMap<>();
        //?
        private Map<Short, ArrayList<Byte[]>> mGetDataMap = new HashMap<>();

        public void putPacketTools(short sequenceId, PacketTools packetTools) {
            mPacketToolsMap.put(sequenceId, packetTools);
        }

        public PacketTools getPacketTools(short sequenceId) {
            return mPacketToolsMap.get(sequenceId);
        }

        public void removePacketTools(short sequenceId) {
            mPacketToolsMap.put(sequenceId, null);
        }

        public void plusAckLength(short sequenceId) {
            Integer ackLength = mPacketToolsAckLengthMap.get(sequenceId);
            if (ackLength == null) {
                mPacketToolsAckLengthMap.put(sequenceId, 1);
            } else {
                mPacketToolsAckLengthMap.put(sequenceId, ackLength++);
            }
        }

        public int getAckLength(short sequenceId) {
            Integer ackLength = mPacketToolsAckLengthMap.get(sequenceId);
            if (ackLength == null) {
                return 0;
            }
            return ackLength;
        }

        public void removeAckLength(short sequenceId) {
            mPacketToolsAckLengthMap.put(sequenceId, null);
        }

        public void putErrFlag(short sequenceId, Boolean b) {
            mPacketToolsErrFlagMap.put(sequenceId, b);
        }

        public Boolean getErrFlag(short sequenceId) {
            return mPacketToolsErrFlagMap.get(sequenceId);

        }

        public void removeErrFlag(short sequenceId) {
            mPacketToolsErrFlagMap.put(sequenceId, null);
        }

        public void plusRetryMapLength(short sequenceId) {
            Integer ackLength = mPacketToolsRetryMap.get(sequenceId);
            if (ackLength == null) {
                mPacketToolsRetryMap.put(sequenceId, 1);
            } else {
                mPacketToolsRetryMap.put(sequenceId, ackLength++);
            }
        }

        public int getRetryMapLength(short sequenceId) {
            Integer length = mPacketToolsRetryMap.get(sequenceId);
            if (length == null) {
                return 0;
            }
            return length;
        }

        public void removeRetryMapLength(short sequenceId) {
            mPacketToolsRetryMap.put(sequenceId, null);
        }

        public void plusGetDataMap(short sequenceId, Byte[] bytes) {
            ArrayList<Byte[]> list = mGetDataMap.get(sequenceId);

            if (list == null) {
                list = new ArrayList<>();
            }
            //
            list.add(bytes);
            mGetDataMap.put(sequenceId, list);
        }

        public ArrayList<Byte[]> getGetDataMap(short sequenceId) {
            ArrayList<Byte[]> list = mGetDataMap.get(sequenceId);
            return list;
        }

        public void removeGetDataMap(short sequenceId) {
            mGetDataMap.put(sequenceId, null);
        }

    }
}