AppScanStats.java revision b7af521b6c2d1ccaaea687207dfbcd0c34489a3c
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 class LastScan { 39 long duration; 40 long timestamp; 41 boolean opportunistic; 42 boolean background; 43 44 public LastScan(long timestamp, long duration, 45 boolean opportunistic, boolean background) { 46 this.duration = duration; 47 this.timestamp = timestamp; 48 this.opportunistic = opportunistic; 49 this.background = background; 50 } 51 } 52 53 static final int NUM_SCAN_DURATIONS_KEPT = 5; 54 55 String appName; 56 int scansStarted = 0; 57 int scansStopped = 0; 58 boolean isScanning = false; 59 boolean isRegistered = false; 60 long minScanTime = Long.MAX_VALUE; 61 long maxScanTime = 0; 62 long totalScanTime = 0; 63 List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT + 1); 64 long startTime = 0; 65 long stopTime = 0; 66 67 public AppScanStats(String name, ContextMap map) { 68 appName = name; 69 contextMap = map; 70 } 71 72 synchronized void recordScanStart(ScanSettings settings) { 73 if (isScanning) 74 return; 75 76 this.scansStarted++; 77 isScanning = true; 78 startTime = System.currentTimeMillis(); 79 80 LastScan scan = new LastScan(startTime, 0, false, false); 81 if (settings != null) { 82 scan.opportunistic = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; 83 scan.background = (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; 84 } 85 lastScans.add(scan); 86 87 BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent(); 88 scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_START); 89 scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE); 90 scanEvent.setInitiator(appName); 91 scanEvent.setEventTimeMillis(System.currentTimeMillis()); 92 contextMap.addScanEvent(scanEvent); 93 } 94 95 synchronized void recordScanStop() { 96 if (!isScanning) 97 return; 98 99 this.scansStopped++; 100 isScanning = false; 101 stopTime = System.currentTimeMillis(); 102 long scanDuration = stopTime - startTime; 103 104 minScanTime = Math.min(scanDuration, minScanTime); 105 maxScanTime = Math.max(scanDuration, maxScanTime); 106 totalScanTime += scanDuration; 107 108 LastScan curr = lastScans.get(lastScans.size() - 1); 109 curr.duration = scanDuration; 110 111 if (lastScans.size() > NUM_SCAN_DURATIONS_KEPT) { 112 lastScans.remove(0); 113 } 114 115 BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent(); 116 scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_STOP); 117 scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE); 118 scanEvent.setInitiator(appName); 119 scanEvent.setEventTimeMillis(System.currentTimeMillis()); 120 contextMap.addScanEvent(scanEvent); 121 } 122 123 synchronized void dumpToString(StringBuilder sb) { 124 long currTime = System.currentTimeMillis(); 125 long maxScan = maxScanTime; 126 long minScan = minScanTime; 127 long scanDuration = 0; 128 129 if (lastScans.isEmpty()) 130 return; 131 132 if (isScanning) { 133 scanDuration = currTime - startTime; 134 minScan = Math.min(scanDuration, minScan); 135 maxScan = Math.max(scanDuration, maxScan); 136 } 137 138 if (minScan == Long.MAX_VALUE) { 139 minScan = 0; 140 } 141 142 long avgScan = 0; 143 if (scansStarted > 0) { 144 avgScan = (totalScanTime + scanDuration) / scansStarted; 145 } 146 147 LastScan lastScan = lastScans.get(lastScans.size() - 1); 148 sb.append(" " + appName); 149 if (isRegistered) sb.append(" (Registered)"); 150 if (lastScan.opportunistic) sb.append(" (Opportunistic)"); 151 if (lastScan.background) sb.append(" (Background)"); 152 sb.append("\n"); 153 154 sb.append(" LE scans (started/stopped) : " + 155 scansStarted + " / " + 156 scansStopped + "\n"); 157 sb.append(" Scan time in ms (min/max/avg/total): " + 158 minScan + " / " + 159 maxScan + " / " + 160 avgScan + " / " + 161 totalScanTime + "\n"); 162 163 if (lastScans.size() != 0) { 164 int lastScansSize = scansStopped < NUM_SCAN_DURATIONS_KEPT ? 165 scansStopped : NUM_SCAN_DURATIONS_KEPT; 166 sb.append(" Last " + lastScansSize + 167 " scans :\n"); 168 169 for (int i = 0; i < lastScansSize; i++) { 170 LastScan scan = lastScans.get(i); 171 Date timestamp = new Date(scan.timestamp); 172 sb.append(" " + dateFormat.format(timestamp) + " - "); 173 sb.append(scan.duration + "ms "); 174 if (scan.opportunistic) sb.append("Opp "); 175 if (scan.background) sb.append("Back"); 176 sb.append("\n"); 177 } 178 } 179 180 if (isRegistered) { 181 ContextMap.App appEntry = contextMap.getByName(appName); 182 sb.append(" Application ID : " + 183 appEntry.id + "\n"); 184 sb.append(" UUID : " + 185 appEntry.uuid + "\n"); 186 187 if (isScanning) { 188 sb.append(" Current scan duration in ms : " + 189 scanDuration + "\n"); 190 } 191 192 List<ContextMap.Connection> connections = 193 contextMap.getConnectionByApp(appEntry.id); 194 195 sb.append(" Connections: " + connections.size() + "\n"); 196 197 Iterator<ContextMap.Connection> ii = connections.iterator(); 198 while(ii.hasNext()) { 199 ContextMap.Connection connection = ii.next(); 200 long connectionTime = System.currentTimeMillis() - connection.startTime; 201 sb.append(" " + connection.connId + ": " + 202 connection.address + " " + connectionTime + "ms\n"); 203 } 204 } 205 sb.append("\n"); 206 } 207} 208