• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IPeriodicAdvertisingCallback;
21 import android.bluetooth.le.PeriodicAdvertisingReport;
22 import android.bluetooth.le.ScanRecord;
23 import android.bluetooth.le.ScanResult;
24 import android.os.IBinder;
25 import android.os.IInterface;
26 import android.os.RemoteException;
27 import android.util.Log;
28 import com.android.bluetooth.Utils;
29 import com.android.bluetooth.btservice.AdapterService;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.Map;
34 import java.util.Set;
35 import java.util.UUID;
36 import java.util.concurrent.CountDownLatch;
37 import java.util.concurrent.TimeUnit;
38 
39 /**
40  * Manages Bluetooth LE Periodic scans
41  *
42  * @hide
43  */
44 class PeriodicScanManager {
45     private static final boolean DBG = GattServiceConfig.DBG;
46     private static final String TAG = GattServiceConfig.TAG_PREFIX + "SyncManager";
47 
48     private final AdapterService mAdapterService;
49     Map<IBinder, SyncInfo> mSyncs = Collections.synchronizedMap(new HashMap<>());
50     static int sTempRegistrationId = -1;
51 
52     /**
53      * Constructor of {@link SyncManager}.
54      */
PeriodicScanManager(AdapterService adapterService)55     PeriodicScanManager(AdapterService adapterService) {
56         if (DBG) Log.d(TAG, "advertise manager created");
57         mAdapterService = adapterService;
58     }
59 
start()60     void start() {
61         initializeNative();
62     }
63 
cleanup()64     void cleanup() {
65         if (DBG) Log.d(TAG, "cleanup()");
66         cleanupNative();
67         mSyncs.clear();
68         sTempRegistrationId = -1;
69     }
70 
71     class SyncInfo {
72         /* When id is negative, the registration is ongoing. When the registration finishes, id
73          * becomes equal to sync_handle */
74         public Integer id;
75         public SyncDeathRecipient deathRecipient;
76         public IPeriodicAdvertisingCallback callback;
77 
SyncInfo(Integer id, SyncDeathRecipient deathRecipient, IPeriodicAdvertisingCallback callback)78         SyncInfo(Integer id, SyncDeathRecipient deathRecipient,
79                 IPeriodicAdvertisingCallback callback) {
80             this.id = id;
81             this.deathRecipient = deathRecipient;
82             this.callback = callback;
83         }
84     }
85 
toBinder(IPeriodicAdvertisingCallback e)86     IBinder toBinder(IPeriodicAdvertisingCallback e) {
87         return ((IInterface) e).asBinder();
88     }
89 
90     class SyncDeathRecipient implements IBinder.DeathRecipient {
91         IPeriodicAdvertisingCallback callback;
92 
SyncDeathRecipient(IPeriodicAdvertisingCallback callback)93         public SyncDeathRecipient(IPeriodicAdvertisingCallback callback) {
94             this.callback = callback;
95         }
96 
97         @Override
binderDied()98         public void binderDied() {
99             if (DBG) Log.d(TAG, "Binder is dead - unregistering advertising set");
100             stopSync(callback);
101         }
102     }
103 
findSync(int sync_handle)104     Map.Entry<IBinder, SyncInfo> findSync(int sync_handle) {
105         Map.Entry<IBinder, SyncInfo> entry = null;
106         for (Map.Entry<IBinder, SyncInfo> e : mSyncs.entrySet()) {
107             if (e.getValue().id == sync_handle) {
108                 entry = e;
109                 break;
110             }
111         }
112         return entry;
113     }
114 
onSyncStarted(int reg_id, int sync_handle, int sid, int address_type, String address, int phy, int interval, int status)115     void onSyncStarted(int reg_id, int sync_handle, int sid, int address_type, String address,
116             int phy, int interval, int status) throws Exception {
117         if (DBG) {
118             Log.d(TAG, "onSyncStarted() - reg_id=" + reg_id + ", sync_handle=" + sync_handle
119                             + ", status=" + status);
120         }
121 
122         Map.Entry<IBinder, SyncInfo> entry = findSync(reg_id);
123         if (entry == null) {
124             Log.i(TAG, "onSyncStarted() - no callback found for reg_id " + reg_id);
125             // Sync was stopped before it was properly registered.
126             stopSyncNative(sync_handle);
127             return;
128         }
129 
130         IPeriodicAdvertisingCallback callback = entry.getValue().callback;
131         if (status == 0) {
132             entry.setValue(new SyncInfo(sync_handle, entry.getValue().deathRecipient, callback));
133         } else {
134             IBinder binder = entry.getKey();
135             binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
136             mSyncs.remove(binder);
137         }
138 
139         // TODO: fix callback arguments
140         // callback.onSyncStarted(sync_handle, tx_power, status);
141     }
142 
onSyncReport(int sync_handle, int tx_power, int rssi, int data_status, byte[] data)143     void onSyncReport(int sync_handle, int tx_power, int rssi, int data_status, byte[] data)
144             throws Exception {
145         if (DBG) Log.d(TAG, "onSyncReport() - sync_handle=" + sync_handle);
146 
147         Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle);
148         if (entry == null) {
149             Log.i(TAG, "onSyncReport() - no callback found for sync_handle " + sync_handle);
150             return;
151         }
152 
153         IPeriodicAdvertisingCallback callback = entry.getValue().callback;
154         PeriodicAdvertisingReport report = new PeriodicAdvertisingReport(
155                 sync_handle, tx_power, rssi, data_status, ScanRecord.parseFromBytes(data));
156         callback.onPeriodicAdvertisingReport(report);
157     }
158 
onSyncLost(int sync_handle)159     void onSyncLost(int sync_handle) throws Exception {
160         if (DBG) Log.d(TAG, "onSyncLost() - sync_handle=" + sync_handle);
161 
162         Map.Entry<IBinder, SyncInfo> entry = findSync(sync_handle);
163         if (entry == null) {
164             Log.i(TAG, "onSyncLost() - no callback found for sync_handle " + sync_handle);
165             return;
166         }
167 
168         IPeriodicAdvertisingCallback callback = entry.getValue().callback;
169         mSyncs.remove(entry);
170         callback.onSyncLost(sync_handle);
171     }
172 
startSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback)173     void startSync(
174             ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback) {
175         SyncDeathRecipient deathRecipient = new SyncDeathRecipient(callback);
176         IBinder binder = toBinder(callback);
177         try {
178             binder.linkToDeath(deathRecipient, 0);
179         } catch (RemoteException e) {
180             throw new IllegalArgumentException("Can't link to periodic scanner death");
181         }
182 
183         String address = scanResult.getDevice().getAddress();
184         int sid = scanResult.getAdvertisingSid();
185 
186         int cb_id = --sTempRegistrationId;
187         mSyncs.put(binder, new SyncInfo(cb_id, deathRecipient, callback));
188 
189         if (DBG) Log.d(TAG, "startSync() - reg_id=" + cb_id + ", callback: " + binder);
190         startSyncNative(sid, address, skip, timeout, cb_id);
191     }
192 
stopSync(IPeriodicAdvertisingCallback callback)193     void stopSync(IPeriodicAdvertisingCallback callback) {
194         IBinder binder = toBinder(callback);
195         if (DBG) Log.d(TAG, "stopSync() " + binder);
196 
197         SyncInfo sync = mSyncs.remove(binder);
198         if (sync == null) {
199             Log.e(TAG, "stopSync() - no client found for callback");
200             return;
201         }
202 
203         Integer sync_handle = sync.id;
204         binder.unlinkToDeath(sync.deathRecipient, 0);
205 
206         if (sync_handle < 0) {
207             Log.i(TAG, "stopSync() - not finished registration yet");
208             // Sync will be freed once initiated in onSyncStarted()
209             return;
210         }
211 
212         stopSyncNative(sync_handle);
213     }
214 
215     static {
classInitNative()216         classInitNative();
217     }
218 
classInitNative()219     private native static void classInitNative();
initializeNative()220     private native void initializeNative();
cleanupNative()221     private native void cleanupNative();
startSyncNative(int sid, String address, int skip, int timeout, int reg_id)222     private native void startSyncNative(int sid, String address, int skip, int timeout, int reg_id);
stopSyncNative(int sync_handle)223     private native void stopSyncNative(int sync_handle);
224 }
225