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

Commit 3cf5c0cf authored by Liang Li's avatar Liang Li Committed by lianglli
Browse files

Add logging for BLE scan and adv new atoms

Bug: 326310753
Bug: 328303508
Test: atest BluetoothInstrumentationTests
Test: manual test with statsd_testdrive
Change-Id: Iee944f390634c88801921ef0bb998532ca3846dd
parent bfcf8aab
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -266,8 +266,8 @@ public class BluetoothMethodProxy {

    /** Proxies {@link AppAdvertiseStats}. */
    public AppAdvertiseStats createAppAdvertiseStats(
            int id, String name, ContextMap map, GattService service) {
        return new AppAdvertiseStats(id, name, map, service);
            int appUid, int id, String name, ContextMap map, GattService service) {
        return new AppAdvertiseStats(appUid, id, name, map, service);
    }

    /** Proxies {@link Thread#start()}. */
+82 −0
Original line number Diff line number Diff line
@@ -389,6 +389,88 @@ public class MetricsLogger {
        return matchedString;
    }

    /** Logs the app scan stats with app attribution when the app scan state changed. */
    public void logAppScanStateChanged(
            int[] uids,
            String[] tags,
            boolean enabled,
            boolean isFilterScan,
            boolean isCallbackScan,
            int scanCallBackType,
            int scanType,
            int scanMode,
            long reportDelayMillis,
            long scanDurationMillis,
            int numOngoingScan,
            boolean isScreenOn,
            boolean isAppDead) {
        BluetoothStatsLog.write(
                BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED,
                uids,
                tags,
                enabled,
                isFilterScan,
                isCallbackScan,
                scanCallBackType,
                scanType,
                scanMode,
                reportDelayMillis,
                scanDurationMillis,
                numOngoingScan,
                isScreenOn,
                isAppDead);
    }

    /** Logs the radio scan stats with app attribution when the radio scan stopped. */
    public void logRadioScanStopped(
            int[] uids,
            String[] tags,
            int scanType,
            int scanMode,
            long scanIntervalMillis,
            long scanWindowMillis,
            boolean isScreenOn,
            long scanDurationMillis) {
        BluetoothStatsLog.write(
                BluetoothStatsLog.LE_RADIO_SCAN_STOPPED,
                uids,
                tags,
                scanType,
                scanMode,
                scanIntervalMillis,
                scanWindowMillis,
                isScreenOn,
                scanDurationMillis);
    }

    /** Logs the advertise stats with app attribution when the advertise state changed. */
    public void logAdvStateChanged(
            int[] uids,
            String[] tags,
            boolean enabled,
            int interval,
            int txPowerLevel,
            boolean isConnectable,
            boolean isPeriodicAdvertisingEnabled,
            boolean hasScanResponse,
            boolean isExtendedAdv,
            int instanceCount,
            long advDurationMs) {
        BluetoothStatsLog.write(
                BluetoothStatsLog.LE_ADV_STATE_CHANGED,
                uids,
                tags,
                enabled,
                interval,
                txPowerLevel,
                isConnectable,
                isPeriodicAdvertisingEnabled,
                hasScanResponse,
                isExtendedAdv,
                instanceCount,
                advDurationMs);
    }

    protected String getAllowlistedDeviceNameHash(String deviceName) {
        List<String> wordBreakdownList = getWordBreakdownList(deviceName);
        String matchedString = getMatchedString(wordBreakdownList);
+3 −4
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.bluetooth.gatt;

import static android.bluetooth.BluetoothProtoEnums.LE_ADV_ERROR_ON_START_COUNT;

import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
@@ -169,10 +168,10 @@ public class AdvertiseManager {

            AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId);
            if (stats != null) {
                stats.recordAdvertiseStop();
                stats.recordAdvertiseStop(mAdvertisers.size());
                stats.recordAdvertiseErrorCount(status);
            }
            mAdvertiserMap.removeAppAdvertiseStats(regId);
            AppAdvertiseStats.recordAdvertiseErrorCount(LE_ADV_ERROR_ON_START_COUNT);
        }

        IBinder gattBinder = mService.getBinder();
@@ -204,7 +203,7 @@ public class AdvertiseManager {
        if (!enable && status != 0) {
            AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId);
            if (stats != null) {
                stats.recordAdvertiseStop();
                stats.recordAdvertiseStop(mAdvertisers.size());
            }
        }
    }
+103 −21
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.bluetooth.gatt;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSetCallback;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.os.ParcelUuid;
@@ -25,7 +26,9 @@ import android.util.SparseArray;

import androidx.annotation.VisibleForTesting;

import com.android.bluetooth.BluetoothStatsLog;
import com.android.bluetooth.btservice.MetricsLogger;
import com.android.bluetooth.flags.Flags;

import java.time.Duration;
import java.time.Instant;
@@ -35,7 +38,7 @@ 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. */
/** AdvStats class helps keep track of information about advertising on a per application basis. */
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public class AppAdvertiseStats {
    private static final String TAG = AppAdvertiseStats.class.getSimpleName();
@@ -84,6 +87,7 @@ public class AppAdvertiseStats {
        }
    }

    private int mAppUid;
    private String mAppName;
    private int mId;
    private boolean mAdvertisingEnabled = false;
@@ -104,7 +108,8 @@ public class AppAdvertiseStats {
    public ArrayList<AppAdvertiserRecord> mAdvertiserRecords = new ArrayList<AppAdvertiserRecord>();

    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public AppAdvertiseStats(int id, String name, ContextMap map, GattService service) {
    public AppAdvertiseStats(int appUid, int id, String name, ContextMap map, GattService service) {
        this.mAppUid = appUid;
        this.mId = id;
        this.mAppName = name;
        this.mContextMap = map;
@@ -118,7 +123,8 @@ public class AppAdvertiseStats {
            PeriodicAdvertisingParameters periodicParameters,
            AdvertiseData periodicData,
            int duration,
            int maxExtAdvEvents) {
            int maxExtAdvEvents,
            int instanceCount) {
        mAdvertisingEnabled = true;
        AppAdvertiserRecord record = new AppAdvertiserRecord(Instant.now());
        record.duration = duration;
@@ -174,20 +180,24 @@ public class AppAdvertiseStats {
            mPeriodicIncludeTxPower = periodicParameters.getIncludeTxPower();
            mPeriodicInterval = periodicParameters.getInterval();
        }
        recordAdvertiseEnableCount(true, mConnectable, mPeriodicAdvertisingEnabled);
        recordAdvertiseEnableCount(true, instanceCount, 0 /* durationMs */);
    }

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

    void recordAdvertiseStop() {
        recordAdvertiseEnableCount(false, mConnectable, mPeriodicAdvertisingEnabled);
    void recordAdvertiseStop(int instanceCount) {
        if (!mAdvertiserRecords.isEmpty()) {
            AppAdvertiserRecord record = mAdvertiserRecords.get(mAdvertiserRecords.size() - 1);
            record.stopTime = Instant.now();
            Duration duration = Duration.between(record.startTime, record.stopTime);
            recordAdvertiseDurationCount(duration, mConnectable, mPeriodicAdvertisingEnabled);
            recordAdvertiseEnableCount(
                    false,
                    instanceCount,
                    record.stopTime.toEpochMilli() - record.startTime.toEpochMilli());
        }
        mAdvertisingEnabled = false;
        mPeriodicAdvertisingEnabled = false;
@@ -206,23 +216,53 @@ public class AppAdvertiseStats {
        }
    }

    static void recordAdvertiseErrorCount(int key) {
        if (key != BluetoothProtoEnums.LE_ADV_ERROR_ON_START_COUNT) {
            return;
        }
        MetricsLogger.getInstance().cacheCount(key, 1);
    }

    void enableAdvertisingSet(boolean enable, int duration, int maxExtAdvEvents) {
    void recordAdvertiseErrorCount(int status) {
        if (Flags.bleScanAdvMetricsRedesign()) {
            BluetoothStatsLog.write(
                    BluetoothStatsLog.LE_ADV_ERROR_REPORTED,
                    new int[] {mAppUid},
                    new String[] {mAppName},
                    BluetoothStatsLog.LE_ADV_ERROR_REPORTED__LE_ADV_OP_CODE__ERROR_CODE_ON_START,
                    convertStatusCode(status));
        }
        MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.LE_ADV_ERROR_ON_START_COUNT, 1);
    }

    private int convertStatusCode(int status) {
        switch (status) {
            case AdvertisingSetCallback.ADVERTISE_SUCCESS:
                return BluetoothStatsLog.LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_SUCCESS;
            case AdvertisingSetCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
                return BluetoothStatsLog
                        .LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_FAILED_DATA_TOO_LARGE;
            case AdvertisingSetCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
                return BluetoothStatsLog
                        .LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_FAILED_TOO_MANY_ADVERTISERS;
            case AdvertisingSetCallback.ADVERTISE_FAILED_ALREADY_STARTED:
                return BluetoothStatsLog
                        .LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_FAILED_ALREADY_STARTED;
            case AdvertisingSetCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
                return BluetoothStatsLog
                        .LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_FAILED_INTERNAL_ERROR;
            case AdvertisingSetCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
                return BluetoothStatsLog
                        .LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_FAILED_FEATURE_UNSUPPORTED;
            default:
                return BluetoothStatsLog.LE_ADV_ERROR_REPORTED__STATUS_CODE__ADV_STATUS_UNKNOWN;
        }
    }

    void enableAdvertisingSet(
            boolean enable, int duration, int maxExtAdvEvents, int instanceCount) {
        if (enable) {
            // if the advertisingSet have not been disabled, skip enabling.
            if (!mAdvertisingEnabled) {
                recordAdvertiseStart(duration, maxExtAdvEvents);
                recordAdvertiseStart(duration, maxExtAdvEvents, instanceCount);
            }
        } else {
            // if the advertisingSet have not been enabled, skip disabling.
            if (mAdvertisingEnabled) {
                recordAdvertiseStop();
                recordAdvertiseStop(instanceCount);
            }
        }
    }
@@ -369,31 +409,73 @@ public class AppAdvertiseStats {
        }
    }

    private static void recordAdvertiseEnableCount(
            boolean enable, boolean isConnectable, boolean inPeriodic) {
    private void recordAdvertiseEnableCount(boolean enable, int instanceCount, long durationMs) {
        if (Flags.bleScanAdvMetricsRedesign()) {
            MetricsLogger.getInstance()
                    .logAdvStateChanged(
                            new int[] {mAppUid},
                            new String[] {mAppName},
                            enable /* enabled */,
                            convertAdvInterval(mInterval),
                            convertTxPowerLevel(mTxPowerLevel),
                            mConnectable,
                            mPeriodicAdvertisingEnabled,
                            mScanResponseData != null && mScannable /* hasScanResponse */,
                            !mLegacy /* isExtendedAdv */,
                            instanceCount,
                            durationMs);
        }
        if (enable) {
            MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_ENABLE, 1);
            if (isConnectable) {
            if (mConnectable) {
                MetricsLogger.getInstance()
                        .cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_CONNECTABLE_ENABLE, 1);
            }
            if (inPeriodic) {
            if (mPeriodicAdvertisingEnabled) {
                MetricsLogger.getInstance()
                        .cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_PERIODIC_ENABLE, 1);
            }
        } else {
            MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_DISABLE, 1);
            if (isConnectable) {
            if (mConnectable) {
                MetricsLogger.getInstance()
                        .cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_CONNECTABLE_DISABLE, 1);
            }
            if (inPeriodic) {
            if (mPeriodicAdvertisingEnabled) {
                MetricsLogger.getInstance()
                        .cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_PERIODIC_DISABLE, 1);
            }
        }
    }

    private int convertAdvInterval(int interval) {
        switch (interval) {
            case AdvertisingSetParameters.INTERVAL_HIGH:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_INTERVAL__INTERVAL_HIGH;
            case AdvertisingSetParameters.INTERVAL_MEDIUM:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_INTERVAL__INTERVAL_MEDIUM;
            case AdvertisingSetParameters.INTERVAL_LOW:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_INTERVAL__INTERVAL_LOW;
            default:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_INTERVAL__INTERVAL_UNKNOWN;
        }
    }

    private int convertTxPowerLevel(int level) {
        switch (level) {
            case AdvertisingSetParameters.TX_POWER_ULTRA_LOW:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_TX_POWER__TX_POWER_ULTRA_LOW;
            case AdvertisingSetParameters.TX_POWER_LOW:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_TX_POWER__TX_POWER_LOW;
            case AdvertisingSetParameters.TX_POWER_MEDIUM:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_TX_POWER__TX_POWER_MEDIUM;
            case AdvertisingSetParameters.TX_POWER_HIGH:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_TX_POWER__TX_POWER_HIGH;
            default:
                return BluetoothStatsLog.LE_ADV_STATE_CHANGED__ADV_TX_POWER__TX_POWER_UNKNOWN;
        }
    }

    private static void dumpAppAdvertiserData(StringBuilder sb, AppAdvertiserData advData) {
        sb.append(
                "\n          └Include Device Name                          : "
+9 −7
Original line number Diff line number Diff line
@@ -252,7 +252,7 @@ public class ContextMap<C, T> {
                if (!mAppAdvertiseStats.containsKey(id)) {
                    AppAdvertiseStats appAdvertiseStats =
                            BluetoothMethodProxy.getInstance()
                                    .createAppAdvertiseStats(id, appName, this, service);
                                    .createAppAdvertiseStats(appUid, id, appName, this, service);
                    mAppAdvertiseStats.put(id, appAdvertiseStats);
                }
            }
@@ -450,6 +450,9 @@ public class ContextMap<C, T> {
            if (stats == null) {
                return;
            }
            int advertiseInstanceCount = mAppAdvertiseStats.size();
            Log.d(TAG, "advertiseInstanceCount is " + advertiseInstanceCount);
            AppAdvertiseStats.recordAdvertiseInstanceCount(advertiseInstanceCount);
            stats.recordAdvertiseStart(
                    parameters,
                    advertiseData,
@@ -457,10 +460,8 @@ public class ContextMap<C, T> {
                    periodicParameters,
                    periodicData,
                    duration,
                    maxExtAdvEvents);
            int advertiseInstanceCount = mAppAdvertiseStats.size();
            Log.d(TAG, "advertiseInstanceCount is " + advertiseInstanceCount);
            AppAdvertiseStats.recordAdvertiseInstanceCount(advertiseInstanceCount);
                    maxExtAdvEvents,
                    advertiseInstanceCount);
        }
    }

@@ -470,7 +471,7 @@ public class ContextMap<C, T> {
            if (stats == null) {
                return;
            }
            stats.recordAdvertiseStop();
            stats.recordAdvertiseStop(mAppAdvertiseStats.size());
            mAppAdvertiseStats.remove(id);
            mLastAdvertises.add(stats);
        }
@@ -482,7 +483,8 @@ public class ContextMap<C, T> {
            if (stats == null) {
                return;
            }
            stats.enableAdvertisingSet(enable, duration, maxExtAdvEvents);
            stats.enableAdvertisingSet(
                    enable, duration, maxExtAdvEvents, mAppAdvertiseStats.size());
        }
    }

Loading