BLEのAdvertiseとScanを同時に行う


## はじめに
GitHubから取得したコード（Advertiseのみ）に追記して（Scan）、
AdvertiseとScanを同時に実現
切り替える必要はない

## 環境
Zenfone2laser(Android 5.0.2)で動作確認済み
AdvertiseとScanを同時に行えることを実証
Nexus5（受信用、送信不可）
Zenfone2 laser（送受信用）

## 確認方法
送信の確認
Zenfone2 laserでNexus5に送信
受信の確認
Aplix BeaconでNexus5,Zenfon2 laserに送信

## reference
メイン機能は以下で取得
・https://github.com/youten/aBeacon
スキャン機能の追加
・http://www.gaprot.jp/pickup/ibeacon/abeacon/

```Ruby:MainActivity.java
/*
 * Copyright (C) 2014 youten
 *
 * 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 youten.redo.ble.abeacon;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Intent;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import java.util.UUID;

import youten.redo.ble.util.BleUtil;

public class MainActivity extends Activity {
    private static final String TAG = "aBeacon";
    private static final int REQUEST_ENABLE_BT = 1;
    // BT
    private BluetoothAdapter mBTAdapter;
    private BluetoothLeAdvertiser mBTAdvertiser;
    private BluetoothGattServer mGattServer;
    // View
    private Button mIBeaconButton;
    private Button mIBeaconScanButton;
    private Button mIASButton;
    private Button mStopButton;
    private Button mStopScanButton;

    private TextView tv;

    private long count=0;

    private AdvertiseCallback mAdvCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(android.bluetooth.le.AdvertiseSettings settingsInEffect) {
            // Advする際に設定した値と実際に動作させることに成功したSettingsが違うとsettingsInEffectに
            // 有効な値が格納される模様です。設定通りに動かすことに成功した際にはnullが返る模様。
            if (settingsInEffect != null) {
                Log.d(TAG, "onStartSuccess TxPowerLv="
                        + settingsInEffect.getTxPowerLevel()
                        + " mode=" + settingsInEffect.getMode()
                        + " timeout=" + settingsInEffect.getTimeout());
            } else {
                Log.d(TAG, "onStartSuccess, settingInEffect is null");
            }
            mIBeaconButton.setEnabled(false);
            mIASButton.setEnabled(false);
            mIBeaconScanButton.setEnabled(true);    // addition for scan
            mStopButton.setEnabled(true);
            mStopScanButton.setEnabled(false);      // addition for scan
            setProgressBarIndeterminateVisibility(false);
        }
        @Override
        public void onStartFailure(int errorCode) {
            Log.d(TAG, "onStartFailure errorCode=" + errorCode);
        };
    };

    private BluetoothAdapter.LeScanCallback mLeScanCallback=new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            ParcelUuid[] uuids = device.getUuids();
            String uuid = "";
            if (uuids != null) {
                for (ParcelUuid puuid : uuids) {
                    uuid += puuid.toString() + " ";
                }
            } else {
                Log.d(TAG, "uuid null");
            }
            String msg = "name=" + device.getName() + ", bondStatus=" + device.getBondState()
                    + ", address=" + device.getAddress() + ", type=" + device.getType() + ", uuids=" + uuid;
            Log.d("BLEActivity", msg);

            //scanrecord to uuid
            if (scanRecord.length > 30) {
                if ((scanRecord[5] == (byte) 0x4c) && (scanRecord[6] == (byte) 0x00) && (scanRecord[7] == (byte) 0x02) && (scanRecord[8] == (byte) 0x15)) {
                    String uuid2 = IntToHex2(scanRecord[9] & 0xff) + IntToHex2(scanRecord[10] & 0xff) + IntToHex2(scanRecord[11] & 0xff) + IntToHex2(scanRecord[12] & 0xff)
                            + "-" + IntToHex2(scanRecord[13] & 0xff) + IntToHex2(scanRecord[14] & 0xff)
                            + "-" + IntToHex2(scanRecord[15] & 0xff) + IntToHex2(scanRecord[16] & 0xff)
                            + "-" + IntToHex2(scanRecord[17] & 0xff) + IntToHex2(scanRecord[18] & 0xff)
                            + "-" + IntToHex2(scanRecord[19] & 0xff) + IntToHex2(scanRecord[20] & 0xff) + IntToHex2(scanRecord[21] & 0xff) + IntToHex2(scanRecord[22] & 0xff) + IntToHex2(scanRecord[23] & 0xff) + IntToHex2(scanRecord[24] & 0xff);

                    String major2 = IntToHex2(scanRecord[25] & 0xff) + IntToHex2(scanRecord[26] & 0xff);
                    String minor2 = IntToHex2(scanRecord[27] & 0xff) + IntToHex2(scanRecord[28] & 0xff);

                    String uuid_text="["+(count++)+"] uuid:" + uuid2 + ",major:" + major2 + ",minor:" + minor2;
                    Log.d(TAG, uuid_text);
                    tv.setText(uuid_text);
                }
            }

        }
        public String IntToHex2(int i) {
            char hex_2[] = {Character.forDigit((i >> 4) & 0x0f, 16), Character.forDigit(i & 0x0f, 16)};
            String hex_2_str = new String(hex_2);
            return hex_2_str.toUpperCase();

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG,"onCreate");
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.activity_main);

        init();
    }

    @Override
    protected void onStop() {
        Log.d(TAG,"onStop");
        super.onStop();
        stopAdvertise();
    }

    private void init() {
        Log.d(TAG,"init");
        tv=(TextView)findViewById(R.id.textview);

        // BLE check
        if (!BleUtil.isBLESupported(this)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        // BT check
        BluetoothManager manager = BleUtil.getManager(this);
        if (manager != null) {
            mBTAdapter = manager.getAdapter();
        }
        if ((mBTAdapter == null) || (!mBTAdapter.isEnabled())) {
            Toast.makeText(this, R.string.bt_unavailable, Toast.LENGTH_SHORT).show();
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }


        mIBeaconButton = (Button) findViewById(R.id.ibeacon_button);
        mIBeaconButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startIBeaconAdvertise();
            }
        });

        // addition for scan
        mIBeaconScanButton = (Button) findViewById(R.id.ibeacon_scan_button);
        mIBeaconScanButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startIBeaconScan();
            }
        });

        mIASButton = (Button) findViewById(R.id.ias_button);
        mIASButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                startIASAdvertise();
            }
        });
        mStopButton = (Button) findViewById(R.id.stop_button);
        mStopButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                stopAdvertise();
            }
        });

        // addition for scan
        mStopScanButton = (Button) findViewById(R.id.stop_scan_button);
        mStopScanButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                stopScan();
            }
        });
    }

    // start Advertise as iBeacon
    private void startIBeaconAdvertise() {
        if (mBTAdapter == null) {
            return;
        }
        if (mBTAdvertiser == null) {
            mBTAdvertiser = mBTAdapter.getBluetoothLeAdvertiser();
        }
        if (mBTAdvertiser != null) {
            mBTAdvertiser.startAdvertising(
                    BleUtil.createAdvSettings(true, 0),
                    BleUtil.createIBeaconAdvertiseData(
                            UUID.fromString("01020304-0506-0708-1112-131415161718"),
                            (short) 257, (short) 514, (byte) 0xc5),
                    mAdvCallback);
        }
        startIBeaconScan();
    }

    // addition for scan
    private void startIBeaconScan(){
        //stopAdvertise();
        mBTAdapter.startLeScan(mLeScanCallback);
        mIBeaconButton.setEnabled(true);
        mIBeaconScanButton.setEnabled(true);
        mIASButton.setEnabled(false);
        mStopButton.setEnabled(false);
        mStopScanButton.setEnabled(true);      // addition for scan
    }

    // addition for scan
    private void stopScan(){
        mBTAdapter.stopLeScan(mLeScanCallback);

        tv.setText("");

        mIBeaconButton.setEnabled(true);
        mIASButton.setEnabled(true);
        mIBeaconScanButton.setEnabled(true);    // addition for scan
        mStopButton.setEnabled(false);
        mStopScanButton.setEnabled(false);      // addition for scan
        setProgressBarIndeterminateVisibility(false);
    }

    // start Advertise as Immediate Alert Service
    private void startIASAdvertise() {
        if (mBTAdapter == null) {
            return;
        }
        if (mBTAdvertiser == null) {
            mBTAdvertiser = mBTAdapter.getBluetoothLeAdvertiser();
        }
        if (mBTAdvertiser != null) {
            ImmediateAlertService ias = new ImmediateAlertService();
            mGattServer = BleUtil.getManager(this).openGattServer(this, ias);
            ias.setupServices(mGattServer);
            
            mBTAdvertiser.startAdvertising(
                    BleUtil.createAdvSettings(true, 0),
                    BleUtil.createFMPAdvertiseData(),
                    mAdvCallback);
        }
    }

    private void stopAdvertise() {
        if (mGattServer != null) {
            mGattServer.clearServices();
            mGattServer.close();
            mGattServer = null;
        }
        if (mBTAdvertiser != null) {
            mBTAdvertiser.stopAdvertising(mAdvCallback);
            mBTAdvertiser = null;
        }
        mIBeaconButton.setEnabled(true);
        mIASButton.setEnabled(true);
        mIBeaconScanButton.setEnabled(true);    // addition for scan
        mStopButton.setEnabled(false);
        mStopScanButton.setEnabled(false);      // addition for scan
        setProgressBarIndeterminateVisibility(false);

        // addtion
        stopScan();
    }

}
```
```Ruby:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:orientation="vertical"
    tools:context="${relativePackage}.${activityClass}" >
    <Button
        android:id="@+id/ibeacon_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/label_ibeacon_button" />
    <Button
        android:id="@+id/ibeacon_scan_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start scan" />
    <Button
        android:id="@+id/ias_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/label_ias_button" />
    <Button
        android:id="@+id/stop_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="@string/label_stop_button" />
    <Button
        android:id="@+id/stop_scan_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:enabled="false"
        android:text="stop scan" />
    <TextView
        android:id="@+id/textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="scan id"/>
</LinearLayout>
```