| /* |
| * Copyright (C) 2017 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.googlecode.android_scripting.facade.bluetooth; |
| |
| import android.app.Service; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.le.AdvertiseCallback; |
| import android.bluetooth.le.AdvertiseData; |
| import android.bluetooth.le.AdvertiseData.Builder; |
| import android.bluetooth.le.AdvertiseSettings; |
| import android.bluetooth.le.BluetoothLeAdvertiser; |
| import android.os.Bundle; |
| import android.os.ParcelUuid; |
| |
| import com.googlecode.android_scripting.Log; |
| import com.googlecode.android_scripting.MainThread; |
| import com.googlecode.android_scripting.facade.EventFacade; |
| import com.googlecode.android_scripting.facade.FacadeManager; |
| import com.googlecode.android_scripting.jsonrpc.RpcReceiver; |
| import com.googlecode.android_scripting.rpc.Rpc; |
| import com.googlecode.android_scripting.rpc.RpcParameter; |
| |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.concurrent.Callable; |
| |
| /** |
| * BluetoothLe Advertise functions. |
| */ |
| |
| public class BluetoothLeAdvertiseFacade extends RpcReceiver { |
| |
| private final EventFacade mEventFacade; |
| private BluetoothAdapter mBluetoothAdapter; |
| private static int BleAdvertiseCallbackCount; |
| private static int BleAdvertiseSettingsCount; |
| private static int BleAdvertiseDataCount; |
| private final HashMap<Integer, MyAdvertiseCallback> mAdvertiseCallbackList; |
| private final BluetoothLeAdvertiser mAdvertise; |
| private final Service mService; |
| private Builder mAdvertiseDataBuilder; |
| private android.bluetooth.le.AdvertiseSettings.Builder mAdvertiseSettingsBuilder; |
| private final HashMap<Integer, AdvertiseData> mAdvertiseDataList; |
| private final HashMap<Integer, AdvertiseSettings> mAdvertiseSettingsList; |
| |
| public BluetoothLeAdvertiseFacade(FacadeManager manager) { |
| super(manager); |
| mService = manager.getService(); |
| mBluetoothAdapter = MainThread.run(mService, |
| new Callable<BluetoothAdapter>() { |
| @Override |
| public BluetoothAdapter call() throws Exception { |
| return BluetoothAdapter.getDefaultAdapter(); |
| } |
| }); |
| mEventFacade = manager.getReceiver(EventFacade.class); |
| mAdvertiseCallbackList = new HashMap<Integer, MyAdvertiseCallback>(); |
| mAdvertise = mBluetoothAdapter.getBluetoothLeAdvertiser(); |
| mAdvertiseDataList = new HashMap<Integer, AdvertiseData>(); |
| mAdvertiseSettingsList = new HashMap<Integer, AdvertiseSettings>(); |
| mAdvertiseDataBuilder = new Builder(); |
| mAdvertiseSettingsBuilder = |
| new android.bluetooth.le.AdvertiseSettings.Builder(); |
| } |
| |
| /** |
| * Constructs a MyAdvertiseCallback obj and returns its index |
| * |
| * @return MyAdvertiseCallback.index |
| */ |
| @Rpc(description = "Generate a new myAdvertisement Object") |
| public Integer bleGenBleAdvertiseCallback() { |
| BleAdvertiseCallbackCount += 1; |
| int index = BleAdvertiseCallbackCount; |
| MyAdvertiseCallback mCallback = new MyAdvertiseCallback(index); |
| mAdvertiseCallbackList.put(mCallback.index, |
| mCallback); |
| return mCallback.index; |
| } |
| |
| /** |
| * Constructs a AdvertiseData obj and returns its index |
| * |
| * @return index |
| */ |
| @Rpc(description = |
| "Constructs a new Builder obj for AdvertiseData and returns its index") |
| public Integer bleBuildAdvertiseData() { |
| BleAdvertiseDataCount += 1; |
| int index = BleAdvertiseDataCount; |
| mAdvertiseDataList.put(index, |
| mAdvertiseDataBuilder.build()); |
| mAdvertiseDataBuilder = new Builder(); |
| return index; |
| } |
| |
| /** |
| * Constructs a Advertise Settings obj and returns its index |
| * |
| * @return index |
| */ |
| @Rpc(description = |
| "Constructs a new Builder obj for AdvertiseData and returns its index") |
| public Integer bleBuildAdvertiseSettings() { |
| BleAdvertiseSettingsCount += 1; |
| int index = BleAdvertiseSettingsCount; |
| mAdvertiseSettingsList.put(index, |
| mAdvertiseSettingsBuilder.build()); |
| mAdvertiseSettingsBuilder = |
| new android.bluetooth.le.AdvertiseSettings.Builder(); |
| return index; |
| } |
| |
| /** |
| * Stops a ble advertisement |
| * |
| * @param index the id of the advertisement to stop advertising on |
| * @throws Exception |
| */ |
| @Rpc(description = "Stops an ongoing ble advertisement") |
| public void bleStopBleAdvertising( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseCallbackList.get(index) != null) { |
| Log.d("bluetooth_le mAdvertise " + index); |
| mAdvertise.stopAdvertising(mAdvertiseCallbackList |
| .get(index)); |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Starts ble advertising |
| * |
| * @param callbackIndex The advertisementCallback index |
| * @param dataIndex the AdvertiseData index |
| * @param settingsIndex the advertisementsettings index |
| * @throws Exception |
| */ |
| @Rpc(description = "Starts ble advertisement") |
| public void bleStartBleAdvertising( |
| @RpcParameter(name = "callbackIndex") |
| Integer callbackIndex, |
| @RpcParameter(name = "dataIndex") |
| Integer dataIndex, |
| @RpcParameter(name = "settingsIndex") |
| Integer settingsIndex |
| ) throws Exception { |
| AdvertiseData mData = new AdvertiseData.Builder().build(); |
| AdvertiseSettings mSettings = new AdvertiseSettings.Builder().build(); |
| if (mAdvertiseDataList.get(dataIndex) != null) { |
| mData = mAdvertiseDataList.get(dataIndex); |
| } else { |
| throw new Exception("Invalid dataIndex input:" |
| + Integer.toString(dataIndex)); |
| } |
| if (mAdvertiseSettingsList.get(settingsIndex) != null) { |
| mSettings = mAdvertiseSettingsList.get(settingsIndex); |
| } else { |
| throw new Exception("Invalid settingsIndex input:" |
| + Integer.toString(settingsIndex)); |
| } |
| if (mAdvertiseCallbackList.get(callbackIndex) != null) { |
| Log.d("bluetooth_le starting a background advertisement on callback index: " |
| + Integer.toString(callbackIndex)); |
| mAdvertise.startAdvertising( |
| mSettings, mData, mAdvertiseCallbackList.get(callbackIndex)); |
| } else { |
| throw new Exception("Invalid callbackIndex input" |
| + Integer.toString(callbackIndex)); |
| } |
| } |
| |
| /** |
| * Starts ble advertising with a scanResponse. ScanResponses are created in |
| * the same way |
| * AdvertiseData is created since they share the same object type. |
| * |
| * @param callbackIndex The advertisementCallback index |
| * @param dataIndex the AdvertiseData index |
| * @param settingsIndex the advertisementsettings index |
| * @param scanResponseIndex the scanResponse index |
| * @throws Exception |
| */ |
| @Rpc(description = "Starts ble advertisement") |
| public void bleStartBleAdvertisingWithScanResponse( |
| @RpcParameter(name = "callbackIndex") |
| Integer callbackIndex, |
| @RpcParameter(name = "dataIndex") |
| Integer dataIndex, |
| @RpcParameter(name = "settingsIndex") |
| Integer settingsIndex, |
| @RpcParameter(name = "scanResponseIndex") |
| Integer scanResponseIndex |
| ) throws Exception { |
| AdvertiseData mData = new AdvertiseData.Builder().build(); |
| AdvertiseSettings mSettings = new AdvertiseSettings.Builder().build(); |
| AdvertiseData mScanResponse = new AdvertiseData.Builder().build(); |
| |
| if (mAdvertiseDataList.get(dataIndex) != null) { |
| mData = mAdvertiseDataList.get(dataIndex); |
| } else { |
| throw new Exception("Invalid dataIndex input:" |
| + Integer.toString(dataIndex)); |
| } |
| if (mAdvertiseSettingsList.get(settingsIndex) != null) { |
| mSettings = mAdvertiseSettingsList.get(settingsIndex); |
| } else { |
| throw new Exception("Invalid settingsIndex input:" |
| + Integer.toString(settingsIndex)); |
| } |
| if (mAdvertiseDataList.get(scanResponseIndex) != null) { |
| mScanResponse = mAdvertiseDataList.get(scanResponseIndex); |
| } else { |
| throw new Exception("Invalid scanResponseIndex input:" |
| + Integer.toString(settingsIndex)); |
| } |
| if (mAdvertiseCallbackList.get(callbackIndex) != null) { |
| Log.d("bluetooth_le starting a background advertise on callback index: " |
| + Integer.toString(callbackIndex)); |
| mAdvertise |
| .startAdvertising(mSettings, mData, mScanResponse, |
| mAdvertiseCallbackList.get(callbackIndex)); |
| } else { |
| throw new Exception("Invalid callbackIndex input" |
| + Integer.toString(callbackIndex)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement settings mode |
| * |
| * @param index the advertise settings object to use |
| * @return the mode of the advertise settings object |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement settings mode") |
| public int bleGetAdvertiseSettingsMode( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseSettingsList.get(index) != null) { |
| AdvertiseSettings mSettings = mAdvertiseSettingsList.get(index); |
| return mSettings.getMode(); |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement settings tx power level |
| * |
| * @param index the advertise settings object to use |
| * @return the tx power level of the advertise settings object |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement settings tx power level") |
| public int bleGetAdvertiseSettingsTxPowerLevel( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseSettingsList.get(index) != null) { |
| AdvertiseSettings mSettings = mAdvertiseSettingsList.get(index); |
| return mSettings.getTxPowerLevel(); |
| } else { |
| throw new Exception("Invalid index input:" + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement settings isConnectable value |
| * |
| * @param index the advertise settings object to use |
| * @return the boolean value whether the advertisement will indicate |
| * connectable. |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement settings isConnectable value") |
| public boolean bleGetAdvertiseSettingsIsConnectable( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseSettingsList.get(index) != null) { |
| AdvertiseSettings mSettings = mAdvertiseSettingsList.get(index); |
| return mSettings.isConnectable(); |
| } else { |
| throw new Exception("Invalid index input:" + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement data include tx power level |
| * |
| * @param index the advertise data object to use |
| * @return True if include tx power level, false otherwise |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement data include tx power level") |
| public Boolean bleGetAdvertiseDataIncludeTxPowerLevel( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseDataList.get(index) != null) { |
| AdvertiseData mData = mAdvertiseDataList.get(index); |
| return mData.getIncludeTxPowerLevel(); |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement data manufacturer specific data |
| * |
| * @param index the advertise data object to use |
| * @param manufacturerId the id that corresponds to the manufacturer specific data. |
| * @return the corresponding manufacturer specific data to the manufacturer id. |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement data manufacturer specific data") |
| public byte[] bleGetAdvertiseDataManufacturerSpecificData( |
| @RpcParameter(name = "index") |
| Integer index, |
| @RpcParameter(name = "manufacturerId") |
| Integer manufacturerId) throws Exception { |
| if (mAdvertiseDataList.get(index) != null) { |
| AdvertiseData mData = mAdvertiseDataList.get(index); |
| if (mData.getManufacturerSpecificData() != null) { |
| return mData.getManufacturerSpecificData().get(manufacturerId); |
| } else { |
| throw new Exception("Invalid manufacturerId input:" |
| + Integer.toString(manufacturerId)); |
| } |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| |
| } |
| } |
| |
| /** |
| * Get ble advertisement data include device name |
| * |
| * @param index the advertise data object to use |
| * @return the advertisement data's include device name |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement include device name") |
| public Boolean bleGetAdvertiseDataIncludeDeviceName( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseDataList.get(index) != null) { |
| AdvertiseData mData = mAdvertiseDataList.get(index); |
| return mData.getIncludeDeviceName(); |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement Service Data |
| * |
| * @param index the advertise data object to use |
| * @param serviceUuid the uuid corresponding to the service data. |
| * @return the advertisement data's service data |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement Service Data") |
| public byte[] bleGetAdvertiseDataServiceData( |
| @RpcParameter(name = "index") |
| Integer index, |
| @RpcParameter(name = "serviceUuid") |
| String serviceUuid) throws Exception { |
| ParcelUuid uuidKey = ParcelUuid.fromString(serviceUuid); |
| if (mAdvertiseDataList.get(index) != null) { |
| AdvertiseData mData = mAdvertiseDataList.get(index); |
| if (mData.getServiceData().containsKey(uuidKey)) { |
| return mData.getServiceData().get(uuidKey); |
| } else { |
| throw new Exception("Invalid serviceUuid input:" + serviceUuid); |
| } |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Get ble advertisement Service Uuids |
| * |
| * @param index the advertise data object to use |
| * @return the advertisement data's Service Uuids |
| * @throws Exception |
| */ |
| @Rpc(description = "Get ble advertisement Service Uuids") |
| public List<ParcelUuid> bleGetAdvertiseDataServiceUuids( |
| @RpcParameter(name = "index") |
| Integer index) throws Exception { |
| if (mAdvertiseDataList.get(index) != null) { |
| AdvertiseData mData = mAdvertiseDataList.get(index); |
| return mData.getServiceUuids(); |
| } else { |
| throw new Exception("Invalid index input:" |
| + Integer.toString(index)); |
| } |
| } |
| |
| /** |
| * Set ble advertisement data service uuids |
| * |
| * @param uuidList |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertisement data service uuids") |
| public void bleSetAdvertiseDataSetServiceUuids( |
| @RpcParameter(name = "uuidList") |
| String[] uuidList |
| ) { |
| for (String uuid : uuidList) { |
| mAdvertiseDataBuilder.addServiceUuid(ParcelUuid.fromString(uuid)); |
| } |
| } |
| |
| /** |
| * Set ble advertise data service uuids |
| * |
| * @param serviceDataUuid |
| * @param serviceData |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertise data service uuids") |
| public void bleAddAdvertiseDataServiceData( |
| @RpcParameter(name = "serviceDataUuid") |
| String serviceDataUuid, |
| @RpcParameter(name = "serviceData") |
| byte[] serviceData |
| ) { |
| mAdvertiseDataBuilder.addServiceData( |
| ParcelUuid.fromString(serviceDataUuid), |
| serviceData); |
| } |
| |
| /** |
| * Set ble advertise data manufacturer id |
| * |
| * @param manufacturerId the manufacturer id to set |
| * @param manufacturerSpecificData the manufacturer specific data to set |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertise data manufacturerId") |
| public void bleAddAdvertiseDataManufacturerId( |
| @RpcParameter(name = "manufacturerId") |
| Integer manufacturerId, |
| @RpcParameter(name = "manufacturerSpecificData") |
| byte[] manufacturerSpecificData |
| ) { |
| mAdvertiseDataBuilder.addManufacturerData(manufacturerId, |
| manufacturerSpecificData); |
| } |
| |
| /** |
| * Set ble advertise settings advertise mode |
| * |
| * @param advertiseMode |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertise settings advertise mode") |
| public void bleSetAdvertiseSettingsAdvertiseMode( |
| @RpcParameter(name = "advertiseMode") |
| Integer advertiseMode |
| ) { |
| mAdvertiseSettingsBuilder.setAdvertiseMode(advertiseMode); |
| } |
| |
| /** |
| * Set ble advertise settings tx power level |
| * |
| * @param txPowerLevel the tx power level to set |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertise settings tx power level") |
| public void bleSetAdvertiseSettingsTxPowerLevel( |
| @RpcParameter(name = "txPowerLevel") |
| Integer txPowerLevel |
| ) { |
| mAdvertiseSettingsBuilder.setTxPowerLevel(txPowerLevel); |
| } |
| |
| /** |
| * Set ble advertise settings the isConnectable value |
| * |
| * @param type the isConnectable value |
| * @throws Exception |
| */ |
| @Rpc(description = "Set ble advertise settings isConnectable value") |
| public void bleSetAdvertiseSettingsIsConnectable( |
| @RpcParameter(name = "value") |
| Boolean value |
| ) { |
| mAdvertiseSettingsBuilder.setConnectable(value); |
| } |
| |
| /** |
| * Set ble advertisement data include tx power level |
| * |
| * @param includeTxPowerLevel boolean whether to include the tx |
| * power level or not in the advertisement |
| */ |
| @Rpc(description = "Set ble advertisement data include tx power level") |
| public void bleSetAdvertiseDataIncludeTxPowerLevel( |
| @RpcParameter(name = "includeTxPowerLevel") |
| Boolean includeTxPowerLevel |
| ) { |
| mAdvertiseDataBuilder.setIncludeTxPowerLevel(includeTxPowerLevel); |
| } |
| |
| /** |
| * Set ble advertisement settings set timeout |
| * |
| * @param timeoutSeconds Limit advertising to a given amount of time. |
| */ |
| @Rpc(description = "Set ble advertisement data include tx power level") |
| public void bleSetAdvertiseSettingsTimeout( |
| @RpcParameter(name = "timeoutSeconds") |
| Integer timeoutSeconds |
| ) { |
| mAdvertiseSettingsBuilder.setTimeout(timeoutSeconds); |
| } |
| |
| /** |
| * Set ble advertisement data include device name |
| * |
| * @param includeDeviceName boolean whether to include device name or |
| * not in the advertisement |
| */ |
| @Rpc(description = "Set ble advertisement data include device name") |
| public void bleSetAdvertiseDataIncludeDeviceName( |
| @RpcParameter(name = "includeDeviceName") |
| Boolean includeDeviceName |
| ) { |
| mAdvertiseDataBuilder.setIncludeDeviceName(includeDeviceName); |
| } |
| |
| private class MyAdvertiseCallback extends AdvertiseCallback { |
| public Integer index; |
| private final Bundle mResults; |
| String mEventType; |
| |
| public MyAdvertiseCallback(int idx) { |
| index = idx; |
| mEventType = "BleAdvertise"; |
| mResults = new Bundle(); |
| } |
| |
| @Override |
| public void onStartSuccess(AdvertiseSettings settingsInEffect) { |
| Log.d("bluetooth_le_advertisement onSuccess " + mEventType + " " |
| + index); |
| mResults.putString("Type", "onSuccess"); |
| mResults.putParcelable("SettingsInEffect", settingsInEffect); |
| mEventFacade.postEvent(mEventType + index |
| + "onSuccess", mResults.clone()); |
| mResults.clear(); |
| } |
| |
| @Override |
| public void onStartFailure(int errorCode) { |
| String errorString = "UNKNOWN_ERROR_CODE"; |
| if (errorCode |
| == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) { |
| errorString = "ADVERTISE_FAILED_ALREADY_STARTED"; |
| } else if (errorCode |
| == AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE) { |
| errorString = "ADVERTISE_FAILED_DATA_TOO_LARGE"; |
| } else if (errorCode |
| == AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED) { |
| errorString = "ADVERTISE_FAILED_FEATURE_UNSUPPORTED"; |
| } else if (errorCode |
| == AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR) { |
| errorString = "ADVERTISE_FAILED_INTERNAL_ERROR"; |
| } else if (errorCode |
| == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) { |
| errorString = "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS"; |
| } |
| Log.d("bluetooth_le_advertisement onFailure " + mEventType + " " |
| + index + " error " + errorString); |
| mResults.putString("Type", "onFailure"); |
| mResults.putInt("ErrorCode", errorCode); |
| mResults.putString("Error", errorString); |
| mEventFacade.postEvent(mEventType + index + "onFailure", |
| mResults.clone()); |
| mResults.clear(); |
| } |
| } |
| |
| /** |
| * Clear all advertise settings |
| * |
| * @param None |
| */ |
| @Rpc(description = "Clear all advertise settings") |
| public void bleAdvertiseClearAll() { |
| Log.d("bleAdvertiseClearAll: called"); |
| if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) { |
| for (MyAdvertiseCallback mAdvertise : mAdvertiseCallbackList |
| .values()) { |
| if (mAdvertise != null) { |
| try{ |
| mBluetoothAdapter.getBluetoothLeAdvertiser() |
| .stopAdvertising(mAdvertise); |
| } catch (NullPointerException e) { |
| Log.e("Failed to stop ble advertising.", e); |
| } |
| } |
| } |
| } |
| mAdvertiseCallbackList.clear(); |
| mAdvertiseSettingsList.clear(); |
| mAdvertiseDataList.clear(); |
| } |
| |
| @Override |
| public void shutdown() { |
| bleAdvertiseClearAll(); |
| } |
| } |