Home | History | Annotate | Download | only in gatt
      1 /*
      2  * Copyright (C) 2017 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.bluetooth.gatt;
     18 
     19 import android.bluetooth.le.AdvertiseData;
     20 import android.bluetooth.le.AdvertisingSetParameters;
     21 import android.bluetooth.le.IAdvertisingSetCallback;
     22 import android.bluetooth.le.PeriodicAdvertisingParameters;
     23 import android.os.Handler;
     24 import android.os.HandlerThread;
     25 import android.os.IBinder;
     26 import android.os.IInterface;
     27 import android.os.Looper;
     28 import android.os.RemoteException;
     29 import android.util.Log;
     30 
     31 import com.android.bluetooth.btservice.AdapterService;
     32 
     33 import java.util.Collections;
     34 import java.util.HashMap;
     35 import java.util.Map;
     36 
     37 /**
     38  * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests.
     39  *
     40  * @hide
     41  */
     42 class AdvertiseManager {
     43     private static final boolean DBG = GattServiceConfig.DBG;
     44     private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager";
     45 
     46     private final GattService mService;
     47     private final AdapterService mAdapterService;
     48     private Handler mHandler;
     49     Map<IBinder, AdvertiserInfo> mAdvertisers = Collections.synchronizedMap(new HashMap<>());
     50     static int sTempRegistrationId = -1;
     51 
     52     /**
     53      * Constructor of {@link AdvertiseManager}.
     54      */
     55     AdvertiseManager(GattService service, AdapterService adapterService) {
     56         if (DBG) {
     57             Log.d(TAG, "advertise manager created");
     58         }
     59         mService = service;
     60         mAdapterService = adapterService;
     61     }
     62 
     63     /**
     64      * Start a {@link HandlerThread} that handles advertising operations.
     65      */
     66     void start() {
     67         initializeNative();
     68         HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager");
     69         thread.start();
     70         mHandler = new Handler(thread.getLooper());
     71     }
     72 
     73     void cleanup() {
     74         if (DBG) {
     75             Log.d(TAG, "cleanup()");
     76         }
     77         cleanupNative();
     78         mAdvertisers.clear();
     79         sTempRegistrationId = -1;
     80 
     81         if (mHandler != null) {
     82             // Shut down the thread
     83             mHandler.removeCallbacksAndMessages(null);
     84             Looper looper = mHandler.getLooper();
     85             if (looper != null) {
     86                 looper.quit();
     87             }
     88             mHandler = null;
     89         }
     90     }
     91 
     92     class AdvertiserInfo {
     93         /* When id is negative, the registration is ongoing. When the registration finishes, id
     94          * becomes equal to advertiser_id */
     95         public Integer id;
     96         public AdvertisingSetDeathRecipient deathRecipient;
     97         public IAdvertisingSetCallback callback;
     98 
     99         AdvertiserInfo(Integer id, AdvertisingSetDeathRecipient deathRecipient,
    100                 IAdvertisingSetCallback callback) {
    101             this.id = id;
    102             this.deathRecipient = deathRecipient;
    103             this.callback = callback;
    104         }
    105     }
    106 
    107     IBinder toBinder(IAdvertisingSetCallback e) {
    108         return ((IInterface) e).asBinder();
    109     }
    110 
    111     class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient {
    112         public IAdvertisingSetCallback callback;
    113 
    114         AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback) {
    115             this.callback = callback;
    116         }
    117 
    118         @Override
    119         public void binderDied() {
    120             if (DBG) {
    121                 Log.d(TAG, "Binder is dead - unregistering advertising set");
    122             }
    123             stopAdvertisingSet(callback);
    124         }
    125     }
    126 
    127     Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) {
    128         Map.Entry<IBinder, AdvertiserInfo> entry = null;
    129         for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) {
    130             if (e.getValue().id == advertiserId) {
    131                 entry = e;
    132                 break;
    133             }
    134         }
    135         return entry;
    136     }
    137 
    138     void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
    139             throws Exception {
    140         if (DBG) {
    141             Log.d(TAG,
    142                     "onAdvertisingSetStarted() - regId=" + regId + ", advertiserId=" + advertiserId
    143                             + ", status=" + status);
    144         }
    145 
    146         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(regId);
    147 
    148         if (entry == null) {
    149             Log.i(TAG, "onAdvertisingSetStarted() - no callback found for regId " + regId);
    150             // Advertising set was stopped before it was properly registered.
    151             stopAdvertisingSetNative(advertiserId);
    152             return;
    153         }
    154 
    155         IAdvertisingSetCallback callback = entry.getValue().callback;
    156         if (status == 0) {
    157             entry.setValue(
    158                     new AdvertiserInfo(advertiserId, entry.getValue().deathRecipient, callback));
    159         } else {
    160             IBinder binder = entry.getKey();
    161             binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
    162             mAdvertisers.remove(binder);
    163         }
    164 
    165         callback.onAdvertisingSetStarted(advertiserId, txPower, status);
    166     }
    167 
    168     void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception {
    169         if (DBG) {
    170             Log.d(TAG, "onAdvertisingSetEnabled() - advertiserId=" + advertiserId + ", enable="
    171                     + enable + ", status=" + status);
    172         }
    173 
    174         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    175         if (entry == null) {
    176             Log.i(TAG, "onAdvertisingSetEnable() - no callback found for advertiserId "
    177                     + advertiserId);
    178             return;
    179         }
    180 
    181         IAdvertisingSetCallback callback = entry.getValue().callback;
    182         callback.onAdvertisingEnabled(advertiserId, enable, status);
    183     }
    184 
    185     void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData,
    186             AdvertiseData scanResponse, PeriodicAdvertisingParameters periodicParameters,
    187             AdvertiseData periodicData, int duration, int maxExtAdvEvents,
    188             IAdvertisingSetCallback callback) {
    189         AdvertisingSetDeathRecipient deathRecipient = new AdvertisingSetDeathRecipient(callback);
    190         IBinder binder = toBinder(callback);
    191         try {
    192             binder.linkToDeath(deathRecipient, 0);
    193         } catch (RemoteException e) {
    194             throw new IllegalArgumentException("Can't link to advertiser's death");
    195         }
    196 
    197         String deviceName = AdapterService.getAdapterService().getName();
    198         byte[] advDataBytes = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName);
    199         byte[] scanResponseBytes = AdvertiseHelper.advertiseDataToBytes(scanResponse, deviceName);
    200         byte[] periodicDataBytes = AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName);
    201 
    202         int cbId = --sTempRegistrationId;
    203         mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback));
    204 
    205         if (DBG) {
    206             Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
    207         }
    208         startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes, periodicParameters,
    209                 periodicDataBytes, duration, maxExtAdvEvents, cbId);
    210     }
    211 
    212     void onOwnAddressRead(int advertiserId, int addressType, String address)
    213             throws RemoteException {
    214         if (DBG) {
    215             Log.d(TAG, "onOwnAddressRead() advertiserId=" + advertiserId);
    216         }
    217 
    218         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    219         if (entry == null) {
    220             Log.i(TAG, "onOwnAddressRead() - bad advertiserId " + advertiserId);
    221             return;
    222         }
    223 
    224         IAdvertisingSetCallback callback = entry.getValue().callback;
    225         callback.onOwnAddressRead(advertiserId, addressType, address);
    226     }
    227 
    228     void getOwnAddress(int advertiserId) {
    229         getOwnAddressNative(advertiserId);
    230     }
    231 
    232     void stopAdvertisingSet(IAdvertisingSetCallback callback) {
    233         IBinder binder = toBinder(callback);
    234         if (DBG) {
    235             Log.d(TAG, "stopAdvertisingSet() " + binder);
    236         }
    237 
    238         AdvertiserInfo adv = mAdvertisers.remove(binder);
    239         if (adv == null) {
    240             Log.e(TAG, "stopAdvertisingSet() - no client found for callback");
    241             return;
    242         }
    243 
    244         Integer advertiserId = adv.id;
    245         binder.unlinkToDeath(adv.deathRecipient, 0);
    246 
    247         if (advertiserId < 0) {
    248             Log.i(TAG, "stopAdvertisingSet() - advertiser not finished registration yet");
    249             // Advertiser will be freed once initiated in onAdvertisingSetStarted()
    250             return;
    251         }
    252 
    253         stopAdvertisingSetNative(advertiserId);
    254 
    255         try {
    256             callback.onAdvertisingSetStopped(advertiserId);
    257         } catch (RemoteException e) {
    258             Log.i(TAG, "error sending onAdvertisingSetStopped callback", e);
    259         }
    260     }
    261 
    262     void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
    263         enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);
    264     }
    265 
    266     void setAdvertisingData(int advertiserId, AdvertiseData data) {
    267         String deviceName = AdapterService.getAdapterService().getName();
    268         setAdvertisingDataNative(advertiserId,
    269                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
    270     }
    271 
    272     void setScanResponseData(int advertiserId, AdvertiseData data) {
    273         String deviceName = AdapterService.getAdapterService().getName();
    274         setScanResponseDataNative(advertiserId,
    275                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
    276     }
    277 
    278     void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) {
    279         setAdvertisingParametersNative(advertiserId, parameters);
    280     }
    281 
    282     void setPeriodicAdvertisingParameters(int advertiserId,
    283             PeriodicAdvertisingParameters parameters) {
    284         setPeriodicAdvertisingParametersNative(advertiserId, parameters);
    285     }
    286 
    287     void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
    288         String deviceName = AdapterService.getAdapterService().getName();
    289         setPeriodicAdvertisingDataNative(advertiserId,
    290                 AdvertiseHelper.advertiseDataToBytes(data, deviceName));
    291     }
    292 
    293     void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) {
    294         setPeriodicAdvertisingEnableNative(advertiserId, enable);
    295     }
    296 
    297     void onAdvertisingDataSet(int advertiserId, int status) throws Exception {
    298         if (DBG) {
    299             Log.d(TAG,
    300                     "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status);
    301         }
    302 
    303         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    304         if (entry == null) {
    305             Log.i(TAG, "onAdvertisingDataSet() - bad advertiserId " + advertiserId);
    306             return;
    307         }
    308 
    309         IAdvertisingSetCallback callback = entry.getValue().callback;
    310         callback.onAdvertisingDataSet(advertiserId, status);
    311     }
    312 
    313     void onScanResponseDataSet(int advertiserId, int status) throws Exception {
    314         if (DBG) {
    315             Log.d(TAG,
    316                     "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status);
    317         }
    318 
    319         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    320         if (entry == null) {
    321             Log.i(TAG, "onScanResponseDataSet() - bad advertiserId " + advertiserId);
    322             return;
    323         }
    324 
    325         IAdvertisingSetCallback callback = entry.getValue().callback;
    326         callback.onScanResponseDataSet(advertiserId, status);
    327     }
    328 
    329     void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status)
    330             throws Exception {
    331         if (DBG) {
    332             Log.d(TAG,
    333                     "onAdvertisingParametersUpdated() advertiserId=" + advertiserId + ", txPower="
    334                             + txPower + ", status=" + status);
    335         }
    336 
    337         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    338         if (entry == null) {
    339             Log.i(TAG, "onAdvertisingParametersUpdated() - bad advertiserId " + advertiserId);
    340             return;
    341         }
    342 
    343         IAdvertisingSetCallback callback = entry.getValue().callback;
    344         callback.onAdvertisingParametersUpdated(advertiserId, txPower, status);
    345     }
    346 
    347     void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception {
    348         if (DBG) {
    349             Log.d(TAG, "onPeriodicAdvertisingParametersUpdated() advertiserId=" + advertiserId
    350                     + ", status=" + status);
    351         }
    352 
    353         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    354         if (entry == null) {
    355             Log.i(TAG,
    356                     "onPeriodicAdvertisingParametersUpdated() - bad advertiserId " + advertiserId);
    357             return;
    358         }
    359 
    360         IAdvertisingSetCallback callback = entry.getValue().callback;
    361         callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status);
    362     }
    363 
    364     void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception {
    365         if (DBG) {
    366             Log.d(TAG, "onPeriodicAdvertisingDataSet() advertiserId=" + advertiserId + ", status="
    367                     + status);
    368         }
    369 
    370         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    371         if (entry == null) {
    372             Log.i(TAG, "onPeriodicAdvertisingDataSet() - bad advertiserId " + advertiserId);
    373             return;
    374         }
    375 
    376         IAdvertisingSetCallback callback = entry.getValue().callback;
    377         callback.onPeriodicAdvertisingDataSet(advertiserId, status);
    378     }
    379 
    380     void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status)
    381             throws Exception {
    382         if (DBG) {
    383             Log.d(TAG, "onPeriodicAdvertisingEnabled() advertiserId=" + advertiserId + ", status="
    384                     + status);
    385         }
    386 
    387         Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId);
    388         if (entry == null) {
    389             Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId);
    390             return;
    391         }
    392 
    393         IAdvertisingSetCallback callback = entry.getValue().callback;
    394         callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
    395     }
    396 
    397     static {
    398         classInitNative();
    399     }
    400 
    401     private static native void classInitNative();
    402 
    403     private native void initializeNative();
    404 
    405     private native void cleanupNative();
    406 
    407     private native void startAdvertisingSetNative(AdvertisingSetParameters parameters,
    408             byte[] advertiseData, byte[] scanResponse,
    409             PeriodicAdvertisingParameters periodicParameters, byte[] periodicData, int duration,
    410             int maxExtAdvEvents, int regId);
    411 
    412     private native void getOwnAddressNative(int advertiserId);
    413 
    414     private native void stopAdvertisingSetNative(int advertiserId);
    415 
    416     private native void enableAdvertisingSetNative(int advertiserId, boolean enable, int duration,
    417             int maxExtAdvEvents);
    418 
    419     private native void setAdvertisingDataNative(int advertiserId, byte[] data);
    420 
    421     private native void setScanResponseDataNative(int advertiserId, byte[] data);
    422 
    423     private native void setAdvertisingParametersNative(int advertiserId,
    424             AdvertisingSetParameters parameters);
    425 
    426     private native void setPeriodicAdvertisingParametersNative(int advertiserId,
    427             PeriodicAdvertisingParameters parameters);
    428 
    429     private native void setPeriodicAdvertisingDataNative(int advertiserId, byte[] data);
    430 
    431     private native void setPeriodicAdvertisingEnableNative(int advertiserId, boolean enable);
    432 }
    433