Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.bluetooth;
     18 
     19 import java.util.UUID;
     20 import java.util.ArrayList;
     21 import java.util.HashMap;
     22 import java.util.List;
     23 import java.util.Map;
     24 
     25 import android.app.Service;
     26 import android.bluetooth.BluetoothAdapter;
     27 import android.bluetooth.BluetoothGattServer;
     28 import android.bluetooth.BluetoothGattServerCallback;
     29 import android.bluetooth.BluetoothManager;
     30 import android.bluetooth.le.BluetoothLeAdvertiser;
     31 import android.bluetooth.le.AdvertiseCallback;
     32 import android.bluetooth.le.AdvertiseData;
     33 import android.bluetooth.le.AdvertiseSettings;
     34 import android.content.Context;
     35 import android.content.Intent;
     36 import android.os.Handler;
     37 import android.os.IBinder;
     38 import android.os.ParcelUuid;
     39 import android.util.Log;
     40 import android.widget.Toast;
     41 
     42 public class BleAdvertiserService extends Service {
     43 
     44     public static final boolean DEBUG = true;
     45     public static final String TAG = "BleAdvertiserService";
     46 
     47     public static final int COMMAND_START_ADVERTISE = 0;
     48     public static final int COMMAND_STOP_ADVERTISE = 1;
     49     public static final int COMMAND_START_POWER_LEVEL = 2;
     50     public static final int COMMAND_STOP_POWER_LEVEL = 3;
     51     public static final int COMMAND_START_SCANNABLE = 4;
     52     public static final int COMMAND_STOP_SCANNABLE = 5;
     53     public static final int COMMAND_START_UNSCANNABLE = 6;
     54     public static final int COMMAND_STOP_UNSCANNABLE = 7;
     55 
     56     public static final String BLE_ADV_NOT_SUPPORT =
     57             "com.android.cts.verifier.bluetooth.BLE_ADV_NOT_SUPPORT";
     58     public static final String BLE_START_ADVERTISE =
     59             "com.android.cts.verifier.bluetooth.BLE_START_ADVERTISE";
     60     public static final String BLE_STOP_ADVERTISE =
     61             "com.android.cts.verifier.bluetooth.BLE_STOP_ADVERTISE";
     62     public static final String BLE_START_POWER_LEVEL =
     63             "com.android.cts.verifier.bluetooth.BLE_START_POWER_LEVEL";
     64     public static final String BLE_STOP_POWER_LEVEL =
     65             "com.android.cts.verifier.bluetooth.BLE_STOP_POWER_LEVEL";
     66     public static final String BLE_START_SCANNABLE =
     67             "com.android.cts.verifier.bluetooth.BLE_START_SCANNABLE";
     68     public static final String BLE_START_UNSCANNABLE =
     69             "com.android.cts.verifier.bluetooth.BLE_START_UNSCANNABLE";
     70     public static final String BLE_STOP_SCANNABLE =
     71             "com.android.cts.verifier.bluetooth.BLE_STOP_SCANNABLE";
     72     public static final String BLE_STOP_UNSCANNABLE =
     73             "com.android.cts.verifier.bluetooth.BLE_STOP_UNSCANNABLE";
     74 
     75     public static final String EXTRA_COMMAND =
     76             "com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
     77 
     78     protected static final UUID PRIVACY_MAC_UUID =
     79             UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
     80     protected static final UUID POWER_LEVEL_UUID =
     81             UUID.fromString("00008888-0000-1000-8000-00805f9b34fb");
     82     protected static final UUID SCAN_RESP_UUID =
     83             UUID.fromString("00007777-0000-1000-8000-00805f9b34fb");
     84     protected static final UUID SCANNABLE_UUID =
     85             UUID.fromString("00006666-0000-1000-8000-00805f9b34fb");
     86     protected static final UUID UNSCANNABLE_UUID =
     87             UUID.fromString("00005555-0000-1000-8000-00805f9b34fb");
     88 
     89     public static final byte MANUFACTURER_TEST_ID = (byte)0x07;
     90     public static final byte[] PRIVACY_MAC_DATA = new byte[]{3, 1, 4};
     91     public static final byte[] PRIVACY_RESPONSE = new byte[]{9, 2, 6};
     92     public static final byte[] POWER_LEVEL_DATA = new byte[]{1, 5, 0, 0, 0,
     93         0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // 15 bytes
     94     public static final byte[] POWER_LEVEL_MASK = new byte[]{1, 1, 0, 0, 0,
     95         0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // 15 bytes
     96     public static final int POWER_LEVEL_DATA_LENGTH = 15;
     97     public static final byte[] SCANNABLE_DATA = new byte[]{5, 3, 5};
     98     public static final byte[] UNSCANNABLE_DATA = new byte[]{8, 9, 7};
     99 
    100     private BluetoothManager mBluetoothManager;
    101     private BluetoothAdapter mBluetoothAdapter;
    102     private BluetoothLeAdvertiser mAdvertiser;
    103     private BluetoothGattServer mGattServer;
    104     private AdvertiseCallback mCallback;
    105     private Handler mHandler;
    106 
    107     private int[] mPowerLevel;
    108     private Map<Integer, AdvertiseCallback> mPowerCallback;
    109     private int mAdvertiserStatus;
    110 
    111     private AdvertiseCallback mScannableCallback;
    112     private AdvertiseCallback mUnscannableCallback;
    113 
    114     @Override
    115     public void onCreate() {
    116         super.onCreate();
    117 
    118         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    119         mBluetoothAdapter = mBluetoothManager.getAdapter();
    120         mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
    121         mGattServer = mBluetoothManager.openGattServer(getApplicationContext(),
    122             new BluetoothGattServerCallback() {});
    123         mHandler = new Handler();
    124         mAdvertiserStatus = 0;
    125 
    126         mCallback = new BLEAdvertiseCallback();
    127         mScannableCallback = new BLEAdvertiseCallback();
    128         mUnscannableCallback = new BLEAdvertiseCallback();
    129         mPowerLevel = new int[]{
    130             AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
    131             AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
    132             AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
    133             AdvertiseSettings.ADVERTISE_TX_POWER_HIGH};
    134         mPowerCallback = new HashMap<Integer, AdvertiseCallback>();
    135         for (int x : mPowerLevel) {
    136             mPowerCallback.put(x, new BLEAdvertiseCallback());
    137         }
    138     }
    139 
    140     @Override
    141     public int onStartCommand(Intent intent, int flags, int startId) {
    142         if (intent != null) handleIntent(intent);
    143         return START_NOT_STICKY;
    144     }
    145 
    146     @Override
    147     public IBinder onBind(Intent intent) {
    148         return null;
    149     }
    150 
    151     @Override
    152     public void onDestroy() {
    153         super.onDestroy();
    154         if (mAdvertiser != null) {
    155             stopAdvertiser();
    156         }
    157     }
    158 
    159     private void stopAdvertiser() {
    160         if (mAdvertiser == null) {
    161             mAdvertiserStatus = 0;
    162             return;
    163         }
    164         if ((mAdvertiserStatus & (1 << COMMAND_START_ADVERTISE)) > 0) {
    165             mAdvertiser.stopAdvertising(mCallback);
    166         }
    167         if ((mAdvertiserStatus & (1 << COMMAND_START_POWER_LEVEL)) > 0) {
    168             for (int t : mPowerLevel) {
    169                 mAdvertiser.stopAdvertising(mPowerCallback.get(t));
    170             }
    171         }
    172         if ((mAdvertiserStatus & (1 << COMMAND_START_SCANNABLE)) > 0) {
    173             mAdvertiser.stopAdvertising(mScannableCallback);
    174         }
    175         if ((mAdvertiserStatus & (1 << COMMAND_START_UNSCANNABLE)) > 0) {
    176             mAdvertiser.stopAdvertising(mUnscannableCallback);
    177         }
    178         mAdvertiserStatus = 0;
    179     }
    180 
    181     private AdvertiseData generateAdvertiseData(UUID uuid, byte[] data) {
    182         return new AdvertiseData.Builder()
    183             .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
    184             .addServiceData(new ParcelUuid(uuid), data)
    185             .setIncludeTxPowerLevel(true)
    186             .build();
    187     }
    188 
    189     private AdvertiseSettings generateSetting(int power) {
    190         return new AdvertiseSettings.Builder()
    191             .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
    192             .setTxPowerLevel(power)
    193             .setConnectable(false)
    194             .build();
    195     }
    196 
    197     private void handleIntent(Intent intent) {
    198         if (mBluetoothAdapter != null && !mBluetoothAdapter.isMultipleAdvertisementSupported()) {
    199             showMessage("Multiple advertisement is not supported.");
    200             sendBroadcast(new Intent(BLE_ADV_NOT_SUPPORT));
    201             return;
    202         } else if (mAdvertiser == null) {
    203             showMessage("Cannot start advertising on this device.");
    204             return;
    205         }
    206         int command = intent.getIntExtra(EXTRA_COMMAND, -1);
    207         if (command >= 0) {
    208             stopAdvertiser();
    209             mAdvertiserStatus |= (1 << command);
    210         }
    211 
    212         switch (command) {
    213             case COMMAND_START_ADVERTISE:
    214                 AdvertiseData data = generateAdvertiseData(PRIVACY_MAC_UUID, PRIVACY_MAC_DATA);
    215                 AdvertiseData response = generateAdvertiseData(SCAN_RESP_UUID, PRIVACY_RESPONSE);
    216                 AdvertiseSettings setting =
    217                         generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
    218 
    219                 mAdvertiser.startAdvertising(setting, data, response, mCallback);
    220                 sendBroadcast(new Intent(BLE_START_ADVERTISE));
    221                 break;
    222             case COMMAND_STOP_ADVERTISE:
    223                 sendBroadcast(new Intent(BLE_STOP_ADVERTISE));
    224                 break;
    225             case COMMAND_START_POWER_LEVEL:
    226                 for (int t : mPowerLevel) {
    227                     // Service data:
    228                     //    field overhead = 2 bytes
    229                     //    uuid = 2 bytes
    230                     //    data = 15 bytes
    231                     // Manufacturer data:
    232                     //    field overhead = 2 bytes
    233                     //    Specific data length = 2 bytes
    234                     //    data length = 2 bytes
    235                     // Include power level:
    236                     //    field overhead = 2 bytes
    237                     //    1 byte
    238                     // Connectable flag: 3 bytes (0 byte for Android 5.1+)
    239                     // SUM = 31 bytes
    240                     byte[] dataBytes = new byte[POWER_LEVEL_DATA_LENGTH];
    241                     dataBytes[0] = 0x01;
    242                     dataBytes[1] = 0x05;
    243                     for (int i = 2; i < POWER_LEVEL_DATA_LENGTH; i++) {
    244                         dataBytes[i] = (byte)t;
    245                     }
    246                     AdvertiseData d = generateAdvertiseData(POWER_LEVEL_UUID, dataBytes);
    247                     AdvertiseSettings settings = generateSetting(t);
    248                     mAdvertiser.startAdvertising(settings, d, mPowerCallback.get(t));
    249                 }
    250                 sendBroadcast(new Intent(BLE_START_POWER_LEVEL));
    251                 break;
    252             case COMMAND_STOP_POWER_LEVEL:
    253                 sendBroadcast(new Intent(BLE_STOP_POWER_LEVEL));
    254                 break;
    255             case COMMAND_START_SCANNABLE:
    256                 data = generateAdvertiseData(SCANNABLE_UUID, SCANNABLE_DATA);
    257                 setting = generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
    258 
    259                 mAdvertiser.startAdvertising(setting, data, mScannableCallback);
    260                 sendBroadcast(new Intent(BLE_START_SCANNABLE));
    261                 break;
    262             case COMMAND_START_UNSCANNABLE:
    263                 data = generateAdvertiseData(UNSCANNABLE_UUID, UNSCANNABLE_DATA);
    264                 setting = generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
    265 
    266                 mAdvertiser.startAdvertising(setting, data, mUnscannableCallback);
    267                 sendBroadcast(new Intent(BLE_START_UNSCANNABLE));
    268                 break;
    269             case COMMAND_STOP_SCANNABLE:
    270                 sendBroadcast(new Intent(BLE_STOP_SCANNABLE));
    271                 break;
    272             case COMMAND_STOP_UNSCANNABLE:
    273                 sendBroadcast(new Intent(BLE_STOP_UNSCANNABLE));
    274                 break;
    275             default:
    276                 showMessage("Unrecognized command: " + command);
    277                 break;
    278         }
    279     }
    280 
    281     private void showMessage(final String msg) {
    282         mHandler.post(new Runnable() {
    283             public void run() {
    284                 Toast.makeText(BleAdvertiserService.this, msg, Toast.LENGTH_SHORT).show();
    285             }
    286         });
    287     }
    288 
    289     private class BLEAdvertiseCallback extends AdvertiseCallback {
    290         @Override
    291         public void onStartFailure(int errorCode) {
    292             Log.e(TAG, "fail. Error code: " + errorCode);
    293         }
    294 
    295         @Override
    296         public void onStartSuccess(AdvertiseSettings setting) {
    297             if (DEBUG) Log.d(TAG, "success.");
    298         }
    299     }
    300 }
    301