Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9b5206e5 authored by Wei Wang's avatar Wei Wang Committed by Android (Google) Code Review
Browse files

Merge "Implementation of batch scan."

parents 6e6e259b bc283ba1
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1706,7 +1706,6 @@ static JNINativeMethod sStateMachineMethods[] = {
    {"gattClientConfigBatchScanStorageNative", "(IIII)V",(void *) gattClientConfigBatchScanStorageNative},
    {"gattClientStartBatchScanNative", "(IIIIII)V", (void *) gattClientStartBatchScanNative},
    {"gattClientStopBatchScanNative", "(I)V", (void *) gattClientStopBatchScanNative},
    {"gattClientReadScanReportsNative", "(II)V", (void *) gattClientReadScanReportsNative},
    {"gattClientScanFilterParamAddNative", "(IIIIIIIIIII)V", (void *) gattClientScanFilterParamAddNative},
    {"gattClientScanFilterParamDeleteNative", "(II)V", (void *) gattClientScanFilterParamDeleteNative},
    {"gattClientScanFilterParamClearAllNative", "(I)V", (void *) gattClientScanFilterParamClearAllNative},
@@ -1761,6 +1760,7 @@ static JNINativeMethod sMethods[] = {

    {"gattSetAdvDataNative", "(IZZZIII[B[B[B)V", (void *) gattSetAdvDataNative},
    {"gattSetScanParametersNative", "(II)V", (void *) gattSetScanParametersNative},
    {"gattClientReadScanReportsNative", "(II)V", (void *) gattClientReadScanReportsNative},
    {"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void *) gattTestNative},
};

+121 −11
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.bluetooth.btservice.ProfileService;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -97,6 +98,12 @@ public class GattService extends ProfileService {
    private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 5000;
    private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 5000;

    private static final int MAC_ADDRESS_LENGTH = 6;
    // Batch scan related constants.
    private static final int TRUNCATED_RESULT_SIZE = 11;
    private static final int TIME_STAMP_LENGTH = 2;


    /**
     * Search queue to serialize remote onbject inspection.
     */
@@ -351,6 +358,13 @@ public class GattService extends ProfileService {
            service.stopScan(appIf, isServer);
        }

        @Override
        public void flushPendingBatchResults(int appIf, boolean isServer) {
            GattService service = getService();
            if (service == null) return;
            service.flushPendingBatchResults(appIf, isServer);
        }

        public void clientConnect(int clientIf, String address, boolean isDirect, int transport) {
            GattService service = getService();
            if (service == null) return;
@@ -662,9 +676,9 @@ public class GattService extends ProfileService {
                if (app != null) {
                    BluetoothDevice device = BluetoothAdapter.getDefaultAdapter()
                            .getRemoteDevice(address);
                    long scanTimeMicros =
                    long scanTimeNanos =
                            TimeUnit.NANOSECONDS.toMicros(SystemClock.elapsedRealtimeNanos());
                    ScanResult result = new ScanResult(device, adv_data, rssi, scanTimeMicros);
                    ScanResult result = new ScanResult(device, adv_data, rssi, scanTimeNanos);
                    if (matchesFilters(client, result)) {
                        try {
                            app.callback.onScanResult(address, rssi, adv_data);
@@ -1037,7 +1051,7 @@ public class GattService extends ProfileService {
        if (DBG) {
            Log.d(TAG, "onBatchScanStorageConfigured() - clientIf="+ clientIf + ", status=" + status);
        }

        mStateMachine.callbackDone();
    }

    // TODO: split into two different callbacks : onBatchScanStarted and onBatchScanStopped.
@@ -1046,20 +1060,100 @@ public class GattService extends ProfileService {
            Log.d(TAG, "onBatchScanStartStopped() - clientIf=" + clientIf
                    + ", status=" + status + ", startStopAction=" + startStopAction);
        }
        mStateMachine.callbackDone();
    }

    void onBatchScanReports(int status, int clientIf, int reportType, int numRecords,
            byte[] recordData) {
            byte[] recordData) throws RemoteException {
        if (DBG) {
            Log.d(TAG, "onBatchScanReports() - clientIf=" + clientIf + ", status=" + status
                    + ", reportType=" + reportType + ", numRecords=" + numRecords);
        }
        ClientMap.App app = mClientMap.getById(clientIf);
        if (app == null) return;
        Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData);
        app.callback.onBatchScanResults(new ArrayList<ScanResult>(results));
    }

    private Set<ScanResult> parseBatchScanResults(int numRecords, int reportType,
            byte[] batchRecord) {
        if (numRecords == 0) {
            return Collections.emptySet();
        }
        if (DBG) Log.d(TAG, "current time is " + SystemClock.elapsedRealtimeNanos());
        if (reportType == GattServiceStateMachine.SCAN_RESULT_TYPE_TRUNCATED) {
            return parseTruncatedResults(numRecords, batchRecord);
        } else {
            return parseFullResults(numRecords, batchRecord);
        }
    }

    private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) {
        Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
        for (int i = 0; i < numRecords; ++i) {
            byte[] record = extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE,
                    TRUNCATED_RESULT_SIZE);
            byte[] address = extractBytes(batchRecord, 0, 6);
            BluetoothDevice device = mAdapter.getRemoteDevice(address);
            int rssi = record[8];
            // Timestamp is in every 50 ms.
            long timestampNanos = parseTimestampNanos(extractBytes(record, 9, 2));
            results.add(new ScanResult(device, new byte[0], rssi, timestampNanos));
        }
        return results;
    }

    private long parseTimestampNanos(byte[] data) {
        long timestampUnit = data[1] & 0xFF << 8 + data[0];
        long timestampNanos = SystemClock.elapsedRealtimeNanos() -
                TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50);
        return timestampNanos;
    }

    private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) {
        Set<ScanResult> results = new HashSet<ScanResult>(numRecords);
        int position = 0;
        while (position < batchRecord.length) {
            byte[] address = extractBytes(batchRecord, position, 6);
            BluetoothDevice device = mAdapter.getRemoteDevice(address);
            position += 6;
            // Skip address type.
            position++;
            // Skip tx power level.
            position++;
            int rssi = batchRecord[position++];
            long timestampNanos = parseTimestampNanos(extractBytes(batchRecord, position, 2));
            position += 2;

            // Combine advertise packet and scan response packet.
            int advertisePacketLen = batchRecord[position++];
            byte[] advertiseBytes = extractBytes(batchRecord, position, advertisePacketLen);
            position += advertisePacketLen;
            int scanResponsePacketLen = batchRecord[position++];
            byte[] scanResponseBytes = extractBytes(batchRecord, position, scanResponsePacketLen);
            position += scanResponsePacketLen;
            byte[] scanRecord = new byte[advertisePacketLen + scanResponsePacketLen];
            System.arraycopy(advertiseBytes, 0, scanRecord, 0, advertisePacketLen);
            System.arraycopy(scanResponseBytes, 0, scanRecord,
                    advertisePacketLen, scanResponsePacketLen);
            results.add(new ScanResult(device, scanRecord, rssi, timestampNanos));
        }
        return results;
    }

    // Helper method to extract bytes from byte array.
    private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
        byte[] bytes = new byte[length];
        System.arraycopy(scanRecord, start, bytes, 0, length);
        return bytes;
    }

    void onBatchScanThresholdCrossed(int clientIf) {
        if (DBG) {
            Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf);
        }
        boolean isServer = false;
        flushPendingBatchResults(clientIf, isServer);
    }

    void onTrackAdvFoundLost(int filterIndex, int addrType, String address, int advState,
@@ -1289,10 +1383,24 @@ public class GattService extends ProfileService {
                if (DBG) Log.d(TAG, "startScan() - adding client=" + appIf);
                mScanQueue.add(new ScanClient(appIf, isServer, settings, filters));
            }
            sendStartScanMessage(appIf, new HashSet<ScanFilter>(filters));
            sendStartScanMessage(appIf, getScanClient(appIf, isServer));
        }
    }

    void flushPendingBatchResults(int clientIf, boolean isServer) {
        if (DBG) Log.d(TAG, "flushPendingBatchResults - clientIf=" + clientIf +
                ", isServer=" + isServer);
        ScanClient scanClient = getScanClient(clientIf, isServer);
        if (scanClient == null || scanClient.settings == null
                || scanClient.settings.getReportDelayNanos() == 0) {
            // Not a batch scan client.
            Log.e(TAG, "called flushPendingBatchResults without a proper app!");
            return;
        }
        int resultType = mStateMachine.getResultType(scanClient.settings);
        gattClientReadScanReportsNative(clientIf, resultType);
    }

    void configureScanParams(int appIf) {
        if (DBG) Log.d(TAG, "configureScanParams() - queue=" + mScanQueue.size());
        int curScanSetting = Integer.MIN_VALUE;
@@ -1345,10 +1453,10 @@ public class GattService extends ProfileService {
        }
    }

    private void sendStartScanMessage(int clientIf, Set<ScanFilter> filters) {
    private void sendStartScanMessage(int clientIf, ScanClient client) {
        Message message = mStateMachine.obtainMessage(GattServiceStateMachine.START_BLE_SCAN);
        message.arg1 = clientIf;
        message.obj = filters;
        message.obj = client;
        mStateMachine.sendMessage(message);
    }

@@ -2471,4 +2579,6 @@ public class GattService extends ProfileService {
    private native void gattServerSendResponseNative (int server_if,
            int conn_id, int trans_id, int status, int handle, int offset,
            byte[] val, int auth_req);

    private native void gattClientReadScanReportsNative(int client_if, int scan_type);
}
+94 −30
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.AdvertisementData;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -34,6 +35,7 @@ import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
@@ -105,6 +107,15 @@ public class GattServiceStateMachine extends StateMachine {
    private static final int ADVERTISING_EVENT_TYPE_SCANNABLE = 2;
    private static final int ADVERTISING_EVENT_TYPE_NON_CONNECTABLE = 3;

    // Result type defined in bt stack.
    static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
    static final int SCAN_RESULT_TYPE_FULL = 2;

    // Delivery mode defined in bt stack.
    private static final int DELIVERY_MODE_IMMEDIATE = 0;
    private static final int DELIVERY_MODE_ON_FOUND = 1;
    private static final int DELIVERY_MODE_BATCH = 2;

    private final GattService mService;
    private final Map<Integer, AdvertiseClient> mAdvertiseClients;
    // Keep track of whether scan filters exist.
@@ -114,6 +125,7 @@ public class GattServiceStateMachine extends StateMachine {
    private final Idle mIdle;
    private final ScanStarting mScanStarting;
    private final AdvertiseStarting mAdvertiseStarting;
    // A count down latch used to block on stack callback. MUST reset before use.
    private CountDownLatch mCallbackLatch;

    // It's sad we need to maintain this.
@@ -160,9 +172,8 @@ public class GattServiceStateMachine extends StateMachine {
    }

    void initFilterIndexStack() {
        int maxFiltersSupported = 16;
                // AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
        log("maxFiltersSupported = " + maxFiltersSupported);
        int maxFiltersSupported =
                AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
        for (int i = 1; i < maxFiltersSupported; ++i) {
            mFilterIndexStack.add(i);
        }
@@ -198,7 +209,6 @@ public class GattServiceStateMachine extends StateMachine {
                    transitionTo(mScanStarting);
                    break;
                case STOP_BLE_SCAN:
                    // Note this should only happen no client is doing scans any more.
                    int clientIf = message.arg1;
                    resetCallbackLatch();
                    gattClientScanFilterParamClearAllNative(clientIf);
@@ -295,8 +305,12 @@ public class GattServiceStateMachine extends StateMachine {
                    resetCallbackLatch();
                    gattClientScanFilterEnableNative(clientIf, true);
                    waitForCallback();
                    Set<ScanFilter> filters = (Set<ScanFilter>) message.obj;
                    if (filters != null && filters.size() <= mFilterIndexStack.size()) {
                    ScanClient client = (ScanClient) message.obj;
                    if (client != null) {
                        Set<ScanFilter> filters = new HashSet<ScanFilter>(client.filters);
                        // TODO: add ALLOW_ALL filter.
                        if (filters != null && !filters.isEmpty() &&
                                filters.size() <= mFilterIndexStack.size()) {
                            Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
                            for (ScanFilter filter : filters) {
                                ScanFilterQueue queue = new ScanFilterQueue();
@@ -309,18 +323,26 @@ public class GattServiceStateMachine extends StateMachine {
                                    waitForCallback();
                                }
                                resetCallbackLatch();
                                int deliveryMode = getDeliveryMode(client.settings);
                                logd("deliveryMode : " + deliveryMode);
                                int listLogicType = 0x1111111;
                                int filterLogicType = 1;
                                int rssiThreshold = Byte.MIN_VALUE;
                                gattClientScanFilterParamAddNative(
                                    clientIf, filterIndex, featureSelection, 0x1111111, 1, -127,
                                    -127, 0, 0, 0, 0);
                                        clientIf, filterIndex, featureSelection, listLogicType,
                                        filterLogicType, rssiThreshold, rssiThreshold, deliveryMode,
                                        0, 0, 0);
                                waitForCallback();
                                clientFilterIndices.add(filterIndex);
                            }
                            mClientFilterIndexMap.put(clientIf, clientFilterIndices);
                        }
                    sendMessage(ENABLE_BLE_SCAN);
                    }
                    sendMessage(ENABLE_BLE_SCAN, client);
                    break;
                case ENABLE_BLE_SCAN:
                    gattClientScanNative(true);
                    client = (ScanClient) message.obj;
                    enableBleScan(client);
                    removeMessages(OPERATION_TIMEOUT);
                    transitionTo(mIdle);
                    break;
@@ -335,6 +357,33 @@ public class GattServiceStateMachine extends StateMachine {

    }

    private void enableBleScan(ScanClient client) {
        if (client == null || client.settings == null
                || client.settings.getReportDelayNanos() == 0) {
            logd("enabling ble scan, appIf " + client.appIf);
            gattClientScanNative(true);
            return;
        }
        int fullScanPercent = 20;
        int notifyThreshold = 95;
        resetCallbackLatch();
        if (DBG)
            logd("configuring batch scan storage, appIf " + client.appIf);
        gattClientConfigBatchScanStorageNative(client.appIf, fullScanPercent,
                100 - fullScanPercent, notifyThreshold);
        waitForCallback();
        int scanMode = getResultType(client.settings);
        // TODO: configure scan parameters.
        int scanIntervalUnit = 8;
        int scanWindowUnit = 8;
        int discardRule = 2;
        int addressType = 0;
        logd("Starting BLE batch scan");
        gattClientStartBatchScanNative(client.appIf, scanMode, scanIntervalUnit, scanWindowUnit,
                addressType,
                discardRule);
    }

    private void resetCallbackLatch() {
        mCallbackLatch = new CountDownLatch(1);
    }
@@ -355,21 +404,18 @@ public class GattServiceStateMachine extends StateMachine {
        log("addFilterToController: " + entry.type);
        switch (entry.type) {
            case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
                // TBD appropriate params need to be passed here
                log("add address " + entry.address);
                gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0, 0,
                        "", entry.address, (byte) 0, new byte[0], new byte[0]);
                break;

            case ScanFilterQueue.TYPE_SERVICE_DATA:
                // TBD appropriate params need to be passed here
                gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0, 0,
                        "", "", (byte) 0, entry.data, entry.data_mask);
                break;

            case ScanFilterQueue.TYPE_SERVICE_UUID:
            case ScanFilterQueue.TYPE_SOLICIT_UUID:
                // TBD appropriate params need to be passed here
                gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
                        entry.uuid.getLeastSignificantBits(),
                        entry.uuid.getMostSignificantBits(),
@@ -379,7 +425,6 @@ public class GattServiceStateMachine extends StateMachine {
                break;

            case ScanFilterQueue.TYPE_LOCAL_NAME:
                // TBD appropriate params need to be passed here
                loge("adding filters: " + entry.name);
                gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0, 0,
                        entry.name, "", (byte) 0, new byte[0], new byte[0]);
@@ -390,7 +435,6 @@ public class GattServiceStateMachine extends StateMachine {
                int len = entry.data.length;
                if (entry.data_mask.length != len)
                    return;
                // TBD appropriate params need to be passed here
                gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
                        entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
                        entry.data, entry.data_mask);
@@ -545,6 +589,28 @@ public class GattServiceStateMachine extends StateMachine {
        }
    }

    /**
     * Return batch scan result type value defined in bt stack.
     */
    int getResultType(ScanSettings settings) {
        return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL ?
                SCAN_RESULT_TYPE_FULL : SCAN_RESULT_TYPE_TRUNCATED;
    }

    // Get delivery mode based on scan settings.
    private int getDeliveryMode(ScanSettings settings) {
        if (settings == null) {
            return DELIVERY_MODE_IMMEDIATE;
        }
        // TODO: double check whether it makes sense to use the same delivery mode for found and
        // lost.
        if (settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ON_FOUND ||
                settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ON_LOST) {
            return DELIVERY_MODE_ON_FOUND;
        }
        return settings.getReportDelayNanos() == 0 ? DELIVERY_MODE_IMMEDIATE : DELIVERY_MODE_BATCH;
    }

    private long millsToUnit(int millisecond) {
        return TimeUnit.MILLISECONDS.toMicros(millisecond) / ADVERTISING_INTERVAL_MICROS_PER_UNIT;
    }
@@ -605,6 +671,4 @@ public class GattServiceStateMachine extends StateMachine {
            int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);

    private native void gattClientStopBatchScanNative(int client_if);

    private native void gattClientReadScanReportsNative(int client_if, int scan_type);
}