1 /* 2 * Copyright (C) 2016 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.le_scan; 18 19 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; 20 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 21 22 import static java.util.Objects.requireNonNull; 23 import static java.util.Objects.requireNonNullElseGet; 24 25 import android.annotation.Nullable; 26 import android.bluetooth.BluetoothProtoEnums; 27 import android.bluetooth.le.ScanFilter; 28 import android.bluetooth.le.ScanSettings; 29 import android.os.BatteryStatsManager; 30 import android.os.Binder; 31 import android.os.WorkSource; 32 33 import com.android.bluetooth.BluetoothStatsLog; 34 import com.android.bluetooth.Utils.TimeProvider; 35 import com.android.bluetooth.btservice.AdapterService; 36 import com.android.bluetooth.btservice.MetricsLogger; 37 import com.android.bluetooth.util.WorkSourceUtil; 38 import com.android.internal.annotations.GuardedBy; 39 import com.android.internal.annotations.VisibleForTesting; 40 41 import java.text.DateFormat; 42 import java.text.SimpleDateFormat; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Date; 46 import java.util.HashMap; 47 import java.util.List; 48 import java.util.Locale; 49 import java.util.Map; 50 import java.util.Objects; 51 52 /** ScanStats class helps keep track of information about scans on a per application basis. */ 53 class AppScanStats { 54 private static final String TAG = AppScanStats.class.getSimpleName(); 55 56 // Weight is the duty cycle of the scan mode 57 static final int OPPORTUNISTIC_WEIGHT = 0; 58 static final int SCREEN_OFF_LOW_POWER_WEIGHT = 5; 59 static final int LOW_POWER_WEIGHT = 10; 60 static final int AMBIENT_DISCOVERY_WEIGHT = 25; 61 static final int BALANCED_WEIGHT = 25; 62 static final int LOW_LATENCY_WEIGHT = 100; 63 64 static final int LARGE_SCAN_TIME_GAP_MS = 24000; 65 66 static WorkSourceUtil sRadioScanWorkSourceUtil; 67 static int sRadioScanType; 68 static int sRadioScanMode; 69 static int sRadioScanWindowMs; 70 static int sRadioScanIntervalMs; 71 static boolean sIsRadioStarted = false; 72 static boolean sIsScreenOn = false; 73 static int sRadioScanAppImportance = IMPORTANCE_CACHED; 74 @Nullable static String sRadioScanAttributionTag; 75 76 @GuardedBy("sLock") 77 static long sRadioStartTime = 0; 78 79 private static final Object sLock = new Object(); 80 81 private static class LastScan { 82 public long duration; 83 public long suspendDuration; 84 public long suspendStartTime; 85 public boolean isSuspended; 86 public final long timestamp; 87 public final long reportDelayMillis; 88 public boolean isOpportunisticScan; 89 public boolean isTimeout; 90 public boolean isDowngraded; 91 public boolean isBackgroundScan; 92 public final boolean isFilterScan; 93 public final boolean isCallbackScan; 94 public boolean isBatchScan; 95 public boolean isAutoBatchScan; 96 public int results; 97 public final int scannerId; 98 public final int scanMode; 99 public final int scanCallbackType; 100 public final StringBuilder filterString; 101 @Nullable public final String attributionTag; 102 public final int appImportanceOnStart; 103 LastScan( long timestamp, long reportDelayMillis, boolean isFilterScan, boolean isCallbackScan, int scannerId, int scanMode, int scanCallbackType, @Nullable String attributionTag, int appImportanceOnStart)104 LastScan( 105 long timestamp, 106 long reportDelayMillis, 107 boolean isFilterScan, 108 boolean isCallbackScan, 109 int scannerId, 110 int scanMode, 111 int scanCallbackType, 112 @Nullable String attributionTag, 113 int appImportanceOnStart) { 114 this.duration = 0; 115 this.timestamp = timestamp; 116 this.reportDelayMillis = reportDelayMillis; 117 this.isOpportunisticScan = false; 118 this.isTimeout = false; 119 this.isDowngraded = false; 120 this.isBackgroundScan = false; 121 this.isFilterScan = isFilterScan; 122 this.isCallbackScan = isCallbackScan; 123 this.isBatchScan = false; 124 this.isAutoBatchScan = false; 125 this.scanMode = scanMode; 126 this.scanCallbackType = scanCallbackType; 127 this.attributionTag = attributionTag; 128 this.results = 0; 129 this.scannerId = scannerId; 130 this.suspendDuration = 0; 131 this.suspendStartTime = 0; 132 this.isSuspended = false; 133 this.appImportanceOnStart = appImportanceOnStart; 134 this.filterString = new StringBuilder(); 135 } 136 getAttributionTag()137 private String getAttributionTag() { 138 return attributionTag != null ? attributionTag : ""; 139 } 140 } 141 142 private final List<LastScan> mLastScans = new ArrayList<>(); 143 private final Map<Integer, LastScan> mOngoingScans = new HashMap<>(); 144 145 final String mAppName; 146 final ScannerMap mScannerMap; // Used to grab Apps 147 final BatteryStatsManager mBatteryStatsManager; // Used to keep track of scans and result stats 148 final ScanController mScanController; // Used to add scan event protos to be dumped later 149 150 private final WorkSource mWorkSource; // Used for BatteryStatsManager 151 private final WorkSourceUtil mWorkSourceUtil; // Used for BluetoothStatsLog 152 private final AdapterService mAdapterService; 153 private final TimeProvider mTimeProvider; 154 155 public boolean isAppDead = false; 156 public boolean isRegistered = false; 157 private int mScansStarted = 0; 158 private int mScansStopped = 0; 159 private long mScanStartTime = 0; 160 private long mTotalActiveTime = 0; 161 private long mTotalSuspendTime = 0; 162 private long mTotalScanTime = 0; 163 private long mOppScanTime = 0; 164 private long mLowPowerScanTime = 0; 165 private long mBalancedScanTime = 0; 166 private long mLowLatencyScanTime = 0; 167 private long mAmbientDiscoveryScanTime = 0; 168 private int mOppScan = 0; 169 private int mLowPowerScan = 0; 170 private int mBalancedScan = 0; 171 private int mLowLatencyScan = 0; 172 private int mAmbientDiscoveryScan = 0; 173 private int mAppImportance = IMPORTANCE_CACHED; 174 private long startTime = 0; 175 private int results = 0; 176 AppScanStats( String name, WorkSource source, ScannerMap map, AdapterService adapterService, ScanController scanController, TimeProvider timeProvider)177 AppScanStats( 178 String name, 179 WorkSource source, 180 ScannerMap map, 181 AdapterService adapterService, 182 ScanController scanController, 183 TimeProvider timeProvider) { 184 mAppName = name; 185 mWorkSource = 186 requireNonNullElseGet( 187 // Bill the caller if the work source isn't passed through 188 source, () -> new WorkSource(Binder.getCallingUid(), mAppName)); 189 mWorkSourceUtil = new WorkSourceUtil(mWorkSource); 190 mScannerMap = map; 191 mAdapterService = requireNonNull(adapterService); 192 mBatteryStatsManager = adapterService.getSystemService(BatteryStatsManager.class); 193 mScanController = scanController; 194 mTimeProvider = requireNonNull(timeProvider); 195 } 196 197 @Nullable getScanFromScannerId(int scannerId)198 private synchronized LastScan getScanFromScannerId(int scannerId) { 199 return mOngoingScans.get(scannerId); 200 } 201 addResult(int scannerId)202 synchronized void addResult(int scannerId) { 203 LastScan scan = getScanFromScannerId(scannerId); 204 if (scan != null) { 205 scan.results++; 206 207 // Only update battery stats after receiving 100 new results in order 208 // to lower the cost of the binder transaction 209 if (scan.results % 100 == 0) { 210 mBatteryStatsManager.reportBleScanResults(mWorkSource, 100); 211 BluetoothStatsLog.write( 212 BluetoothStatsLog.BLE_SCAN_RESULT_RECEIVED, 213 mWorkSourceUtil.getUids(), 214 mWorkSourceUtil.getTags(), 215 100); 216 } 217 } 218 219 results++; 220 } 221 isScanning()222 synchronized boolean isScanning() { 223 return !mOngoingScans.isEmpty(); 224 } 225 isScanTimeout(int scannerId)226 synchronized boolean isScanTimeout(int scannerId) { 227 LastScan scan = getScanFromScannerId(scannerId); 228 if (scan == null) { 229 return false; 230 } 231 return scan.isTimeout; 232 } 233 isScanDowngraded(int scannerId)234 synchronized boolean isScanDowngraded(int scannerId) { 235 LastScan scan = getScanFromScannerId(scannerId); 236 if (scan == null) { 237 return false; 238 } 239 return scan.isDowngraded; 240 } 241 isAutoBatchScan(int scannerId)242 synchronized boolean isAutoBatchScan(int scannerId) { 243 LastScan scan = getScanFromScannerId(scannerId); 244 if (scan == null) { 245 return false; 246 } 247 return scan.isAutoBatchScan; 248 } 249 setAppImportance(int importance)250 synchronized void setAppImportance(int importance) { 251 mAppImportance = importance; 252 } 253 recordScanStart( ScanSettings settings, List<ScanFilter> filters, boolean isFilterScan, boolean isCallbackScan, int scannerId, @Nullable String attributionTag)254 synchronized void recordScanStart( 255 ScanSettings settings, 256 List<ScanFilter> filters, 257 boolean isFilterScan, 258 boolean isCallbackScan, 259 int scannerId, 260 @Nullable String attributionTag) { 261 LastScan existingScan = getScanFromScannerId(scannerId); 262 if (existingScan != null) { 263 return; 264 } 265 this.mScansStarted++; 266 startTime = mTimeProvider.elapsedRealtime(); 267 268 LastScan scan = 269 new LastScan( 270 startTime, 271 settings.getReportDelayMillis(), 272 isFilterScan, 273 isCallbackScan, 274 scannerId, 275 settings.getScanMode(), 276 settings.getCallbackType(), 277 attributionTag, 278 mAppImportance); 279 if (settings != null) { 280 scan.isOpportunisticScan = scan.scanMode == ScanSettings.SCAN_MODE_OPPORTUNISTIC; 281 scan.isBackgroundScan = 282 (scan.scanCallbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; 283 scan.isBatchScan = 284 settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES 285 && settings.getReportDelayMillis() != 0; 286 switch (scan.scanMode) { 287 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 288 mOppScan++; 289 break; 290 case ScanSettings.SCAN_MODE_LOW_POWER: 291 mLowPowerScan++; 292 break; 293 case ScanSettings.SCAN_MODE_BALANCED: 294 mBalancedScan++; 295 break; 296 case ScanSettings.SCAN_MODE_LOW_LATENCY: 297 mLowLatencyScan++; 298 break; 299 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 300 mAmbientDiscoveryScan++; 301 break; 302 } 303 } 304 305 if (isFilterScan) { 306 for (ScanFilter filter : filters) { 307 scan.filterString 308 .append("\n └ ") 309 .append(filterToStringWithoutNullParam(filter)); 310 } 311 } 312 313 if (!isScanning()) { 314 mScanStartTime = startTime; 315 } 316 boolean isUnoptimized = 317 !(scan.isFilterScan || scan.isBackgroundScan || scan.isOpportunisticScan); 318 mBatteryStatsManager.reportBleScanStarted(mWorkSource, isUnoptimized); 319 BluetoothStatsLog.write( 320 BluetoothStatsLog.BLE_SCAN_STATE_CHANGED, 321 mWorkSourceUtil.getUids(), 322 mWorkSourceUtil.getTags(), 323 BluetoothStatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON, 324 scan.isFilterScan, 325 scan.isBackgroundScan, 326 scan.isOpportunisticScan); 327 recordScanAppCountMetricsStart(scan); 328 329 mOngoingScans.put(scannerId, scan); 330 } 331 recordScanStop(int scannerId)332 synchronized void recordScanStop(int scannerId) { 333 LastScan scan = getScanFromScannerId(scannerId); 334 if (scan == null) { 335 return; 336 } 337 this.mScansStopped++; 338 long stopTime = mTimeProvider.elapsedRealtime(); 339 long scanDuration = stopTime - scan.timestamp; 340 scan.duration = scanDuration; 341 if (scan.isSuspended) { 342 long suspendDuration = stopTime - scan.suspendStartTime; 343 scan.suspendDuration += suspendDuration; 344 mTotalSuspendTime += suspendDuration; 345 } 346 mOngoingScans.remove(scannerId); 347 if (mLastScans.size() >= mAdapterService.getScanQuotaCount()) { 348 mLastScans.remove(0); 349 } 350 mLastScans.add(scan); 351 352 mTotalScanTime += scanDuration; 353 long activeDuration = scanDuration - scan.suspendDuration; 354 mTotalActiveTime += activeDuration; 355 switch (scan.scanMode) { 356 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 357 mOppScanTime += activeDuration; 358 break; 359 case ScanSettings.SCAN_MODE_LOW_POWER: 360 mLowPowerScanTime += activeDuration; 361 break; 362 case ScanSettings.SCAN_MODE_BALANCED: 363 mBalancedScanTime += activeDuration; 364 break; 365 case ScanSettings.SCAN_MODE_LOW_LATENCY: 366 mLowLatencyScanTime += activeDuration; 367 break; 368 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 369 mAmbientDiscoveryScanTime += activeDuration; 370 break; 371 } 372 373 // Inform battery stats of any results it might be missing on scan stop 374 boolean isUnoptimized = 375 !(scan.isFilterScan || scan.isBackgroundScan || scan.isOpportunisticScan); 376 mBatteryStatsManager.reportBleScanResults(mWorkSource, scan.results % 100); 377 mBatteryStatsManager.reportBleScanStopped(mWorkSource, isUnoptimized); 378 BluetoothStatsLog.write( 379 BluetoothStatsLog.BLE_SCAN_RESULT_RECEIVED, 380 mWorkSourceUtil.getUids(), 381 mWorkSourceUtil.getTags(), 382 scan.results % 100); 383 BluetoothStatsLog.write( 384 BluetoothStatsLog.BLE_SCAN_STATE_CHANGED, 385 mWorkSourceUtil.getUids(), 386 mWorkSourceUtil.getTags(), 387 BluetoothStatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF, 388 scan.isFilterScan, 389 scan.isBackgroundScan, 390 scan.isOpportunisticScan); 391 recordScanAppCountMetricsStop(scan); 392 } 393 recordScanAppCountMetricsStart(LastScan scan)394 private void recordScanAppCountMetricsStart(LastScan scan) { 395 MetricsLogger logger = MetricsLogger.getInstance(); 396 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_TOTAL_ENABLE, 1); 397 logger.logAppScanStateChanged( 398 mWorkSourceUtil.getUids(), 399 mWorkSourceUtil.getTags(), 400 true /* enabled */, 401 scan.isFilterScan, 402 scan.isCallbackScan, 403 convertScanCallbackType(scan.scanCallbackType), 404 convertScanType(scan), 405 convertScanMode(scan.scanMode), 406 scan.reportDelayMillis, 407 0 /* app_scan_duration_ms */, 408 mOngoingScans.size(), 409 sIsScreenOn, 410 isAppDead, 411 mAppImportance, 412 scan.getAttributionTag()); 413 if (scan.isAutoBatchScan) { 414 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_AUTO_BATCH_ENABLE, 1); 415 } else if (scan.isBatchScan) { 416 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_BATCH_ENABLE, 1); 417 } else { 418 if (scan.isFilterScan) { 419 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_FILTERED_ENABLE, 1); 420 } else { 421 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_UNFILTERED_ENABLE, 1); 422 } 423 } 424 } 425 recordScanAppCountMetricsStop(LastScan scan)426 private void recordScanAppCountMetricsStop(LastScan scan) { 427 MetricsLogger logger = MetricsLogger.getInstance(); 428 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_TOTAL_DISABLE, 1); 429 logger.logAppScanStateChanged( 430 mWorkSourceUtil.getUids(), 431 mWorkSourceUtil.getTags(), 432 false /* enabled */, 433 scan.isFilterScan, 434 scan.isCallbackScan, 435 convertScanCallbackType(scan.scanCallbackType), 436 convertScanType(scan), 437 convertScanMode(scan.scanMode), 438 scan.reportDelayMillis, 439 scan.duration, 440 mOngoingScans.size(), 441 sIsScreenOn, 442 isAppDead, 443 mAppImportance, 444 scan.getAttributionTag()); 445 if (scan.isAutoBatchScan) { 446 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_AUTO_BATCH_DISABLE, 1); 447 } else if (scan.isBatchScan) { 448 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_BATCH_DISABLE, 1); 449 } else { 450 if (scan.isFilterScan) { 451 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_FILTERED_DISABLE, 1); 452 } else { 453 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_UNFILTERED_DISABLE, 1); 454 } 455 } 456 } 457 convertScanCallbackType(int type)458 private static int convertScanCallbackType(int type) { 459 switch (type) { 460 case ScanSettings.CALLBACK_TYPE_ALL_MATCHES: 461 return BluetoothStatsLog 462 .LE_APP_SCAN_STATE_CHANGED__SCAN_CALLBACK_TYPE__TYPE_ALL_MATCHES; 463 case ScanSettings.CALLBACK_TYPE_FIRST_MATCH: 464 return BluetoothStatsLog 465 .LE_APP_SCAN_STATE_CHANGED__SCAN_CALLBACK_TYPE__TYPE_FIRST_MATCH; 466 case ScanSettings.CALLBACK_TYPE_MATCH_LOST: 467 return BluetoothStatsLog 468 .LE_APP_SCAN_STATE_CHANGED__SCAN_CALLBACK_TYPE__TYPE_MATCH_LOST; 469 case ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH: 470 return BluetoothStatsLog 471 .LE_APP_SCAN_STATE_CHANGED__SCAN_CALLBACK_TYPE__TYPE_ALL_MATCHES_AUTO_BATCH; 472 default: 473 return BluetoothStatsLog 474 .LE_APP_SCAN_STATE_CHANGED__SCAN_CALLBACK_TYPE__TYPE_UNKNOWN; 475 } 476 } 477 convertScanType(LastScan scan)478 private static int convertScanType(LastScan scan) { 479 if (scan == null) { 480 return BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED__LE_SCAN_TYPE__SCAN_TYPE_UNKNOWN; 481 } 482 if (scan.isAutoBatchScan) { 483 return BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED__LE_SCAN_TYPE__SCAN_TYPE_AUTO_BATCH; 484 } else if (scan.isBatchScan) { 485 return BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED__LE_SCAN_TYPE__SCAN_TYPE_BATCH; 486 } else { 487 return BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED__LE_SCAN_TYPE__SCAN_TYPE_REGULAR; 488 } 489 } 490 491 @VisibleForTesting convertScanMode(int mode)492 static int convertScanMode(int mode) { 493 switch (mode) { 494 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 495 return BluetoothStatsLog 496 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_OPPORTUNISTIC; 497 case ScanSettings.SCAN_MODE_LOW_POWER: 498 return BluetoothStatsLog 499 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_LOW_POWER; 500 case ScanSettings.SCAN_MODE_BALANCED: 501 return BluetoothStatsLog 502 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_BALANCED; 503 case ScanSettings.SCAN_MODE_LOW_LATENCY: 504 return BluetoothStatsLog 505 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_LOW_LATENCY; 506 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 507 return BluetoothStatsLog 508 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_AMBIENT_DISCOVERY; 509 case ScanSettings.SCAN_MODE_SCREEN_OFF: 510 return BluetoothStatsLog 511 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_SCREEN_OFF; 512 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: 513 return BluetoothStatsLog 514 .LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_SCREEN_OFF_BALANCED; 515 default: 516 return BluetoothStatsLog.LE_APP_SCAN_STATE_CHANGED__LE_SCAN_MODE__SCAN_MODE_UNKNOWN; 517 } 518 } 519 recordScanTimeoutCountMetrics(int scannerId, long scanTimeoutMillis)520 synchronized void recordScanTimeoutCountMetrics(int scannerId, long scanTimeoutMillis) { 521 BluetoothStatsLog.write( 522 BluetoothStatsLog.LE_SCAN_ABUSED, 523 mWorkSourceUtil.getUids(), 524 mWorkSourceUtil.getTags(), 525 convertScanType(getScanFromScannerId(scannerId)), 526 BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_SCAN_TIMEOUT, 527 scanTimeoutMillis, 528 getAttributionTagFromScannerId(scannerId)); 529 MetricsLogger.getInstance() 530 .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_SCAN_TIMEOUT, 1); 531 } 532 recordHwFilterNotAvailableCountMetrics( int scannerId, long numOfFilterSupported)533 synchronized void recordHwFilterNotAvailableCountMetrics( 534 int scannerId, long numOfFilterSupported) { 535 BluetoothStatsLog.write( 536 BluetoothStatsLog.LE_SCAN_ABUSED, 537 mWorkSourceUtil.getUids(), 538 mWorkSourceUtil.getTags(), 539 convertScanType(getScanFromScannerId(scannerId)), 540 BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_HW_FILTER_NA, 541 numOfFilterSupported, 542 getAttributionTagFromScannerId(scannerId)); 543 MetricsLogger.getInstance() 544 .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_HW_FILTER_NOT_AVAILABLE, 1); 545 } 546 recordTrackingHwFilterNotAvailableCountMetrics( int scannerId, long numOfTrackableAdv)547 synchronized void recordTrackingHwFilterNotAvailableCountMetrics( 548 int scannerId, long numOfTrackableAdv) { 549 BluetoothStatsLog.write( 550 BluetoothStatsLog.LE_SCAN_ABUSED, 551 mWorkSourceUtil.getUids(), 552 mWorkSourceUtil.getTags(), 553 convertScanType(getScanFromScannerId(scannerId)), 554 BluetoothStatsLog 555 .LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_TRACKING_HW_FILTER_NA, 556 numOfTrackableAdv, 557 getAttributionTagFromScannerId(scannerId)); 558 MetricsLogger.getInstance() 559 .cacheCount( 560 BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_TRACKING_HW_FILTER_NOT_AVAILABLE, 561 1); 562 } 563 initScanRadioState()564 static void initScanRadioState() { 565 synchronized (sLock) { 566 sIsRadioStarted = false; 567 } 568 } 569 recordScanRadioStart( int scanMode, int scannerId, AppScanStats stats, int scanWindowMs, int scanIntervalMs, TimeProvider timeProvider)570 static boolean recordScanRadioStart( 571 int scanMode, 572 int scannerId, 573 AppScanStats stats, 574 int scanWindowMs, 575 int scanIntervalMs, 576 TimeProvider timeProvider) { 577 synchronized (sLock) { 578 if (sIsRadioStarted) { 579 return false; 580 } 581 sRadioStartTime = timeProvider.elapsedRealtime(); 582 sRadioScanWorkSourceUtil = stats.mWorkSourceUtil; 583 sRadioScanType = convertScanType(stats.getScanFromScannerId(scannerId)); 584 sRadioScanMode = scanMode; 585 sRadioScanWindowMs = scanWindowMs; 586 sRadioScanIntervalMs = scanIntervalMs; 587 sIsRadioStarted = true; 588 sRadioScanAppImportance = stats.mAppImportance; 589 sRadioScanAttributionTag = stats.getAttributionTagFromScannerId(scannerId); 590 } 591 return true; 592 } 593 recordScanRadioStop(TimeProvider timeProvider)594 static boolean recordScanRadioStop(TimeProvider timeProvider) { 595 synchronized (sLock) { 596 if (!sIsRadioStarted) { 597 return false; 598 } 599 recordScanRadioDurationMetrics(timeProvider); 600 } 601 return true; 602 } 603 604 @GuardedBy("sLock") recordScanRadioDurationMetrics(TimeProvider timeProvider)605 private static void recordScanRadioDurationMetrics(TimeProvider timeProvider) { 606 if (!sIsRadioStarted) { 607 return; 608 } 609 MetricsLogger logger = MetricsLogger.getInstance(); 610 long currentTime = timeProvider.elapsedRealtime(); 611 long radioScanDuration = currentTime - sRadioStartTime; 612 double scanWeight = getScanWeight(sRadioScanMode) * 0.01; 613 long weightedDuration = (long) (radioScanDuration * scanWeight); 614 615 logger.logRadioScanStopped( 616 getRadioScanUids(), 617 getRadioScanTags(), 618 sRadioScanType, 619 convertScanMode(sRadioScanMode), 620 sRadioScanIntervalMs, 621 sRadioScanWindowMs, 622 sIsScreenOn, 623 radioScanDuration, 624 sRadioScanAppImportance, 625 getRadioScanAttributionTag()); 626 sRadioStartTime = 0; 627 sIsRadioStarted = false; 628 if (weightedDuration > 0) { 629 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR, weightedDuration); 630 if (sIsScreenOn) { 631 logger.cacheCount( 632 BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR_SCREEN_ON, 633 weightedDuration); 634 } else { 635 logger.cacheCount( 636 BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR_SCREEN_OFF, 637 weightedDuration); 638 } 639 } 640 } 641 getRadioScanUids()642 private static int[] getRadioScanUids() { 643 synchronized (sLock) { 644 return sRadioScanWorkSourceUtil != null 645 ? sRadioScanWorkSourceUtil.getUids() 646 : new int[] {0}; 647 } 648 } 649 getRadioScanTags()650 private static String[] getRadioScanTags() { 651 synchronized (sLock) { 652 return sRadioScanWorkSourceUtil != null 653 ? sRadioScanWorkSourceUtil.getTags() 654 : new String[] {""}; 655 } 656 } 657 getRadioScanAttributionTag()658 private static String getRadioScanAttributionTag() { 659 synchronized (sLock) { 660 return sRadioScanAttributionTag != null ? sRadioScanAttributionTag : ""; 661 } 662 } 663 664 @GuardedBy("sLock") recordScreenOnOffMetrics(boolean isScreenOn)665 private static void recordScreenOnOffMetrics(boolean isScreenOn) { 666 if (isScreenOn) { 667 MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SCREEN_ON_EVENT, 1); 668 } else { 669 MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SCREEN_OFF_EVENT, 1); 670 } 671 } 672 getScanWeight(int scanMode)673 private static int getScanWeight(int scanMode) { 674 switch (scanMode) { 675 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 676 return OPPORTUNISTIC_WEIGHT; 677 case ScanSettings.SCAN_MODE_SCREEN_OFF: 678 return SCREEN_OFF_LOW_POWER_WEIGHT; 679 case ScanSettings.SCAN_MODE_LOW_POWER: 680 return LOW_POWER_WEIGHT; 681 case ScanSettings.SCAN_MODE_BALANCED: 682 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 683 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: 684 return BALANCED_WEIGHT; 685 case ScanSettings.SCAN_MODE_LOW_LATENCY: 686 return LOW_LATENCY_WEIGHT; 687 default: 688 return LOW_POWER_WEIGHT; 689 } 690 } 691 recordScanRadioResultCount()692 static void recordScanRadioResultCount() { 693 synchronized (sLock) { 694 if (!sIsRadioStarted) { 695 return; 696 } 697 BluetoothStatsLog.write( 698 BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, 699 getRadioScanUids(), 700 getRadioScanTags(), 701 1 /* num_results */, 702 BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_REGULAR, 703 sIsScreenOn, 704 getRadioScanAttributionTag()); 705 MetricsLogger logger = MetricsLogger.getInstance(); 706 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_REGULAR, 1); 707 if (sIsScreenOn) { 708 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_REGULAR_SCREEN_ON, 1); 709 } else { 710 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_REGULAR_SCREEN_OFF, 1); 711 } 712 } 713 } 714 recordBatchScanRadioResultCount(int numRecords)715 static void recordBatchScanRadioResultCount(int numRecords) { 716 boolean isScreenOn; 717 synchronized (sLock) { 718 isScreenOn = sIsScreenOn; 719 } 720 BluetoothStatsLog.write( 721 BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, 722 getRadioScanUids(), 723 getRadioScanTags(), 724 numRecords, 725 BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_BATCH, 726 sIsScreenOn, 727 getRadioScanAttributionTag()); 728 MetricsLogger logger = MetricsLogger.getInstance(); 729 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_BUNDLE, 1); 730 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH, numRecords); 731 if (isScreenOn) { 732 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_BUNDLE_SCREEN_ON, 1); 733 logger.cacheCount( 734 BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_SCREEN_ON, numRecords); 735 } else { 736 logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_BUNDLE_SCREEN_OFF, 1); 737 logger.cacheCount( 738 BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_SCREEN_OFF, numRecords); 739 } 740 } 741 setScreenState(boolean isScreenOn, TimeProvider timeProvider)742 static void setScreenState(boolean isScreenOn, TimeProvider timeProvider) { 743 synchronized (sLock) { 744 if (sIsScreenOn == isScreenOn) { 745 return; 746 } 747 if (sIsRadioStarted) { 748 recordScanRadioDurationMetrics(timeProvider); 749 sRadioStartTime = timeProvider.elapsedRealtime(); 750 } 751 recordScreenOnOffMetrics(isScreenOn); 752 sIsScreenOn = isScreenOn; 753 } 754 } 755 recordScanSuspend(int scannerId)756 synchronized void recordScanSuspend(int scannerId) { 757 LastScan scan = getScanFromScannerId(scannerId); 758 if (scan == null || scan.isSuspended) { 759 return; 760 } 761 scan.suspendStartTime = mTimeProvider.elapsedRealtime(); 762 scan.isSuspended = true; 763 } 764 recordScanResume(int scannerId)765 synchronized void recordScanResume(int scannerId) { 766 LastScan scan = getScanFromScannerId(scannerId); 767 if (scan == null || !scan.isSuspended) { 768 return; 769 } 770 scan.isSuspended = false; 771 long stopTime = mTimeProvider.elapsedRealtime(); 772 long suspendDuration = stopTime - scan.suspendStartTime; 773 scan.suspendDuration += suspendDuration; 774 mTotalSuspendTime += suspendDuration; 775 } 776 setScanTimeout(int scannerId)777 synchronized void setScanTimeout(int scannerId) { 778 if (!isScanning()) { 779 return; 780 } 781 782 LastScan scan = getScanFromScannerId(scannerId); 783 if (scan != null) { 784 scan.isTimeout = true; 785 } 786 } 787 setScanDowngrade(int scannerId, boolean isDowngrade)788 synchronized void setScanDowngrade(int scannerId, boolean isDowngrade) { 789 if (!isScanning()) { 790 return; 791 } 792 793 LastScan scan = getScanFromScannerId(scannerId); 794 if (scan != null) { 795 scan.isDowngraded = isDowngrade; 796 } 797 } 798 setAutoBatchScan(int scannerId, boolean isBatchScan)799 synchronized void setAutoBatchScan(int scannerId, boolean isBatchScan) { 800 LastScan scan = getScanFromScannerId(scannerId); 801 if (scan != null) { 802 scan.isAutoBatchScan = isBatchScan; 803 } 804 } 805 isScanningTooFrequently()806 synchronized boolean isScanningTooFrequently() { 807 if (mLastScans.size() < mAdapterService.getScanQuotaCount()) { 808 return false; 809 } 810 811 return (mTimeProvider.elapsedRealtime() - mLastScans.get(0).timestamp) 812 < mAdapterService.getScanQuotaWindowMillis(); 813 } 814 isScanningTooLong()815 synchronized boolean isScanningTooLong() { 816 if (!isScanning()) { 817 return false; 818 } 819 return (mTimeProvider.elapsedRealtime() - mScanStartTime) 820 >= mAdapterService.getScanTimeoutMillis(); 821 } 822 hasRecentScan()823 synchronized boolean hasRecentScan() { 824 if (!isScanning() || mLastScans.isEmpty()) { 825 return false; 826 } 827 LastScan lastScan = mLastScans.get(mLastScans.size() - 1); 828 return ((mTimeProvider.elapsedRealtime() - lastScan.duration - lastScan.timestamp) 829 < LARGE_SCAN_TIME_GAP_MS); 830 } 831 getAttributionTagFromScannerId(int scannerId)832 private String getAttributionTagFromScannerId(int scannerId) { 833 LastScan scan = getScanFromScannerId(scannerId); 834 return scan == null ? "" : scan.getAttributionTag(); 835 } 836 filterToStringWithoutNullParam(ScanFilter filter)837 private static String filterToStringWithoutNullParam(ScanFilter filter) { 838 StringBuilder filterString = new StringBuilder("BluetoothLeScanFilter ["); 839 if (filter.getDeviceName() != null) { 840 filterString.append(" DeviceName=").append(filter.getDeviceName()); 841 } 842 if (filter.getDeviceAddress() != null) { 843 filterString.append(" DeviceAddress=").append(filter.getDeviceAddress()); 844 } 845 if (filter.getServiceUuid() != null) { 846 filterString.append(" ServiceUuid=").append(filter.getServiceUuid()); 847 } 848 if (filter.getServiceUuidMask() != null) { 849 filterString.append(" ServiceUuidMask=").append(filter.getServiceUuidMask()); 850 } 851 if (filter.getServiceSolicitationUuid() != null) { 852 filterString 853 .append(" ServiceSolicitationUuid=") 854 .append(filter.getServiceSolicitationUuid()); 855 } 856 if (filter.getServiceSolicitationUuidMask() != null) { 857 filterString 858 .append(" ServiceSolicitationUuidMask=") 859 .append(filter.getServiceSolicitationUuidMask()); 860 } 861 if (filter.getServiceDataUuid() != null) { 862 filterString 863 .append(" ServiceDataUuid=") 864 .append(Objects.toString(filter.getServiceDataUuid())); 865 } 866 if (filter.getServiceData() != null) { 867 filterString.append(" ServiceData=").append(Arrays.toString(filter.getServiceData())); 868 } 869 if (filter.getServiceDataMask() != null) { 870 filterString 871 .append(" ServiceDataMask=") 872 .append(Arrays.toString(filter.getServiceDataMask())); 873 } 874 if (filter.getManufacturerId() >= 0) { 875 filterString.append(" ManufacturerId=").append(filter.getManufacturerId()); 876 } 877 if (filter.getManufacturerData() != null) { 878 filterString 879 .append(" ManufacturerData=") 880 .append(Arrays.toString(filter.getManufacturerData())); 881 } 882 if (filter.getManufacturerDataMask() != null) { 883 filterString 884 .append(" ManufacturerDataMask=") 885 .append(Arrays.toString(filter.getManufacturerDataMask())); 886 } 887 filterString.append(" ]"); 888 return filterString.toString(); 889 } 890 scanModeToString(int scanMode)891 private static String scanModeToString(int scanMode) { 892 switch (scanMode) { 893 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 894 return "OPPORTUNISTIC"; 895 case ScanSettings.SCAN_MODE_LOW_LATENCY: 896 return "LOW_LATENCY"; 897 case ScanSettings.SCAN_MODE_BALANCED: 898 return "BALANCED"; 899 case ScanSettings.SCAN_MODE_LOW_POWER: 900 return "LOW_POWER"; 901 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 902 return "AMBIENT_DISCOVERY"; 903 default: 904 return "UNKNOWN(" + scanMode + ")"; 905 } 906 } 907 callbackTypeToString(int callbackType)908 private static String callbackTypeToString(int callbackType) { 909 switch (callbackType) { 910 case ScanSettings.CALLBACK_TYPE_ALL_MATCHES: 911 return "ALL_MATCHES"; 912 case ScanSettings.CALLBACK_TYPE_FIRST_MATCH: 913 return "FIRST_MATCH"; 914 case ScanSettings.CALLBACK_TYPE_MATCH_LOST: 915 return "LOST"; 916 case ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH: 917 return "ALL_MATCHES_AUTO_BATCH"; 918 default: 919 return callbackType 920 == (ScanSettings.CALLBACK_TYPE_FIRST_MATCH 921 | ScanSettings.CALLBACK_TYPE_MATCH_LOST) 922 ? "[FIRST_MATCH | LOST]" 923 : "UNKNOWN: " + callbackType; 924 } 925 } 926 927 @SuppressWarnings("JavaUtilDate") // TODO: b/365629730 -- prefer Instant or LocalDate dumpToString(StringBuilder sb)928 public synchronized void dumpToString(StringBuilder sb) { 929 DateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm:ss", Locale.ROOT); 930 931 long currentTime = System.currentTimeMillis(); 932 long currTime = mTimeProvider.elapsedRealtime(); 933 long scanDuration = 0; 934 long suspendDuration = 0; 935 long activeDuration = 0; 936 long totalActiveTime = mTotalActiveTime; 937 long totalSuspendTime = mTotalSuspendTime; 938 long totalScanTime = mTotalScanTime; 939 long oppScanTime = mOppScanTime; 940 long lowPowerScanTime = mLowPowerScanTime; 941 long balancedScanTime = mBalancedScanTime; 942 long lowLatencyScanTime = mLowLatencyScanTime; 943 long ambientDiscoveryScanTime = mAmbientDiscoveryScanTime; 944 int oppScan = mOppScan; 945 int lowPowerScan = mLowPowerScan; 946 int balancedScan = mBalancedScan; 947 int lowLatencyScan = mLowLatencyScan; 948 long ambientDiscoveryScan = mAmbientDiscoveryScan; 949 950 for (LastScan scan : mOngoingScans.values()) { 951 scanDuration = currTime - scan.timestamp; 952 953 if (scan.isSuspended) { 954 suspendDuration = currTime - scan.suspendStartTime; 955 totalSuspendTime += suspendDuration; 956 } 957 958 totalScanTime += scanDuration; 959 totalSuspendTime += suspendDuration; 960 activeDuration = scanDuration - scan.suspendDuration - suspendDuration; 961 totalActiveTime += activeDuration; 962 switch (scan.scanMode) { 963 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 964 oppScanTime += activeDuration; 965 break; 966 case ScanSettings.SCAN_MODE_LOW_POWER: 967 lowPowerScanTime += activeDuration; 968 break; 969 case ScanSettings.SCAN_MODE_BALANCED: 970 balancedScanTime += activeDuration; 971 break; 972 case ScanSettings.SCAN_MODE_LOW_LATENCY: 973 lowLatencyScanTime += activeDuration; 974 break; 975 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 976 ambientDiscoveryScan += activeDuration; 977 break; 978 } 979 } 980 981 long Score = 982 (oppScanTime * OPPORTUNISTIC_WEIGHT 983 + lowPowerScanTime * LOW_POWER_WEIGHT 984 + balancedScanTime * BALANCED_WEIGHT 985 + lowLatencyScanTime * LOW_LATENCY_WEIGHT 986 + ambientDiscoveryScanTime * AMBIENT_DISCOVERY_WEIGHT) 987 / 100; 988 989 sb.append(" ").append(mAppName); 990 if (isRegistered) { 991 sb.append(" (Registered)"); 992 } 993 994 sb.append("\n LE scans (started/stopped) : ") 995 .append(mScansStarted) 996 .append(" / ") 997 .append(mScansStopped); 998 sb.append("\n Scan time in ms (active/suspend/total) : ") 999 .append(totalActiveTime) 1000 .append(" / ") 1001 .append(totalSuspendTime) 1002 .append(" / ") 1003 .append(totalScanTime); 1004 sb.append("\n Scan time with mode in ms ") 1005 .append("(Opp/LowPower/Balanced/LowLatency/AmbientDiscovery):") 1006 .append(oppScanTime) 1007 .append(" / ") 1008 .append(lowPowerScanTime) 1009 .append(" / ") 1010 .append(balancedScanTime) 1011 .append(" / ") 1012 .append(lowLatencyScanTime) 1013 .append(" / ") 1014 .append(ambientDiscoveryScanTime); 1015 sb.append("\n Scan mode counter (Opp/LowPower/Balanced/LowLatency/AmbientDiscovery):") 1016 .append(oppScan) 1017 .append(" / ") 1018 .append(lowPowerScan) 1019 .append(" / ") 1020 .append(balancedScan) 1021 .append(" / ") 1022 .append(lowLatencyScan) 1023 .append(" / ") 1024 .append(ambientDiscoveryScan); 1025 sb.append("\n Score : ") 1026 .append(Score); 1027 sb.append("\n Total number of results : ") 1028 .append(results); 1029 1030 if (!mLastScans.isEmpty()) { 1031 sb.append("\n Last ") 1032 .append(mLastScans.size()) 1033 .append(" scans :"); 1034 1035 for (int i = 0; i < mLastScans.size(); i++) { 1036 LastScan scan = mLastScans.get(i); 1037 Date timestamp = new Date(currentTime - currTime + scan.timestamp); 1038 sb.append("\n ").append(dateFormat.format(timestamp)).append(" - "); 1039 sb.append(scan.duration).append("ms "); 1040 if (scan.isOpportunisticScan) { 1041 sb.append("Opp "); 1042 } 1043 if (scan.isBackgroundScan) { 1044 sb.append("Back "); 1045 } 1046 if (scan.isTimeout) { 1047 sb.append("Forced "); 1048 } 1049 if (scan.isFilterScan) { 1050 sb.append("Filter "); 1051 } 1052 sb.append(scan.results).append(" results"); 1053 sb.append(" (").append(scan.scannerId).append(") "); 1054 if (scan.attributionTag != null) { 1055 sb.append(" [").append(scan.attributionTag).append("] "); 1056 } 1057 if (scan.isCallbackScan) { 1058 sb.append("CB "); 1059 } else { 1060 sb.append("PI "); 1061 } 1062 if (scan.isBatchScan) { 1063 sb.append("Batch Scan"); 1064 } else if (scan.isAutoBatchScan) { 1065 sb.append("Auto Batch Scan"); 1066 } else { 1067 sb.append("Regular Scan"); 1068 } 1069 if (scan.appImportanceOnStart < IMPORTANCE_FOREGROUND_SERVICE) { 1070 sb.append("\n └ ") 1071 .append("App Importance: higher than Foreground Service"); 1072 } else if (scan.appImportanceOnStart > IMPORTANCE_FOREGROUND_SERVICE) { 1073 sb.append("\n └ ").append("App Importance: lower than Foreground Service"); 1074 } else { 1075 sb.append("\n └ ").append("App Importance: Foreground Service"); 1076 } 1077 if (scan.suspendDuration != 0) { 1078 activeDuration = scan.duration - scan.suspendDuration; 1079 sb.append("\n └ ") 1080 .append("Suspended Time: ") 1081 .append(scan.suspendDuration) 1082 .append("ms, Active Time: ") 1083 .append(activeDuration); 1084 } 1085 sb.append("\n └ ") 1086 .append("Scan Config: [ ScanMode=") 1087 .append(scanModeToString(scan.scanMode)) 1088 .append(", callbackType=") 1089 .append(callbackTypeToString(scan.scanCallbackType)) 1090 .append(" ]"); 1091 if (scan.isFilterScan) { 1092 sb.append(scan.filterString); 1093 } 1094 } 1095 } 1096 1097 if (!mOngoingScans.isEmpty()) { 1098 sb.append("\n Ongoing scans :"); 1099 for (LastScan scan : mOngoingScans.values()) { 1100 Date timestamp = new Date(currentTime - currTime + scan.timestamp); 1101 sb.append("\n ").append(dateFormat.format(timestamp)).append(" - "); 1102 sb.append((currTime - scan.timestamp)).append("ms "); 1103 if (scan.isOpportunisticScan) { 1104 sb.append("Opp "); 1105 } 1106 if (scan.isBackgroundScan) { 1107 sb.append("Back "); 1108 } 1109 if (scan.isTimeout) { 1110 sb.append("Forced "); 1111 } 1112 if (scan.isFilterScan) { 1113 sb.append("Filter "); 1114 } 1115 if (scan.isSuspended) { 1116 sb.append("Suspended "); 1117 } 1118 sb.append(scan.results).append(" results"); 1119 sb.append(" (").append(scan.scannerId).append(") "); 1120 if (scan.isCallbackScan) { 1121 sb.append("CB "); 1122 } else { 1123 sb.append("PI "); 1124 } 1125 if (scan.isBatchScan) { 1126 sb.append("Batch Scan"); 1127 } else if (scan.isAutoBatchScan) { 1128 sb.append("Auto Batch Scan"); 1129 } else { 1130 sb.append("Regular Scan"); 1131 } 1132 if (scan.suspendStartTime != 0) { 1133 activeDuration = scan.duration - scan.suspendDuration; 1134 sb.append("\n └ ") 1135 .append("Suspended Time:") 1136 .append(scan.suspendDuration) 1137 .append("ms, Active Time:") 1138 .append(activeDuration); 1139 } 1140 sb.append("\n └ ") 1141 .append("Scan Config: [ ScanMode=") 1142 .append(scanModeToString(scan.scanMode)) 1143 .append(", callbackType=") 1144 .append(callbackTypeToString(scan.scanCallbackType)) 1145 .append(" ]"); 1146 if (scan.isFilterScan) { 1147 sb.append(scan.filterString); 1148 } 1149 } 1150 } 1151 1152 if (isRegistered) { 1153 List<ScannerMap.ScannerApp> appEntries = mScannerMap.getByName(mAppName); 1154 for (ScannerMap.ScannerApp appEntry : appEntries) { 1155 sb.append("\n Application ID: ").append(appEntry.mId); 1156 sb.append(", UUID: ").append(appEntry.mUuid); 1157 if (appEntry.mAttributionTag != null) { 1158 sb.append(", Tag: ").append(appEntry.mAttributionTag); 1159 } 1160 } 1161 } 1162 sb.append("\n\n"); 1163 } 1164 } 1165