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

Commit 90a85aea authored by Jayden Kim's avatar Jayden Kim Committed by Gerrit Code Review
Browse files

Merge changes from topic "adv_logger_aosp"

* changes:
  Fix Bluetooth LE advertising logger for enableAdvertisingSet
  Fix the BT settings is crashing after Toggle OFF/ON
  Add Bluetooth LE advertising logger
parents a80d0f65 7fc66d2c
Loading
Loading
Loading
Loading
+52 −8
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.os.RemoteException;
import android.util.Log;

import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.gatt.GattService.AdvertiserMap;

import java.util.Collections;
import java.util.HashMap;
@@ -46,6 +47,7 @@ class AdvertiseManager {

    private final GattService mService;
    private final AdapterService mAdapterService;
    private final AdvertiserMap mAdvertiserMap;
    private Handler mHandler;
    Map<IBinder, AdvertiserInfo> mAdvertisers = Collections.synchronizedMap(new HashMap<>());
    static int sTempRegistrationId = -1;
@@ -53,12 +55,14 @@ class AdvertiseManager {
    /**
     * Constructor of {@link AdvertiseManager}.
     */
    AdvertiseManager(GattService service, AdapterService adapterService) {
    AdvertiseManager(GattService service, AdapterService adapterService,
            AdvertiserMap advertiserMap) {
        if (DBG) {
            Log.d(TAG, "advertise manager created");
        }
        mService = service;
        mAdapterService = adapterService;
        mAdvertiserMap = advertiserMap;
    }

    /**
@@ -157,10 +161,18 @@ class AdvertiseManager {
        if (status == 0) {
            entry.setValue(
                    new AdvertiserInfo(advertiserId, entry.getValue().deathRecipient, callback));

            mAdvertiserMap.setAdvertiserIdByRegId(regId, advertiserId);
        } else {
            IBinder binder = entry.getKey();
            binder.unlinkToDeath(entry.getValue().deathRecipient, 0);
            mAdvertisers.remove(binder);

            AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId);
            if (stats != null) {
                stats.recordAdvertiseStop();
            }
            mAdvertiserMap.removeAppAdvertiseStats(regId);
        }

        callback.onAdvertisingSetStarted(advertiserId, txPower, status);
@@ -181,6 +193,13 @@ class AdvertiseManager {

        IAdvertisingSetCallback callback = entry.getValue().callback;
        callback.onAdvertisingEnabled(advertiserId, enable, status);

        if (!enable && status != 0) {
            AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
            if (stats != null) {
                stats.recordAdvertiseStop();
            }
        }
    }

    void startAdvertisingSet(AdvertisingSetParameters parameters, AdvertiseData advertiseData,
@@ -209,8 +228,13 @@ class AdvertiseManager {
            if (DBG) {
                Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder);
            }
        startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes, periodicParameters,
                periodicDataBytes, duration, maxExtAdvEvents, cbId);

            mAdvertiserMap.add(cbId, callback, mService);
            mAdvertiserMap.recordAdvertiseStart(cbId, parameters, advertiseData,
                    scanResponse, periodicParameters, periodicData, duration, maxExtAdvEvents);

            startAdvertisingSetNative(parameters, advDataBytes, scanResponseBytes,
                    periodicParameters, periodicDataBytes, duration, maxExtAdvEvents, cbId);

        } catch (IllegalArgumentException e) {
            try {
@@ -276,6 +300,8 @@ class AdvertiseManager {
        } catch (RemoteException e) {
            Log.i(TAG, "error sending onAdvertisingSetStopped callback", e);
        }

        mAdvertiserMap.recordAdvertiseStop(advertiserId);
    }

    void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) {
@@ -285,6 +311,9 @@ class AdvertiseManager {
            return;
        }
        enableAdvertisingSetNative(advertiserId, enable, duration, maxExtAdvEvents);

        mAdvertiserMap.enableAdvertisingSet(advertiserId,
                enable, duration, maxExtAdvEvents);
    }

    void setAdvertisingData(int advertiserId, AdvertiseData data) {
@@ -297,6 +326,8 @@ class AdvertiseManager {
        try {
            setAdvertisingDataNative(advertiserId,
                    AdvertiseHelper.advertiseDataToBytes(data, deviceName));

            mAdvertiserMap.setAdvertisingData(advertiserId, data);
        } catch (IllegalArgumentException e) {
            try {
                onAdvertisingDataSet(advertiserId,
@@ -317,6 +348,8 @@ class AdvertiseManager {
        try {
            setScanResponseDataNative(advertiserId,
                    AdvertiseHelper.advertiseDataToBytes(data, deviceName));

            mAdvertiserMap.setScanResponseData(advertiserId, data);
        } catch (IllegalArgumentException e) {
            try {
                onScanResponseDataSet(advertiserId,
@@ -334,6 +367,8 @@ class AdvertiseManager {
            return;
        }
        setAdvertisingParametersNative(advertiserId, parameters);

        mAdvertiserMap.setAdvertisingParameters(advertiserId, parameters);
    }

    void setPeriodicAdvertisingParameters(int advertiserId,
@@ -344,6 +379,8 @@ class AdvertiseManager {
            return;
        }
        setPeriodicAdvertisingParametersNative(advertiserId, parameters);

        mAdvertiserMap.setPeriodicAdvertisingParameters(advertiserId, parameters);
    }

    void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) {
@@ -356,6 +393,8 @@ class AdvertiseManager {
        try {
            setPeriodicAdvertisingDataNative(advertiserId,
                    AdvertiseHelper.advertiseDataToBytes(data, deviceName));

            mAdvertiserMap.setPeriodicAdvertisingData(advertiserId, data);
        } catch (IllegalArgumentException e) {
            try {
                onPeriodicAdvertisingDataSet(advertiserId,
@@ -473,6 +512,11 @@ class AdvertiseManager {

        IAdvertisingSetCallback callback = entry.getValue().callback;
        callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status);

        AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
        if (stats != null) {
            stats.onPeriodicAdvertiseEnabled(enable);
        }
    }

    static {
+385 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.bluetooth.gatt;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.os.ParcelUuid;
import android.util.SparseArray;

import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * ScanStats class helps keep track of information about scans
 * on a per application basis.
 * @hide
 */
/*package*/ class AppAdvertiseStats {
    private static final String TAG = AppAdvertiseStats.class.getSimpleName();

    private static DateTimeFormatter sDateFormat = DateTimeFormatter.ofPattern("MM-dd HH:mm:ss")
            .withZone(ZoneId.systemDefault());

    static final String[] PHY_LE_STRINGS = {"LE_1M", "LE_2M", "LE_CODED"};
    static final int UUID_STRING_FILTER_LEN = 8;

    // ContextMap here is needed to grab Apps and Connections
    ContextMap mContextMap;

    // GattService is needed to add scan event protos to be dumped later
    GattService mGattService;

    static class AppAdvertiserData {
        public boolean includeDeviceName = false;
        public boolean includeTxPowerLevel = false;
        public SparseArray<byte[]> manufacturerData;
        public Map<ParcelUuid, byte[]> serviceData;
        public List<ParcelUuid> serviceUuids;
        AppAdvertiserData(boolean includeDeviceName, boolean includeTxPowerLevel,
                SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData,
                List<ParcelUuid> serviceUuids) {
            this.includeDeviceName = includeDeviceName;
            this.includeTxPowerLevel = includeTxPowerLevel;
            this.manufacturerData = manufacturerData;
            this.serviceData = serviceData;
            this.serviceUuids = serviceUuids;
        }
    }

    static class AppAdvertiserRecord {
        public Instant startTime = null;
        public Instant stopTime = null;
        public int duration = 0;
        public int maxExtendedAdvertisingEvents = 0;
        AppAdvertiserRecord(Instant startTime) {
            this.startTime = startTime;
        }
    }

    private int mAppUid;
    private String mAppName;
    private int mId;
    private boolean mAdvertisingEnabled = false;
    private boolean mPeriodicAdvertisingEnabled = false;
    private int mPrimaryPhy = BluetoothDevice.PHY_LE_1M;
    private int mSecondaryPhy = BluetoothDevice.PHY_LE_1M;
    private int mInterval = 0;
    private int mTxPowerLevel = 0;
    private boolean mLegacy = false;
    private boolean mAnonymous = false;
    private boolean mConnectable = false;
    private boolean mScannable = false;
    private AppAdvertiserData mAdvertisingData = null;
    private AppAdvertiserData mScanResponseData = null;
    private AppAdvertiserData mPeriodicAdvertisingData = null;
    private boolean mPeriodicIncludeTxPower = false;
    private int mPeriodicInterval = 0;
    public ArrayList<AppAdvertiserRecord> mAdvertiserRecords =
            new ArrayList<AppAdvertiserRecord>();

    AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) {
        this.mAppUid = appUid;
        this.mId = id;
        this.mAppName = name;
        this.mContextMap = map;
        this.mGattService = service;
    }

    void recordAdvertiseStart(AdvertisingSetParameters parameters,
            AdvertiseData advertiseData, AdvertiseData scanResponse,
            PeriodicAdvertisingParameters periodicParameters, AdvertiseData periodicData,
            int duration, int maxExtAdvEvents) {
        mAdvertisingEnabled = true;
        AppAdvertiserRecord record = new AppAdvertiserRecord(Instant.now());
        record.duration = duration;
        record.maxExtendedAdvertisingEvents = maxExtAdvEvents;
        mAdvertiserRecords.add(record);
        if (mAdvertiserRecords.size() > 5) {
            mAdvertiserRecords.remove(0);
        }

        if (parameters != null) {
            mPrimaryPhy = parameters.getPrimaryPhy();
            mSecondaryPhy = parameters.getSecondaryPhy();
            mInterval = parameters.getInterval();
            mTxPowerLevel = parameters.getTxPowerLevel();
            mLegacy = parameters.isLegacy();
            mAnonymous = parameters.isAnonymous();
            mConnectable = parameters.isConnectable();
            mScannable = parameters.isScannable();
        }

        if (advertiseData != null) {
            mAdvertisingData = new AppAdvertiserData(advertiseData.getIncludeDeviceName(),
                    advertiseData.getIncludeTxPowerLevel(),
                    advertiseData.getManufacturerSpecificData(),
                    advertiseData.getServiceData(),
                    advertiseData.getServiceUuids());
        }

        if (scanResponse != null) {
            mScanResponseData = new AppAdvertiserData(scanResponse.getIncludeDeviceName(),
                    scanResponse.getIncludeTxPowerLevel(),
                    scanResponse.getManufacturerSpecificData(),
                    scanResponse.getServiceData(),
                    scanResponse.getServiceUuids());
        }

        if (periodicData != null) {
            mPeriodicAdvertisingData = new AppAdvertiserData(
                    periodicData.getIncludeDeviceName(),
                    periodicData.getIncludeTxPowerLevel(),
                    periodicData.getManufacturerSpecificData(),
                    periodicData.getServiceData(),
                    periodicData.getServiceUuids());
        }

        if (periodicParameters != null) {
            mPeriodicAdvertisingEnabled = true;
            mPeriodicIncludeTxPower = periodicParameters.getIncludeTxPower();
            mPeriodicInterval = periodicParameters.getInterval();
        }
    }

    void recordAdvertiseStart(int duration, int maxExtAdvEvents) {
        recordAdvertiseStart(null, null, null, null, null, duration, maxExtAdvEvents);
    }

    void recordAdvertiseStop() {
        mAdvertisingEnabled = false;
        mPeriodicAdvertisingEnabled = false;
        if (!mAdvertiserRecords.isEmpty()) {
            AppAdvertiserRecord record = mAdvertiserRecords.get(mAdvertiserRecords.size() - 1);
            record.stopTime = Instant.now();
        }
    }

    void enableAdvertisingSet(boolean enable, int duration, int maxExtAdvEvents) {
        if (enable) {
            //if the advertisingSet have not been disabled, skip enabling.
            if (!mAdvertisingEnabled) {
                recordAdvertiseStart(duration, maxExtAdvEvents);
            }
        } else {
            //if the advertisingSet have not been enabled, skip disabling.
            if (mAdvertisingEnabled) {
                recordAdvertiseStop();
            }
        }
    }

    void setAdvertisingData(AdvertiseData data) {
        if (mAdvertisingData == null) {
            mAdvertisingData = new AppAdvertiserData(data.getIncludeDeviceName(),
                    data.getIncludeTxPowerLevel(),
                    data.getManufacturerSpecificData(),
                    data.getServiceData(),
                    data.getServiceUuids());
        } else if (data != null) {
            mAdvertisingData.includeDeviceName = data.getIncludeDeviceName();
            mAdvertisingData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
            mAdvertisingData.manufacturerData = data.getManufacturerSpecificData();
            mAdvertisingData.serviceData = data.getServiceData();
            mAdvertisingData.serviceUuids = data.getServiceUuids();
        }
    }

    void setScanResponseData(AdvertiseData data) {
        if (mScanResponseData == null) {
            mScanResponseData = new AppAdvertiserData(data.getIncludeDeviceName(),
                    data.getIncludeTxPowerLevel(),
                    data.getManufacturerSpecificData(),
                    data.getServiceData(),
                    data.getServiceUuids());
        } else if (data != null) {
            mScanResponseData.includeDeviceName = data.getIncludeDeviceName();
            mScanResponseData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
            mScanResponseData.manufacturerData = data.getManufacturerSpecificData();
            mScanResponseData.serviceData = data.getServiceData();
            mScanResponseData.serviceUuids = data.getServiceUuids();
        }
    }

    void setAdvertisingParameters(AdvertisingSetParameters parameters) {
        if (parameters != null) {
            mPrimaryPhy = parameters.getPrimaryPhy();
            mSecondaryPhy = parameters.getSecondaryPhy();
            mInterval = parameters.getInterval();
            mTxPowerLevel = parameters.getTxPowerLevel();
            mLegacy = parameters.isLegacy();
            mAnonymous = parameters.isAnonymous();
            mConnectable = parameters.isConnectable();
            mScannable = parameters.isScannable();
        }
    }

    void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
        if (parameters != null) {
            mPeriodicIncludeTxPower = parameters.getIncludeTxPower();
            mPeriodicInterval = parameters.getInterval();
        }
    }

    void setPeriodicAdvertisingData(AdvertiseData data) {
        if (mPeriodicAdvertisingData == null) {
            mPeriodicAdvertisingData = new AppAdvertiserData(data.getIncludeDeviceName(),
                    data.getIncludeTxPowerLevel(),
                    data.getManufacturerSpecificData(),
                    data.getServiceData(),
                    data.getServiceUuids());
        } else if (data != null) {
            mPeriodicAdvertisingData.includeDeviceName = data.getIncludeDeviceName();
            mPeriodicAdvertisingData.includeTxPowerLevel = data.getIncludeTxPowerLevel();
            mPeriodicAdvertisingData.manufacturerData = data.getManufacturerSpecificData();
            mPeriodicAdvertisingData.serviceData = data.getServiceData();
            mPeriodicAdvertisingData.serviceUuids = data.getServiceUuids();
        }
    }

    void onPeriodicAdvertiseEnabled(boolean enable) {
        mPeriodicAdvertisingEnabled = enable;
    }

    void setId(int id) {
        this.mId = id;
    }

    private static String printByteArrayInHex(byte[] data) {
        final StringBuilder hex = new StringBuilder();
        for (byte b : data) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();
    }

    private static void dumpAppAdvertiserData(StringBuilder sb, AppAdvertiserData advData) {
        sb.append("\n          └Include Device Name                          : "
                + advData.includeDeviceName);
        sb.append("\n          └Include Tx Power Level                       : "
                + advData.includeTxPowerLevel);

        if (advData.manufacturerData.size() > 0) {
            sb.append("\n          └Manufacturer Data (length of data)           : "
                    + advData.manufacturerData.size());
        }

        if (!advData.serviceData.isEmpty()) {
            sb.append("\n          └Service Data(UUID, length of data)           : ");
            for (ParcelUuid uuid : advData.serviceData.keySet()) {
                sb.append("\n            [" + uuid.toString().substring(0, UUID_STRING_FILTER_LEN)
                        + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx, "
                        + advData.serviceData.get(uuid).length + "]");
            }
        }

        if (!advData.serviceUuids.isEmpty()) {
            sb.append("\n          └Service Uuids                                : \n            "
                    + advData.serviceUuids.toString().substring(0, UUID_STRING_FILTER_LEN)
                    + "-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
        }
    }

    private static String dumpPhyString(int phy) {
        if (phy > PHY_LE_STRINGS.length) {
            return Integer.toString(phy);
        } else {
            return PHY_LE_STRINGS[phy - 1];
        }
    }

    private static void dumpAppAdvertiseStats(StringBuilder sb, AppAdvertiseStats stats) {
        sb.append("\n      └Advertising:");
        sb.append("\n        └Interval(0.625ms)                              : "
                + stats.mInterval);
        sb.append("\n        └TX POWER(dbm)                                  : "
                + stats.mTxPowerLevel);
        sb.append("\n        └Primary Phy                                    : "
                + dumpPhyString(stats.mPrimaryPhy));
        sb.append("\n        └Secondary Phy                                  : "
                + dumpPhyString(stats.mSecondaryPhy));
        sb.append("\n        └Legacy                                         : "
                + stats.mLegacy);
        sb.append("\n        └Anonymous                                      : "
                + stats.mAnonymous);
        sb.append("\n        └Connectable                                    : "
                + stats.mConnectable);
        sb.append("\n        └Scannable                                      : "
                + stats.mScannable);

        if (stats.mAdvertisingData != null) {
            sb.append("\n        └Advertise Data:");
            dumpAppAdvertiserData(sb, stats.mAdvertisingData);
        }

        if (stats.mScanResponseData != null) {
            sb.append("\n        └Scan Response:");
            dumpAppAdvertiserData(sb, stats.mScanResponseData);
        }

        if (stats.mPeriodicInterval > 0) {
            sb.append("\n      └Periodic Advertising Enabled                     : "
                    + stats.mPeriodicAdvertisingEnabled);
            sb.append("\n        └Periodic Include TxPower                       : "
                    + stats.mPeriodicIncludeTxPower);
            sb.append("\n        └Periodic Interval(1.25ms)                      : "
                    + stats.mPeriodicInterval);
        }

        if (stats.mPeriodicAdvertisingData != null) {
            sb.append("\n        └Periodic Advertise Data:");
            dumpAppAdvertiserData(sb, stats.mPeriodicAdvertisingData);
        }

        sb.append("\n");
    }

    static void dumpToString(StringBuilder sb, AppAdvertiseStats stats) {
        Instant currentTime = Instant.now();

        sb.append("\n    " + stats.mAppName);
        sb.append("\n     Advertising ID                                     : "
                + stats.mId);
        for (int i = 0; i < stats.mAdvertiserRecords.size(); i++) {
            AppAdvertiserRecord record = stats.mAdvertiserRecords.get(i);

            sb.append("\n      " + (i + 1) + ":");
            sb.append("\n        └Start time                                     : "
                    + sDateFormat.format(record.startTime));
            if (record.stopTime == null) {
                Duration timeElapsed = Duration.between(record.startTime, currentTime);
                sb.append("\n        └Elapsed time                                   : "
                        + timeElapsed.toMillis() + "ms");
            } else {
                sb.append("\n        └Stop time                                      : "
                        + sDateFormat.format(record.stopTime));
            }
            sb.append("\n        └Duration(10ms unit)                            : "
                    + record.duration);
            sb.append("\n        └Maximum number of extended advertising events  : "
                    + record.maxExtendedAdvertisingEvents);
        }

        dumpAppAdvertiseStats(sb, stats);
    }
}
+187 −2

File changed.

Preview size limit exceeded, changes collapsed.

+12 −1
Original line number Diff line number Diff line
@@ -221,6 +221,13 @@ public class GattService extends ProfileService {

    ScannerMap mScannerMap = new ScannerMap();

    /**
     * List of our registered advertisers.
     */
    static class AdvertiserMap extends ContextMap<IAdvertisingSetCallback, Void> {}

    private AdvertiserMap mAdvertiserMap = new AdvertiserMap();

    /**
     * List of our registered clients.
     */
@@ -323,7 +330,7 @@ public class GattService extends ProfileService {
        mBluetoothAdapterProxy = BluetoothAdapterProxy.getInstance();
        mCompanionManager = getSystemService(CompanionDeviceManager.class);
        mAppOps = getSystemService(AppOpsManager.class);
        mAdvertiseManager = new AdvertiseManager(this, mAdapterService);
        mAdvertiseManager = new AdvertiseManager(this, mAdapterService, mAdvertiserMap);
        mAdvertiseManager.start();

        mScanManager = new ScanManager(this, mAdapterService, mBluetoothAdapterProxy);
@@ -343,6 +350,7 @@ public class GattService extends ProfileService {
        }
        setGattService(null);
        mScannerMap.clear();
        mAdvertiserMap.clear();
        mClientMap.clear();
        mServerMap.clear();
        mHandleMap.clear();
@@ -4664,6 +4672,9 @@ public class GattService extends ProfileService {
        sb.append("GATT Scanner Map\n");
        mScannerMap.dump(sb);

        sb.append("GATT Advertiser Map\n");
        mAdvertiserMap.dumpAdvertiser(sb);

        sb.append("GATT Client Map\n");
        mClientMap.dump(sb);