AppScanStats.java revision e90db937c008f365f47e7199d6d86f9eb13bed1e
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 */ 16package com.android.bluetooth.gatt; 17 18import android.bluetooth.le.ScanSettings; 19import java.text.DateFormat; 20import java.text.SimpleDateFormat; 21import java.util.ArrayList; 22import java.util.Date; 23import java.util.Iterator; 24import java.util.List; 25 26import com.android.bluetooth.btservice.BluetoothProto; 27/** 28 * ScanStats class helps keep track of information about scans 29 * on a per application basis. 30 * @hide 31 */ 32/*package*/ class AppScanStats { 33 static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); 34 35 /* ContextMap here is needed to grab Apps and Connections */ 36 ContextMap contextMap; 37 38 /* GattService is needed to add scan event protos to be dumped later */ 39 GattService gattService; 40 41 class LastScan { 42 long duration; 43 long timestamp; 44 boolean opportunistic; 45 boolean background; 46 int results; 47 48 public LastScan(long timestamp, long duration, 49 boolean opportunistic, boolean background) { 50 this.duration = duration; 51 this.timestamp = timestamp; 52 this.opportunistic = opportunistic; 53 this.background = background; 54 this.results = 0; 55 } 56 } 57 58 static final int NUM_SCAN_DURATIONS_KEPT = 5; 59 60 String appName; 61 int scansStarted = 0; 62 int scansStopped = 0; 63 boolean isScanning = false; 64 boolean isRegistered = false; 65 long minScanTime = Long.MAX_VALUE; 66 long maxScanTime = 0; 67 long totalScanTime = 0; 68 List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT + 1); 69 long startTime = 0; 70 long stopTime = 0; 71 int results = 0; 72 73 public AppScanStats(String name, ContextMap map, GattService service) { 74 appName = name; 75 contextMap = map; 76 gattService = service; 77 } 78 79 synchronized void addResult() { 80 if (!lastScans.isEmpty()) 81 lastScans.get(lastScans.size() - 1).results++; 82 83 results++; 84 } 85 86 synchronized void recordScanStart(ScanSettings settings) { 87 if (isScanning) 88 return; 89 90 this.scansStarted++; 91 isScanning = true; 92 startTime = System.currentTimeMillis(); 93 94 LastScan scan = new LastScan(startTime, 0, false, false); 95 if (settings != null) { 96 scan.opportunistic = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; 97 scan.background = (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; 98 } 99 lastScans.add(scan); 100 101 BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent(); 102 scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_START); 103 scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE); 104 scanEvent.setInitiator(appName); 105 scanEvent.setEventTimeMillis(System.currentTimeMillis()); 106 gattService.addScanEvent(scanEvent); 107 } 108 109 synchronized void recordScanStop() { 110 if (!isScanning) 111 return; 112 113 this.scansStopped++; 114 isScanning = false; 115 stopTime = System.currentTimeMillis(); 116 long scanDuration = stopTime - startTime; 117 118 minScanTime = Math.min(scanDuration, minScanTime); 119 maxScanTime = Math.max(scanDuration, maxScanTime); 120 totalScanTime += scanDuration; 121 122 LastScan curr = lastScans.get(lastScans.size() - 1); 123 curr.duration = scanDuration; 124 125 if (lastScans.size() > NUM_SCAN_DURATIONS_KEPT) { 126 lastScans.remove(0); 127 } 128 129 BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent(); 130 scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_STOP); 131 scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE); 132 scanEvent.setInitiator(appName); 133 scanEvent.setEventTimeMillis(System.currentTimeMillis()); 134 gattService.addScanEvent(scanEvent); 135 } 136 137 synchronized void dumpToString(StringBuilder sb) { 138 long currTime = System.currentTimeMillis(); 139 long maxScan = maxScanTime; 140 long minScan = minScanTime; 141 long scanDuration = 0; 142 143 if (lastScans.isEmpty()) 144 return; 145 146 if (isScanning) { 147 scanDuration = currTime - startTime; 148 minScan = Math.min(scanDuration, minScan); 149 maxScan = Math.max(scanDuration, maxScan); 150 } 151 152 if (minScan == Long.MAX_VALUE) { 153 minScan = 0; 154 } 155 156 long avgScan = 0; 157 if (scansStarted > 0) { 158 avgScan = (totalScanTime + scanDuration) / scansStarted; 159 } 160 161 LastScan lastScan = lastScans.get(lastScans.size() - 1); 162 sb.append(" " + appName); 163 if (isRegistered) sb.append(" (Registered)"); 164 if (lastScan.opportunistic) sb.append(" (Opportunistic)"); 165 if (lastScan.background) sb.append(" (Background)"); 166 sb.append("\n"); 167 168 sb.append(" LE scans (started/stopped) : " + 169 scansStarted + " / " + 170 scansStopped + "\n"); 171 sb.append(" Scan time in ms (min/max/avg/total): " + 172 minScan + " / " + 173 maxScan + " / " + 174 avgScan + " / " + 175 totalScanTime + "\n"); 176 sb.append(" Total number of results : " + 177 results + "\n"); 178 179 if (lastScans.size() != 0) { 180 int lastScansSize = scansStopped < NUM_SCAN_DURATIONS_KEPT ? 181 scansStopped : NUM_SCAN_DURATIONS_KEPT; 182 sb.append(" Last " + lastScansSize + 183 " scans :\n"); 184 185 for (int i = 0; i < lastScansSize; i++) { 186 LastScan scan = lastScans.get(i); 187 Date timestamp = new Date(scan.timestamp); 188 sb.append(" " + dateFormat.format(timestamp) + " - "); 189 sb.append(scan.duration + "ms "); 190 if (scan.opportunistic) sb.append("Opp "); 191 if (scan.background) sb.append("Back "); 192 sb.append(scan.results + " results"); 193 sb.append("\n"); 194 } 195 } 196 197 if (isRegistered) { 198 ContextMap.App appEntry = contextMap.getByName(appName); 199 sb.append(" Application ID : " + 200 appEntry.id + "\n"); 201 sb.append(" UUID : " + 202 appEntry.uuid + "\n"); 203 204 if (isScanning) { 205 sb.append(" Current scan duration in ms : " + 206 scanDuration + "\n"); 207 } 208 209 List<ContextMap.Connection> connections = 210 contextMap.getConnectionByApp(appEntry.id); 211 212 sb.append(" Connections: " + connections.size() + "\n"); 213 214 Iterator<ContextMap.Connection> ii = connections.iterator(); 215 while(ii.hasNext()) { 216 ContextMap.Connection connection = ii.next(); 217 long connectionTime = System.currentTimeMillis() - connection.startTime; 218 sb.append(" " + connection.connId + ": " + 219 connection.address + " " + connectionTime + "ms\n"); 220 } 221 } 222 sb.append("\n"); 223 } 224} 225