Home | History | Annotate | Download | only in gatt
      1 /*
      2  * Copyright (C) 2014 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.app.AlarmManager;
     20 import android.app.PendingIntent;
     21 import android.bluetooth.BluetoothAdapter;
     22 import android.bluetooth.le.ScanCallback;
     23 import android.bluetooth.le.ScanFilter;
     24 import android.bluetooth.le.ScanSettings;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.os.Handler;
     30 import android.os.HandlerThread;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.RemoteException;
     34 import android.os.ServiceManager;
     35 import android.os.SystemClock;
     36 import android.util.Log;
     37 
     38 import com.android.bluetooth.Utils;
     39 import com.android.bluetooth.btservice.AdapterService;
     40 
     41 import java.util.ArrayDeque;
     42 import java.util.Collections;
     43 import java.util.Deque;
     44 import java.util.HashMap;
     45 import java.util.HashSet;
     46 import java.util.Map;
     47 import java.util.Set;
     48 import java.util.UUID;
     49 import java.util.concurrent.ConcurrentHashMap;
     50 import java.util.concurrent.CountDownLatch;
     51 import java.util.concurrent.TimeUnit;
     52 
     53 /**
     54  * Class that handles Bluetooth LE scan related operations.
     55  *
     56  * @hide
     57  */
     58 public class ScanManager {
     59     private static final boolean DBG = GattServiceConfig.DBG;
     60     private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager";
     61 
     62     // Result type defined in bt stack. Need to be accessed by GattService.
     63     static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
     64     static final int SCAN_RESULT_TYPE_FULL = 2;
     65     static final int SCAN_RESULT_TYPE_BOTH = 3;
     66 
     67     // Internal messages for handling BLE scan operations.
     68     private static final int MSG_START_BLE_SCAN = 0;
     69     private static final int MSG_STOP_BLE_SCAN = 1;
     70     private static final int MSG_FLUSH_BATCH_RESULTS = 2;
     71     private static final int MSG_SCAN_TIMEOUT = 3;
     72 
     73     private static final String ACTION_REFRESH_BATCHED_SCAN =
     74             "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";
     75 
     76     // Timeout for each controller operation.
     77     private static final int OPERATION_TIME_OUT_MILLIS = 500;
     78 
     79     private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
     80     // Scan parameters for batch scan.
     81     private BatchScanParams mBatchScanParms;
     82 
     83     private Integer curUsedTrackableAdvertisements;
     84     private GattService mService;
     85     private BroadcastReceiver mBatchAlarmReceiver;
     86     private boolean mBatchAlarmReceiverRegistered;
     87     private ScanNative mScanNative;
     88     private ClientHandler mHandler;
     89 
     90     private Set<ScanClient> mRegularScanClients;
     91     private Set<ScanClient> mBatchClients;
     92 
     93     private CountDownLatch mLatch;
     94 
     95     ScanManager(GattService service) {
     96         mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
     97         mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
     98         mService = service;
     99         mScanNative = new ScanNative();
    100         curUsedTrackableAdvertisements = 0;
    101     }
    102 
    103     void start() {
    104         HandlerThread thread = new HandlerThread("BluetoothScanManager");
    105         thread.start();
    106         mHandler = new ClientHandler(thread.getLooper());
    107     }
    108 
    109     void cleanup() {
    110         mRegularScanClients.clear();
    111         mBatchClients.clear();
    112         mScanNative.cleanup();
    113 
    114         if (mHandler != null) {
    115             // Shut down the thread
    116             mHandler.removeCallbacksAndMessages(null);
    117             Looper looper = mHandler.getLooper();
    118             if (looper != null) {
    119                 looper.quit();
    120             }
    121             mHandler = null;
    122         }
    123     }
    124 
    125     void registerScanner(UUID uuid) {
    126         mScanNative.registerScannerNative(
    127             uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
    128     }
    129 
    130     void unregisterScanner(int scannerId) {
    131         mScanNative.unregisterScannerNative(scannerId);
    132     }
    133 
    134     /**
    135      * Returns the regular scan queue.
    136      */
    137     Set<ScanClient> getRegularScanQueue() {
    138         return mRegularScanClients;
    139     }
    140 
    141     /**
    142      * Returns batch scan queue.
    143      */
    144     Set<ScanClient> getBatchScanQueue() {
    145         return mBatchClients;
    146     }
    147 
    148     /**
    149      * Returns a set of full batch scan clients.
    150      */
    151     Set<ScanClient> getFullBatchScanQueue() {
    152         // TODO: split full batch scan clients and truncated batch clients so we don't need to
    153         // construct this every time.
    154         Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
    155         for (ScanClient client : mBatchClients) {
    156             if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
    157                 fullBatchClients.add(client);
    158             }
    159         }
    160         return fullBatchClients;
    161     }
    162 
    163     void startScan(ScanClient client) {
    164         sendMessage(MSG_START_BLE_SCAN, client);
    165     }
    166 
    167     void stopScan(ScanClient client) {
    168         sendMessage(MSG_STOP_BLE_SCAN, client);
    169     }
    170 
    171     void flushBatchScanResults(ScanClient client) {
    172         sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
    173     }
    174 
    175     void callbackDone(int scannerId, int status) {
    176         if (DBG) Log.d(TAG, "callback done for scannerId - " + scannerId + " status - " + status);
    177         if (status == 0) {
    178             mLatch.countDown();
    179         }
    180         // TODO: add a callback for scan failure.
    181     }
    182 
    183     private void sendMessage(int what, ScanClient client) {
    184         Message message = new Message();
    185         message.what = what;
    186         message.obj = client;
    187         mHandler.sendMessage(message);
    188     }
    189 
    190     private boolean isFilteringSupported() {
    191         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    192         return adapter.isOffloadedFilteringSupported();
    193     }
    194 
    195     // Handler class that handles BLE scan operations.
    196     private class ClientHandler extends Handler {
    197 
    198         ClientHandler(Looper looper) {
    199             super(looper);
    200         }
    201 
    202         @Override
    203         public void handleMessage(Message msg) {
    204             ScanClient client = (ScanClient) msg.obj;
    205             switch (msg.what) {
    206                 case MSG_START_BLE_SCAN:
    207                     handleStartScan(client);
    208                     break;
    209                 case MSG_STOP_BLE_SCAN:
    210                     handleStopScan(client);
    211                     break;
    212                 case MSG_FLUSH_BATCH_RESULTS:
    213                     handleFlushBatchResults(client);
    214                     break;
    215                 case MSG_SCAN_TIMEOUT:
    216                     mScanNative.regularScanTimeout(client);
    217                     break;
    218                 default:
    219                     // Shouldn't happen.
    220                     Log.e(TAG, "received an unkown message : " + msg.what);
    221             }
    222         }
    223 
    224         void handleStartScan(ScanClient client) {
    225             Utils.enforceAdminPermission(mService);
    226             if (DBG) Log.d(TAG, "handling starting scan");
    227 
    228             if (!isScanSupported(client)) {
    229                 Log.e(TAG, "Scan settings not supported");
    230                 return;
    231             }
    232 
    233             if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) {
    234                 Log.e(TAG, "Scan already started");
    235                 return;
    236             }
    237             // Begin scan operations.
    238             if (isBatchClient(client)) {
    239                 mBatchClients.add(client);
    240                 mScanNative.startBatchScan(client);
    241             } else {
    242                 mRegularScanClients.add(client);
    243                 mScanNative.startRegularScan(client);
    244                 if (!mScanNative.isOpportunisticScanClient(client)) {
    245                     mScanNative.configureRegularScanParams();
    246 
    247                     if (!mScanNative.isExemptFromScanDowngrade(client)) {
    248                         Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT);
    249                         msg.obj = client;
    250                         // Only one timeout message should exist at any time
    251                         mHandler.sendMessageDelayed(msg, AppScanStats.SCAN_TIMEOUT_MS);
    252                     }
    253                 }
    254             }
    255         }
    256 
    257         void handleStopScan(ScanClient client) {
    258             Utils.enforceAdminPermission(mService);
    259             if (client == null) return;
    260 
    261             if (mRegularScanClients.contains(client)) {
    262                 mScanNative.stopRegularScan(client);
    263 
    264                 if (mScanNative.numRegularScanClients() == 0) {
    265                     mHandler.removeMessages(MSG_SCAN_TIMEOUT);
    266                 }
    267 
    268                 if (!mScanNative.isOpportunisticScanClient(client)) {
    269                     mScanNative.configureRegularScanParams();
    270                 }
    271             } else {
    272                 mScanNative.stopBatchScan(client);
    273             }
    274             if (client.appDied) {
    275                 if (DBG) Log.d(TAG, "app died, unregister scanner - " + client.scannerId);
    276                 mService.unregisterScanner(client.scannerId);
    277             }
    278         }
    279 
    280         void handleFlushBatchResults(ScanClient client) {
    281             Utils.enforceAdminPermission(mService);
    282             if (!mBatchClients.contains(client)) {
    283                 return;
    284             }
    285             mScanNative.flushBatchResults(client.scannerId);
    286         }
    287 
    288         private boolean isBatchClient(ScanClient client) {
    289             if (client == null || client.settings == null) {
    290                 return false;
    291             }
    292             ScanSettings settings = client.settings;
    293             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
    294                     settings.getReportDelayMillis() != 0;
    295         }
    296 
    297         private boolean isScanSupported(ScanClient client) {
    298             if (client == null || client.settings == null) {
    299                 return true;
    300             }
    301             ScanSettings settings = client.settings;
    302             if (isFilteringSupported()) {
    303                 return true;
    304             }
    305             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
    306                     settings.getReportDelayMillis() == 0;
    307         }
    308     }
    309 
    310     /**
    311      * Parameters for batch scans.
    312      */
    313     class BatchScanParams {
    314         int scanMode;
    315         int fullScanscannerId;
    316         int truncatedScanscannerId;
    317 
    318         BatchScanParams() {
    319             scanMode = -1;
    320             fullScanscannerId = -1;
    321             truncatedScanscannerId = -1;
    322         }
    323 
    324         @Override
    325         public boolean equals(Object obj) {
    326             if (this == obj) {
    327                 return true;
    328             }
    329             if (obj == null || getClass() != obj.getClass()) {
    330                 return false;
    331             }
    332             BatchScanParams other = (BatchScanParams) obj;
    333             return scanMode == other.scanMode && fullScanscannerId == other.fullScanscannerId
    334                     && truncatedScanscannerId == other.truncatedScanscannerId;
    335 
    336         }
    337     }
    338 
    339     public int getCurrentUsedTrackingAdvertisement() {
    340         return curUsedTrackableAdvertisements;
    341     }
    342 
    343     private class ScanNative {
    344 
    345         // Delivery mode defined in bt stack.
    346         private static final int DELIVERY_MODE_IMMEDIATE = 0;
    347         private static final int DELIVERY_MODE_ON_FOUND_LOST = 1;
    348         private static final int DELIVERY_MODE_BATCH = 2;
    349 
    350         private static final int ONFOUND_SIGHTINGS_AGGRESSIVE = 1;
    351         private static final int ONFOUND_SIGHTINGS_STICKY = 4;
    352 
    353         private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1;
    354         private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2;
    355         private static final int ALL_PASS_FILTER_SELECTION = 0;
    356 
    357         private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0;
    358 
    359         /**
    360          * Scan params corresponding to regular scan setting
    361          */
    362         private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 500;
    363         private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 5000;
    364         private static final int SCAN_MODE_BALANCED_WINDOW_MS = 2000;
    365         private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 5000;
    366         private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 5000;
    367         private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 5000;
    368 
    369         /**
    370          * Onfound/onlost for scan settings
    371          */
    372         private static final int MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR = (1);
    373         private static final int MATCH_MODE_STICKY_TIMEOUT_FACTOR = (3);
    374         private static final int ONLOST_FACTOR = 2;
    375         private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500;
    376 
    377         /**
    378          * Scan params corresponding to batch scan setting
    379          */
    380         private static final int SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS = 1500;
    381         private static final int SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS = 150000;
    382         private static final int SCAN_MODE_BATCH_BALANCED_WINDOW_MS = 1500;
    383         private static final int SCAN_MODE_BATCH_BALANCED_INTERVAL_MS = 15000;
    384         private static final int SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS = 1500;
    385         private static final int SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS = 5000;
    386 
    387         // The logic is AND for each filter field.
    388         private static final int LIST_LOGIC_TYPE = 0x1111111;
    389         private static final int FILTER_LOGIC_TYPE = 1;
    390         // Filter indices that are available to user. It's sad we need to maintain filter index.
    391         private final Deque<Integer> mFilterIndexStack;
    392         // Map of scannerId and Filter indices used by client.
    393         private final Map<Integer, Deque<Integer>> mClientFilterIndexMap;
    394         // Keep track of the clients that uses ALL_PASS filters.
    395         private final Set<Integer> mAllPassRegularClients = new HashSet<>();
    396         private final Set<Integer> mAllPassBatchClients = new HashSet<>();
    397 
    398         private AlarmManager mAlarmManager;
    399         private PendingIntent mBatchScanIntervalIntent;
    400 
    401         ScanNative() {
    402             mFilterIndexStack = new ArrayDeque<Integer>();
    403             mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
    404 
    405             mAlarmManager = (AlarmManager) mService.getSystemService(Context.ALARM_SERVICE);
    406             Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
    407             mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent, 0);
    408             IntentFilter filter = new IntentFilter();
    409             filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
    410             mBatchAlarmReceiver = new BroadcastReceiver() {
    411                     @Override
    412                 public void onReceive(Context context, Intent intent) {
    413                     Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime());
    414                     String action = intent.getAction();
    415 
    416                     if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
    417                         if (mBatchClients.isEmpty()) {
    418                             return;
    419                         }
    420                         // Note this actually flushes all pending batch data.
    421                         flushBatchScanResults(mBatchClients.iterator().next());
    422                     }
    423                 }
    424             };
    425             mService.registerReceiver(mBatchAlarmReceiver, filter);
    426             mBatchAlarmReceiverRegistered = true;
    427         }
    428 
    429         private void resetCountDownLatch() {
    430             mLatch = new CountDownLatch(1);
    431         }
    432 
    433         // Returns true if mLatch reaches 0, false if timeout or interrupted.
    434         private boolean waitForCallback() {
    435             try {
    436                 return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
    437             } catch (InterruptedException e) {
    438                 return false;
    439             }
    440         }
    441 
    442         void configureRegularScanParams() {
    443             if (DBG) {
    444                 Log.d(TAG, "configureRegularScanParams() - queue=" + mRegularScanClients.size());
    445             }
    446             int curScanSetting = Integer.MIN_VALUE;
    447             ScanClient client = getAggressiveClient(mRegularScanClients);
    448             if (client != null) {
    449                 curScanSetting = client.settings.getScanMode();
    450             }
    451 
    452             if (DBG) {
    453                 Log.d(TAG, "configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting
    454                                 + " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting);
    455             }
    456 
    457             if (curScanSetting != Integer.MIN_VALUE &&
    458                     curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
    459                 if (curScanSetting != mLastConfiguredScanSetting) {
    460                     int scanWindow = getScanWindowMillis(client.settings);
    461                     int scanInterval = getScanIntervalMillis(client.settings);
    462                     // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
    463                     scanWindow = Utils.millsToUnit(scanWindow);
    464                     scanInterval = Utils.millsToUnit(scanInterval);
    465                     gattClientScanNative(false);
    466                     if (DBG) {
    467                         Log.d(TAG, "configureRegularScanParams - scanInterval = " + scanInterval
    468                                         + "configureRegularScanParams - scanWindow = "
    469                                         + scanWindow);
    470                     }
    471                     gattSetScanParametersNative(client.scannerId, scanInterval, scanWindow);
    472                     gattClientScanNative(true);
    473                     mLastConfiguredScanSetting = curScanSetting;
    474                 }
    475             } else {
    476                 mLastConfiguredScanSetting = curScanSetting;
    477                 if (DBG) Log.d(TAG, "configureRegularScanParams() - queue emtpy, scan stopped");
    478             }
    479         }
    480 
    481         ScanClient getAggressiveClient(Set<ScanClient> cList) {
    482             ScanClient result = null;
    483             int curScanSetting = Integer.MIN_VALUE;
    484             for (ScanClient client : cList) {
    485                 // ScanClient scan settings are assumed to be monotonically increasing in value for
    486                 // more power hungry(higher duty cycle) operation.
    487                 if (client.settings.getScanMode() > curScanSetting) {
    488                     result = client;
    489                     curScanSetting = client.settings.getScanMode();
    490                 }
    491             }
    492             return result;
    493         }
    494 
    495         void startRegularScan(ScanClient client) {
    496             if (isFilteringSupported() && mFilterIndexStack.isEmpty()
    497                     && mClientFilterIndexMap.isEmpty()) {
    498                 initFilterIndexStack();
    499             }
    500             if (isFilteringSupported()) {
    501                 configureScanFilters(client);
    502             }
    503             // Start scan native only for the first client.
    504             if (numRegularScanClients() == 1) {
    505                 gattClientScanNative(true);
    506             }
    507         }
    508 
    509         private int numRegularScanClients() {
    510             int num = 0;
    511             for (ScanClient client: mRegularScanClients) {
    512                 if (client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
    513                     num++;
    514                 }
    515             }
    516             return num;
    517         }
    518 
    519         void startBatchScan(ScanClient client) {
    520             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
    521                 initFilterIndexStack();
    522             }
    523             configureScanFilters(client);
    524             if (!isOpportunisticScanClient(client)) {
    525                 // Reset batch scan. May need to stop the existing batch scan and update scan params.
    526                 resetBatchScan(client);
    527             }
    528         }
    529 
    530         private boolean isExemptFromScanDowngrade(ScanClient client) {
    531           return isOpportunisticScanClient(client)
    532               || isFirstMatchScanClient(client)
    533               || !shouldUseAllPassFilter(client);
    534         }
    535 
    536         private boolean isOpportunisticScanClient(ScanClient client) {
    537             return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
    538         }
    539 
    540         private boolean isFirstMatchScanClient(ScanClient client) {
    541             return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
    542         }
    543 
    544         private void resetBatchScan(ScanClient client) {
    545             int scannerId = client.scannerId;
    546             BatchScanParams batchScanParams = getBatchScanParams();
    547             // Stop batch if batch scan params changed and previous params is not null.
    548             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
    549                 if (DBG) Log.d(TAG, "stopping BLe Batch");
    550                 resetCountDownLatch();
    551                 gattClientStopBatchScanNative(scannerId);
    552                 waitForCallback();
    553                 // Clear pending results as it's illegal to config storage if there are still
    554                 // pending results.
    555                 flushBatchResults(scannerId);
    556             }
    557             // Start batch if batchScanParams changed and current params is not null.
    558             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
    559                 int notifyThreshold = 95;
    560                 if (DBG) Log.d(TAG, "Starting BLE batch scan");
    561                 int resultType = getResultType(batchScanParams);
    562                 int fullScanPercent = getFullScanStoragePercent(resultType);
    563                 resetCountDownLatch();
    564                 if (DBG) Log.d(TAG, "configuring batch scan storage, appIf " + client.scannerId);
    565                 gattClientConfigBatchScanStorageNative(client.scannerId, fullScanPercent,
    566                         100 - fullScanPercent, notifyThreshold);
    567                 waitForCallback();
    568                 resetCountDownLatch();
    569                 int scanInterval =
    570                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
    571                 int scanWindow =
    572                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
    573                 gattClientStartBatchScanNative(scannerId, resultType, scanInterval,
    574                         scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
    575                 waitForCallback();
    576             }
    577             mBatchScanParms = batchScanParams;
    578             setBatchAlarm();
    579         }
    580 
    581         private int getFullScanStoragePercent(int resultType) {
    582             switch (resultType) {
    583                 case SCAN_RESULT_TYPE_FULL:
    584                     return 100;
    585                 case SCAN_RESULT_TYPE_TRUNCATED:
    586                     return 0;
    587                 case SCAN_RESULT_TYPE_BOTH:
    588                     return 50;
    589                 default:
    590                     return 50;
    591             }
    592         }
    593 
    594         private BatchScanParams getBatchScanParams() {
    595             if (mBatchClients.isEmpty()) {
    596                 return null;
    597             }
    598             BatchScanParams params = new BatchScanParams();
    599             // TODO: split full batch scan results and truncated batch scan results to different
    600             // collections.
    601             for (ScanClient client : mBatchClients) {
    602                 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
    603                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
    604                     params.fullScanscannerId = client.scannerId;
    605                 } else {
    606                     params.truncatedScanscannerId = client.scannerId;
    607                 }
    608             }
    609             return params;
    610         }
    611 
    612         private int getBatchScanWindowMillis(int scanMode) {
    613             switch (scanMode) {
    614                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
    615                     return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
    616                 case ScanSettings.SCAN_MODE_BALANCED:
    617                     return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
    618                 case ScanSettings.SCAN_MODE_LOW_POWER:
    619                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
    620                 default:
    621                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
    622             }
    623         }
    624 
    625         private int getBatchScanIntervalMillis(int scanMode) {
    626             switch (scanMode) {
    627                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
    628                     return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
    629                 case ScanSettings.SCAN_MODE_BALANCED:
    630                     return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
    631                 case ScanSettings.SCAN_MODE_LOW_POWER:
    632                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
    633                 default:
    634                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
    635             }
    636         }
    637 
    638         // Set the batch alarm to be triggered within a short window after batch interval. This
    639         // allows system to optimize wake up time while still allows a degree of precise control.
    640         private void setBatchAlarm() {
    641             // Cancel any pending alarm just in case.
    642             mAlarmManager.cancel(mBatchScanIntervalIntent);
    643             if (mBatchClients.isEmpty()) {
    644                 return;
    645             }
    646             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
    647             // Allows the alarm to be triggered within
    648             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
    649             long windowLengthMillis = batchTriggerIntervalMillis / 10;
    650             long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
    651             mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    652                     windowStartMillis, windowLengthMillis,
    653                     mBatchScanIntervalIntent);
    654         }
    655 
    656         void stopRegularScan(ScanClient client) {
    657             // Remove scan filters and recycle filter indices.
    658             if (client == null) return;
    659             int deliveryMode = getDeliveryMode(client);
    660             if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
    661                 for (ScanFilter filter : client.filters) {
    662                     int entriesToFree = getNumOfTrackingAdvertisements(client.settings);
    663                     if (!manageAllocationOfTrackingAdvertisement(entriesToFree, false)) {
    664                         Log.e(TAG, "Error freeing for onfound/onlost filter resources "
    665                                     + entriesToFree);
    666                         try {
    667                             mService.onScanManagerErrorCallback(client.scannerId,
    668                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
    669                         } catch (RemoteException e) {
    670                             Log.e(TAG, "failed on onScanManagerCallback at freeing", e);
    671                         }
    672                     }
    673                 }
    674             }
    675             mRegularScanClients.remove(client);
    676             if (numRegularScanClients() == 0) {
    677                 if (DBG) Log.d(TAG, "stop scan");
    678                 gattClientScanNative(false);
    679             }
    680             removeScanFilters(client.scannerId);
    681         }
    682 
    683         void regularScanTimeout(ScanClient client) {
    684             if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) {
    685                 Log.w(TAG,
    686                         "Moving scan client to opportunistic (scannerId " + client.scannerId + ")");
    687                 setOpportunisticScanClient(client);
    688                 removeScanFilters(client.scannerId);
    689                 client.stats.setScanTimeout();
    690             }
    691 
    692             // The scan should continue for background scans
    693             configureRegularScanParams();
    694             if (numRegularScanClients() == 0) {
    695                 if (DBG) Log.d(TAG, "stop scan");
    696                 gattClientScanNative(false);
    697             }
    698         }
    699 
    700         void setOpportunisticScanClient(ScanClient client) {
    701             // TODO: Add constructor to ScanSettings.Builder
    702             // that can copy values from an existing ScanSettings object
    703             ScanSettings.Builder builder = new ScanSettings.Builder();
    704             ScanSettings settings = client.settings;
    705             builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC);
    706             builder.setCallbackType(settings.getCallbackType());
    707             builder.setScanResultType(settings.getScanResultType());
    708             builder.setReportDelay(settings.getReportDelayMillis());
    709             builder.setNumOfMatches(settings.getNumOfMatches());
    710             client.settings = builder.build();
    711         }
    712 
    713         // Find the regular scan client information.
    714         ScanClient getRegularScanClient(int scannerId) {
    715             for (ScanClient client : mRegularScanClients) {
    716               if (client.scannerId == scannerId) return client;
    717             }
    718             return null;
    719         }
    720 
    721         void stopBatchScan(ScanClient client) {
    722             mBatchClients.remove(client);
    723             removeScanFilters(client.scannerId);
    724             if (!isOpportunisticScanClient(client)) {
    725                 resetBatchScan(client);
    726             }
    727         }
    728 
    729         void flushBatchResults(int scannerId) {
    730             if (DBG) Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId);
    731             if (mBatchScanParms.fullScanscannerId != -1) {
    732                 resetCountDownLatch();
    733                 gattClientReadScanReportsNative(mBatchScanParms.fullScanscannerId,
    734                         SCAN_RESULT_TYPE_FULL);
    735                 waitForCallback();
    736             }
    737             if (mBatchScanParms.truncatedScanscannerId != -1) {
    738                 resetCountDownLatch();
    739                 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanscannerId,
    740                         SCAN_RESULT_TYPE_TRUNCATED);
    741                 waitForCallback();
    742             }
    743             setBatchAlarm();
    744         }
    745 
    746         void cleanup() {
    747             mAlarmManager.cancel(mBatchScanIntervalIntent);
    748             // Protect against multiple calls of cleanup.
    749             if (mBatchAlarmReceiverRegistered) {
    750                 mService.unregisterReceiver(mBatchAlarmReceiver);
    751             }
    752             mBatchAlarmReceiverRegistered = false;
    753         }
    754 
    755         private long getBatchTriggerIntervalMillis() {
    756             long intervalMillis = Long.MAX_VALUE;
    757             for (ScanClient client : mBatchClients) {
    758                 if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
    759                     intervalMillis = Math.min(intervalMillis,
    760                             client.settings.getReportDelayMillis());
    761                 }
    762             }
    763             return intervalMillis;
    764         }
    765 
    766         // Add scan filters. The logic is:
    767         // If no offload filter can/needs to be set, set ALL_PASS filter.
    768         // Otherwise offload all filters to hardware and enable all filters.
    769         private void configureScanFilters(ScanClient client) {
    770             int scannerId = client.scannerId;
    771             int deliveryMode = getDeliveryMode(client);
    772             int trackEntries = 0;
    773 
    774             // Do not add any filters set by opportunistic scan clients
    775             if (isOpportunisticScanClient(client)) {
    776                 return;
    777             }
    778 
    779             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
    780                 return;
    781             }
    782 
    783             resetCountDownLatch();
    784             gattClientScanFilterEnableNative(scannerId, true);
    785             waitForCallback();
    786 
    787             if (shouldUseAllPassFilter(client)) {
    788                 int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
    789                         ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
    790                 resetCountDownLatch();
    791                 // Don't allow Onfound/onlost with all pass
    792                 configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION,
    793                                 filterIndex, 0);
    794                 waitForCallback();
    795             } else {
    796                 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
    797                 for (ScanFilter filter : client.filters) {
    798                     ScanFilterQueue queue = new ScanFilterQueue();
    799                     queue.addScanFilter(filter);
    800                     int featureSelection = queue.getFeatureSelection();
    801                     int filterIndex = mFilterIndexStack.pop();
    802                     while (!queue.isEmpty()) {
    803                         resetCountDownLatch();
    804                         addFilterToController(scannerId, queue.pop(), filterIndex);
    805                         waitForCallback();
    806                     }
    807                     resetCountDownLatch();
    808                     if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
    809                         trackEntries = getNumOfTrackingAdvertisements(client.settings);
    810                         if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) {
    811                             Log.e(TAG, "No hardware resources for onfound/onlost filter " +
    812                                     trackEntries);
    813                             try {
    814                                 mService.onScanManagerErrorCallback(scannerId,
    815                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
    816                             } catch (RemoteException e) {
    817                                 Log.e(TAG, "failed on onScanManagerCallback", e);
    818                             }
    819                         }
    820                     }
    821                     configureFilterParamter(scannerId, client, featureSelection, filterIndex,
    822                                             trackEntries);
    823                     waitForCallback();
    824                     clientFilterIndices.add(filterIndex);
    825                 }
    826                 mClientFilterIndexMap.put(scannerId, clientFilterIndices);
    827             }
    828         }
    829 
    830         // Check whether the filter should be added to controller.
    831         // Note only on ALL_PASS filter should be added.
    832         private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
    833             // Not an ALL_PASS client, need to add filter.
    834             if (!shouldUseAllPassFilter(client)) {
    835                 return true;
    836             }
    837 
    838             if (deliveryMode == DELIVERY_MODE_BATCH) {
    839                 mAllPassBatchClients.add(client.scannerId);
    840                 return mAllPassBatchClients.size() == 1;
    841             } else {
    842                 mAllPassRegularClients.add(client.scannerId);
    843                 return mAllPassRegularClients.size() == 1;
    844             }
    845         }
    846 
    847         private void removeScanFilters(int scannerId) {
    848             Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId);
    849             if (filterIndices != null) {
    850                 mFilterIndexStack.addAll(filterIndices);
    851                 for (Integer filterIndex : filterIndices) {
    852                     resetCountDownLatch();
    853                     gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
    854                     waitForCallback();
    855                 }
    856             }
    857             // Remove if ALL_PASS filters are used.
    858             removeFilterIfExisits(mAllPassRegularClients, scannerId,
    859                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
    860             removeFilterIfExisits(mAllPassBatchClients, scannerId,
    861                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
    862         }
    863 
    864         private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) {
    865             if (!clients.contains(scannerId)) {
    866                 return;
    867             }
    868             clients.remove(scannerId);
    869             // Remove ALL_PASS filter iff no app is using it.
    870             if (clients.isEmpty()) {
    871                 resetCountDownLatch();
    872                 gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
    873                 waitForCallback();
    874             }
    875         }
    876 
    877         private ScanClient getBatchScanClient(int scannerId) {
    878             for (ScanClient client : mBatchClients) {
    879                 if (client.scannerId == scannerId) {
    880                     return client;
    881                 }
    882             }
    883             return null;
    884         }
    885 
    886         /**
    887          * Return batch scan result type value defined in bt stack.
    888          */
    889         private int getResultType(BatchScanParams params) {
    890             if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) {
    891                 return SCAN_RESULT_TYPE_BOTH;
    892             }
    893             if (params.truncatedScanscannerId != -1) {
    894                 return SCAN_RESULT_TYPE_TRUNCATED;
    895             }
    896             if (params.fullScanscannerId != -1) {
    897                 return SCAN_RESULT_TYPE_FULL;
    898             }
    899             return -1;
    900         }
    901 
    902         // Check if ALL_PASS filter should be used for the client.
    903         private boolean shouldUseAllPassFilter(ScanClient client) {
    904             if (client == null) {
    905                 return true;
    906             }
    907             if (client.filters == null || client.filters.isEmpty()) {
    908                 return true;
    909             }
    910             return client.filters.size() > mFilterIndexStack.size();
    911         }
    912 
    913         private void addFilterToController(int scannerId, ScanFilterQueue.Entry entry,
    914                 int filterIndex) {
    915             if (DBG) Log.d(TAG, "addFilterToController: " + entry.type);
    916             switch (entry.type) {
    917                 case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
    918                     if (DBG) Log.d(TAG, "add address " + entry.address);
    919                     gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
    920                             0,
    921                             "", entry.address, (byte) entry.addr_type, new byte[0], new byte[0]);
    922                     break;
    923 
    924                 case ScanFilterQueue.TYPE_SERVICE_DATA:
    925                     gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
    926                             0,
    927                             "", "", (byte) 0, entry.data, entry.data_mask);
    928                     break;
    929 
    930                 case ScanFilterQueue.TYPE_SERVICE_UUID:
    931                 case ScanFilterQueue.TYPE_SOLICIT_UUID:
    932                     gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0,
    933                             entry.uuid.getLeastSignificantBits(),
    934                             entry.uuid.getMostSignificantBits(),
    935                             entry.uuid_mask.getLeastSignificantBits(),
    936                             entry.uuid_mask.getMostSignificantBits(),
    937                             "", "", (byte) 0, new byte[0], new byte[0]);
    938                     break;
    939 
    940                 case ScanFilterQueue.TYPE_LOCAL_NAME:
    941                     if (DBG) Log.d(TAG, "adding filters: " + entry.name);
    942                     gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
    943                             0,
    944                             entry.name, "", (byte) 0, new byte[0], new byte[0]);
    945                     break;
    946 
    947                 case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
    948                     int len = entry.data.length;
    949                     if (entry.data_mask.length != len)
    950                         return;
    951                     gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, entry.company,
    952                             entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
    953                             entry.data, entry.data_mask);
    954                     break;
    955             }
    956         }
    957 
    958         private void initFilterIndexStack() {
    959             int maxFiltersSupported =
    960                     AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
    961             // Start from index 3 as:
    962             // index 0 is reserved for ALL_PASS filter in Settings app.
    963             // index 1 is reserved for ALL_PASS filter for regular scan apps.
    964             // index 2 is reserved for ALL_PASS filter for batch scan apps.
    965             for (int i = 3; i < maxFiltersSupported; ++i) {
    966                 mFilterIndexStack.add(i);
    967             }
    968         }
    969 
    970         // Configure filter parameters.
    971         private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection,
    972                 int filterIndex, int numOfTrackingEntries) {
    973             int deliveryMode = getDeliveryMode(client);
    974             int rssiThreshold = Byte.MIN_VALUE;
    975             ScanSettings settings = client.settings;
    976             int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true);
    977             int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false);
    978             int onFoundCount = getOnFoundOnLostSightings(settings);
    979             onLostTimeout = 10000;
    980             if (DBG) {
    981                 Log.d(TAG, "configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " "
    982                                 + onFoundCount + " " + numOfTrackingEntries);
    983             }
    984             FilterParams FiltValue = new FilterParams(scannerId, filterIndex, featureSelection,
    985                     LIST_LOGIC_TYPE, FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
    986                     onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries);
    987             gattClientScanFilterParamAddNative(FiltValue);
    988         }
    989 
    990         // Get delivery mode based on scan settings.
    991         private int getDeliveryMode(ScanClient client) {
    992             if (client == null) {
    993                 return DELIVERY_MODE_IMMEDIATE;
    994             }
    995             ScanSettings settings = client.settings;
    996             if (settings == null) {
    997                 return DELIVERY_MODE_IMMEDIATE;
    998             }
    999             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
   1000                     || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
   1001                 return DELIVERY_MODE_ON_FOUND_LOST;
   1002             }
   1003             return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
   1004                     : DELIVERY_MODE_BATCH;
   1005         }
   1006 
   1007         private int getScanWindowMillis(ScanSettings settings) {
   1008             if (settings == null) {
   1009                 return SCAN_MODE_LOW_POWER_WINDOW_MS;
   1010             }
   1011             switch (settings.getScanMode()) {
   1012                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
   1013                     return SCAN_MODE_LOW_LATENCY_WINDOW_MS;
   1014                 case ScanSettings.SCAN_MODE_BALANCED:
   1015                     return SCAN_MODE_BALANCED_WINDOW_MS;
   1016                 case ScanSettings.SCAN_MODE_LOW_POWER:
   1017                     return SCAN_MODE_LOW_POWER_WINDOW_MS;
   1018                 default:
   1019                     return SCAN_MODE_LOW_POWER_WINDOW_MS;
   1020             }
   1021         }
   1022 
   1023         private int getScanIntervalMillis(ScanSettings settings) {
   1024             if (settings == null)
   1025                 return SCAN_MODE_LOW_POWER_INTERVAL_MS;
   1026             switch (settings.getScanMode()) {
   1027                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
   1028                     return SCAN_MODE_LOW_LATENCY_INTERVAL_MS;
   1029                 case ScanSettings.SCAN_MODE_BALANCED:
   1030                     return SCAN_MODE_BALANCED_INTERVAL_MS;
   1031                 case ScanSettings.SCAN_MODE_LOW_POWER:
   1032                     return SCAN_MODE_LOW_POWER_INTERVAL_MS;
   1033                 default:
   1034                     return SCAN_MODE_LOW_POWER_INTERVAL_MS;
   1035             }
   1036         }
   1037 
   1038         private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) {
   1039             int factor;
   1040             int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS;
   1041 
   1042             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
   1043                 factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR;
   1044             } else {
   1045                 factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR;
   1046             }
   1047             if (!onFound) factor = factor * ONLOST_FACTOR;
   1048             return (timeout*factor);
   1049         }
   1050 
   1051         private int getOnFoundOnLostSightings(ScanSettings settings) {
   1052             if (settings == null)
   1053                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
   1054             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
   1055                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
   1056             } else {
   1057                 return ONFOUND_SIGHTINGS_STICKY;
   1058             }
   1059         }
   1060 
   1061         private int getNumOfTrackingAdvertisements(ScanSettings settings) {
   1062             if (settings == null)
   1063                 return 0;
   1064             int val=0;
   1065             int maxTotalTrackableAdvertisements =
   1066                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
   1067             // controller based onfound onlost resources are scarce commodity; the
   1068             // assignment of filters to num of beacons to track is configurable based
   1069             // on hw capabilities. Apps give an intent and allocation of onfound
   1070             // resources or failure there of is done based on availibility - FCFS model
   1071             switch (settings.getNumOfMatches()) {
   1072                 case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT:
   1073                     val = 1;
   1074                     break;
   1075                 case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT:
   1076                     val = 2;
   1077                     break;
   1078                 case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT:
   1079                     val = maxTotalTrackableAdvertisements/2;
   1080                     break;
   1081                 default:
   1082                     val = 1;
   1083                     if (DBG) {
   1084                         Log.d(TAG, "Invalid setting for getNumOfMatches() "
   1085                                         + settings.getNumOfMatches());
   1086                     }
   1087             }
   1088             return val;
   1089         }
   1090 
   1091         private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement,
   1092                             boolean allocate) {
   1093             int maxTotalTrackableAdvertisements =
   1094                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
   1095             synchronized(curUsedTrackableAdvertisements) {
   1096                 int availableEntries = maxTotalTrackableAdvertisements
   1097                                             - curUsedTrackableAdvertisements;
   1098                 if (allocate) {
   1099                     if (availableEntries >= numOfTrackableAdvertisement) {
   1100                         curUsedTrackableAdvertisements += numOfTrackableAdvertisement;
   1101                         return true;
   1102                     } else {
   1103                         return false;
   1104                     }
   1105                 } else {
   1106                     if (numOfTrackableAdvertisement > curUsedTrackableAdvertisements) {
   1107                         return false;
   1108                     } else {
   1109                          curUsedTrackableAdvertisements -= numOfTrackableAdvertisement;
   1110                          return true;
   1111                     }
   1112                 }
   1113             }
   1114         }
   1115 
   1116 
   1117         /************************** Regular scan related native methods **************************/
   1118         private native void registerScannerNative(long app_uuid_lsb, long app_uuid_msb);
   1119         private native void unregisterScannerNative(int scannerId);
   1120 
   1121         private native void gattClientScanNative(boolean start);
   1122 
   1123         private native void gattSetScanParametersNative(int client_if, int scan_interval,
   1124                 int scan_window);
   1125 
   1126         /************************** Filter related native methods ********************************/
   1127         private native void gattClientScanFilterAddNative(int client_if,
   1128                 int filter_type, int filter_index, int company_id,
   1129                 int company_id_mask, long uuid_lsb, long uuid_msb,
   1130                 long uuid_mask_lsb, long uuid_mask_msb, String name,
   1131                 String address, byte addr_type, byte[] data, byte[] mask);
   1132 
   1133         private native void gattClientScanFilterDeleteNative(int client_if,
   1134                 int filter_type, int filter_index, int company_id,
   1135                 int company_id_mask, long uuid_lsb, long uuid_msb,
   1136                 long uuid_mask_lsb, long uuid_mask_msb, String name,
   1137                 String address, byte addr_type, byte[] data, byte[] mask);
   1138 
   1139         private native void gattClientScanFilterParamAddNative(FilterParams FiltValue);
   1140 
   1141         // Note this effectively remove scan filters for ALL clients.
   1142         private native void gattClientScanFilterParamClearAllNative(
   1143                 int client_if);
   1144 
   1145         private native void gattClientScanFilterParamDeleteNative(
   1146                 int client_if, int filt_index);
   1147 
   1148         private native void gattClientScanFilterClearNative(int client_if,
   1149                 int filter_index);
   1150 
   1151         private native void gattClientScanFilterEnableNative(int client_if,
   1152                 boolean enable);
   1153 
   1154         /************************** Batch related native methods *********************************/
   1155         private native void gattClientConfigBatchScanStorageNative(int client_if,
   1156                 int max_full_reports_percent, int max_truncated_reports_percent,
   1157                 int notify_threshold_percent);
   1158 
   1159         private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
   1160                 int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
   1161 
   1162         private native void gattClientStopBatchScanNative(int client_if);
   1163 
   1164         private native void gattClientReadScanReportsNative(int client_if, int scan_type);
   1165     }
   1166 }
   1167