1b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker/*
2b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * Copyright (C) 2016 The Android Open Source Project
3b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker *
4b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * Licensed under the Apache License, Version 2.0 (the "License");
5b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * you may not use this file except in compliance with the License.
6b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * You may obtain a copy of the License at
7b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker *
8b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker *      http://www.apache.org/licenses/LICENSE-2.0
9b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker *
10b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * Unless required by applicable law or agreed to in writing, software
11b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * distributed under the License is distributed on an "AS IS" BASIS,
12b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * See the License for the specific language governing permissions and
14b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * limitations under the License.
15b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker */
16b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerpackage com.android.bluetooth.gatt;
17b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
18b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport android.bluetooth.le.ScanSettings;
19edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panickerimport android.os.Binder;
20edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panickerimport android.os.WorkSource;
21edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panickerimport android.os.ServiceManager;
22ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panickerimport android.os.SystemClock;
23edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panickerimport android.os.RemoteException;
24edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panickerimport com.android.internal.app.IBatteryStats;
25b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.text.DateFormat;
26b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.text.SimpleDateFormat;
27b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.util.ArrayList;
28b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.util.Date;
29b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.util.Iterator;
30b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport java.util.List;
31ff0aaa12c3204523136cc75886add779e4727d00Vinay Kaliaimport java.util.HashMap;
32b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
33b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panickerimport com.android.bluetooth.btservice.BluetoothProto;
34b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker/**
35b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * ScanStats class helps keep track of information about scans
36b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * on a per application basis.
37b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker * @hide
38b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker */
39b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker/*package*/ class AppScanStats {
40b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
41b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
42b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    /* ContextMap here is needed to grab Apps and Connections */
43b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    ContextMap contextMap;
44b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
45c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker    /* GattService is needed to add scan event protos to be dumped later */
46c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker    GattService gattService;
47c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker
48edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker    /* Battery stats is used to keep track of scans and result stats */
49edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker    IBatteryStats batteryStats;
50edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker
51b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    class LastScan {
52b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long duration;
5312991e68679245a717ba3a94df90c57d438e259eVinay Kalia        long suspendDuration;
5412991e68679245a717ba3a94df90c57d438e259eVinay Kalia        long suspendStartTime;
5512991e68679245a717ba3a94df90c57d438e259eVinay Kalia        boolean isSuspended;
56b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long timestamp;
57b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        boolean opportunistic;
581fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker        boolean timeout;
59b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        boolean background;
60bd11ad34482a898a53bbd9f55bf3607c40c648ecAjay Panicker        boolean filtered;
61c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        int results;
62ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        int scannerId;
63b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
64bd11ad34482a898a53bbd9f55bf3607c40c648ecAjay Panicker        public LastScan(long timestamp, long duration, boolean opportunistic, boolean background,
65ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                boolean filtered, int scannerId) {
66b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            this.duration = duration;
67b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            this.timestamp = timestamp;
68b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            this.opportunistic = opportunistic;
69b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            this.background = background;
70bd11ad34482a898a53bbd9f55bf3607c40c648ecAjay Panicker            this.filtered = filtered;
71c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker            this.results = 0;
72ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            this.scannerId = scannerId;
7312991e68679245a717ba3a94df90c57d438e259eVinay Kalia            this.suspendDuration = 0;
7412991e68679245a717ba3a94df90c57d438e259eVinay Kalia            this.suspendStartTime = 0;
7512991e68679245a717ba3a94df90c57d438e259eVinay Kalia            this.isSuspended = false;
76b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
77b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    }
78b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
79b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    static final int NUM_SCAN_DURATIONS_KEPT = 5;
80b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
811fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    // This constant defines the time window an app can scan multiple times.
821fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    // Any single app can scan up to |NUM_SCAN_DURATIONS_KEPT| times during
831fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    // this window. Once they reach this limit, they must wait until their
841fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    // earliest recorded scan exits this window.
851fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    static final long EXCESSIVE_SCANNING_PERIOD_MS = 30 * 1000;
861fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker
8765e839fa45222955a605f6270a240edc68b21617Ajay Panicker    // Maximum msec before scan gets downgraded to opportunistic
8865e839fa45222955a605f6270a240edc68b21617Ajay Panicker    static final int SCAN_TIMEOUT_MS = 30 * 60 * 1000;
8965e839fa45222955a605f6270a240edc68b21617Ajay Panicker
90b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    String appName;
91edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker    WorkSource workSource; // Used for BatteryStats
92b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    int scansStarted = 0;
93b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    int scansStopped = 0;
94b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    boolean isRegistered = false;
95b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    long minScanTime = Long.MAX_VALUE;
96b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    long maxScanTime = 0;
97ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    long mScanStartTime = 0;
98ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    long mTotalScanTime = 0;
9912991e68679245a717ba3a94df90c57d438e259eVinay Kalia    long mTotalSuspendTime = 0;
100ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT);
101ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    HashMap<Integer, LastScan> ongoingScans = new HashMap<Integer, LastScan>();
102b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    long startTime = 0;
103b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    long stopTime = 0;
104c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker    int results = 0;
105b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
106edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker    public AppScanStats(String name, WorkSource source, ContextMap map, GattService service) {
107b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        appName = name;
108b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        contextMap = map;
109c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        gattService = service;
110edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        batteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batterystats"));
111edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker
112edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        if (source == null) {
113edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker            // Bill the caller if the work source isn't passed through
114edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker            source = new WorkSource(Binder.getCallingUid(), appName);
115edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        }
116edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        workSource = source;
117c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker    }
118c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker
119ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    synchronized void addResult(int scannerId) {
120ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        LastScan scan = getScanFromScannerId(scannerId);
121ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (scan != null) {
122ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            int batteryStatsResults = ++scan.results;
1238dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker
1248dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker            // Only update battery stats after receiving 100 new results in order
1258dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker            // to lower the cost of the binder transaction
1268dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker            if (batteryStatsResults % 100 == 0) {
1278dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker                try {
1288dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker                    batteryStats.noteBleScanResults(workSource, 100);
1298dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker                } catch (RemoteException e) {
1308dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker                    /* ignore */
1318dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker                }
1328dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker            }
1338dc1ac7b011ff262830a0a28f66ae53fe5f63f74Ajay Panicker        }
134c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker
135c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        results++;
136b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    }
137b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
138ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    boolean isScanning() {
139ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        return !ongoingScans.isEmpty();
140ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    }
141ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia
142ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    LastScan getScanFromScannerId(int scannerId) {
143ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        return ongoingScans.get(scannerId);
144ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    }
1452e20779b97be2073942b1fc8093414ae08e03136Vinay Kalia
146ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    synchronized void recordScanStart(ScanSettings settings, boolean filtered, int scannerId) {
147ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        LastScan existingScan = getScanFromScannerId(scannerId);
148ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (existingScan != null) {
149ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            return;
150ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        }
151b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        this.scansStarted++;
152ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker        startTime = SystemClock.elapsedRealtime();
153b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
154ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        LastScan scan = new LastScan(startTime, 0, false, false, filtered, scannerId);
155b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        if (settings != null) {
156b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker          scan.opportunistic = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
157b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker          scan.background = (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
158b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
159b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
160b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
161b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_START);
162b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
163b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setEventTimeMillis(System.currentTimeMillis());
1643d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        scanEvent.setInitiator(truncateAppName(appName));
165c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        gattService.addScanEvent(scanEvent);
166edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker
167d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        if (!isScanning()) mScanStartTime = startTime;
168d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        try {
169d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            boolean isUnoptimized = !(scan.filtered || scan.background || scan.opportunistic);
170d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            batteryStats.noteBleScanStarted(workSource, isUnoptimized);
171d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        } catch (RemoteException e) {
172d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            /* ignore */
173edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        }
174b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
175ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        ongoingScans.put(scannerId, scan);
176ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    }
1772e20779b97be2073942b1fc8093414ae08e03136Vinay Kalia
178ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    synchronized void recordScanStop(int scannerId) {
179ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        LastScan scan = getScanFromScannerId(scannerId);
180ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (scan == null) {
181ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            return;
182ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        }
183b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        this.scansStopped++;
184ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker        stopTime = SystemClock.elapsedRealtime();
185ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        long scanDuration = stopTime - scan.timestamp;
186ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        scan.duration = scanDuration;
18712991e68679245a717ba3a94df90c57d438e259eVinay Kalia        if (scan.isSuspended) {
18812991e68679245a717ba3a94df90c57d438e259eVinay Kalia            scan.suspendDuration += stopTime - scan.suspendStartTime;
18912991e68679245a717ba3a94df90c57d438e259eVinay Kalia            mTotalSuspendTime += scan.suspendDuration;
19012991e68679245a717ba3a94df90c57d438e259eVinay Kalia        }
191ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        ongoingScans.remove(scannerId);
192ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (lastScans.size() >= NUM_SCAN_DURATIONS_KEPT) {
193b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            lastScans.remove(0);
194b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
195ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        lastScans.add(scan);
196b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
197b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
198b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_STOP);
199b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
200b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        scanEvent.setEventTimeMillis(System.currentTimeMillis());
2013d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        scanEvent.setInitiator(truncateAppName(appName));
202c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        gattService.addScanEvent(scanEvent);
203edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker
204ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (!isScanning()) {
205d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            long totalDuration = stopTime - mScanStartTime;
206d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            mTotalScanTime += totalDuration;
207d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            minScanTime = Math.min(totalDuration, minScanTime);
208d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            maxScanTime = Math.max(totalDuration, maxScanTime);
209d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        }
210d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker
211d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        try {
212d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            // Inform battery stats of any results it might be missing on
213d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            // scan stop
214d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            boolean isUnoptimized = !(scan.filtered || scan.background || scan.opportunistic);
215d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            batteryStats.noteBleScanResults(workSource, scan.results % 100);
216d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            batteryStats.noteBleScanStopped(workSource, isUnoptimized);
217d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker        } catch (RemoteException e) {
218d5078f2b255eafb78881f39e23ae329f8515db58Ajay Panicker            /* ignore */
219edbc90eb917a8a509d2d03fb304763b768ec5517Ajay Panicker        }
220b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    }
221b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
22212991e68679245a717ba3a94df90c57d438e259eVinay Kalia    synchronized void recordScanSuspend(int scannerId) {
22312991e68679245a717ba3a94df90c57d438e259eVinay Kalia        LastScan scan = getScanFromScannerId(scannerId);
22412991e68679245a717ba3a94df90c57d438e259eVinay Kalia        if (scan == null || scan.isSuspended) {
22512991e68679245a717ba3a94df90c57d438e259eVinay Kalia            return;
22612991e68679245a717ba3a94df90c57d438e259eVinay Kalia        }
22712991e68679245a717ba3a94df90c57d438e259eVinay Kalia        scan.suspendStartTime = SystemClock.elapsedRealtime();
22812991e68679245a717ba3a94df90c57d438e259eVinay Kalia        scan.isSuspended = true;
22912991e68679245a717ba3a94df90c57d438e259eVinay Kalia    }
23012991e68679245a717ba3a94df90c57d438e259eVinay Kalia
23112991e68679245a717ba3a94df90c57d438e259eVinay Kalia    synchronized void recordScanResume(int scannerId) {
23212991e68679245a717ba3a94df90c57d438e259eVinay Kalia        LastScan scan = getScanFromScannerId(scannerId);
23312991e68679245a717ba3a94df90c57d438e259eVinay Kalia        if (scan == null || !scan.isSuspended) {
23412991e68679245a717ba3a94df90c57d438e259eVinay Kalia            return;
23512991e68679245a717ba3a94df90c57d438e259eVinay Kalia        }
23612991e68679245a717ba3a94df90c57d438e259eVinay Kalia        scan.isSuspended = false;
23712991e68679245a717ba3a94df90c57d438e259eVinay Kalia        stopTime = SystemClock.elapsedRealtime();
23812991e68679245a717ba3a94df90c57d438e259eVinay Kalia        scan.suspendDuration += stopTime - scan.suspendStartTime;
23912991e68679245a717ba3a94df90c57d438e259eVinay Kalia        mTotalSuspendTime += scan.suspendDuration;
24012991e68679245a717ba3a94df90c57d438e259eVinay Kalia    }
24112991e68679245a717ba3a94df90c57d438e259eVinay Kalia
242ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia    synchronized void setScanTimeout(int scannerId) {
243ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (!isScanning()) return;
2441fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker
245ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        LastScan scan = getScanFromScannerId(scannerId);
246ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (scan != null) {
247ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            scan.timeout = true;
2481fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker        }
2491fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    }
2501fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker
2511fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    synchronized boolean isScanningTooFrequently() {
2521fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker        if (lastScans.size() < NUM_SCAN_DURATIONS_KEPT) {
2531fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker            return false;
2541fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker        }
2551fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker
256ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker        return (SystemClock.elapsedRealtime() - lastScans.get(0).timestamp)
257ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker                < EXCESSIVE_SCANNING_PERIOD_MS;
2581fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker    }
2591fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker
26065e839fa45222955a605f6270a240edc68b21617Ajay Panicker    synchronized boolean isScanningTooLong() {
261ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (!isScanning()) {
26265e839fa45222955a605f6270a240edc68b21617Ajay Panicker            return false;
26365e839fa45222955a605f6270a240edc68b21617Ajay Panicker        }
264ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        return (SystemClock.elapsedRealtime() - mScanStartTime) > SCAN_TIMEOUT_MS;
26565e839fa45222955a605f6270a240edc68b21617Ajay Panicker    }
26665e839fa45222955a605f6270a240edc68b21617Ajay Panicker
2673d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    // This function truncates the app name for privacy reasons. Apps with
2683d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    // four part package names or more get truncated to three parts, and apps
2693d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    // with three part package names names get truncated to two. Apps with two
2703d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    // or less package names names are untouched.
2713d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    // Examples: one.two.three.four => one.two.three
2723d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    //           one.two.three => one.two
2733d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    private String truncateAppName(String name) {
2743d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        String initiator = name;
2753d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        String[] nameSplit = initiator.split("\\.");
2763d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        if (nameSplit.length > 3) {
2773d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker            initiator = nameSplit[0] + "." +
2783d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker                        nameSplit[1] + "." +
2793d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker                        nameSplit[2];
2803d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        } else if (nameSplit.length == 3) {
2813d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker            initiator = nameSplit[0] + "." + nameSplit[1];
2823d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        }
2833d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker
2843d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker        return initiator;
2853d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker    }
2863d69ae195b8d3918958ea703a188cc77e511da6cAjay Panicker
287b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    synchronized void dumpToString(StringBuilder sb) {
288ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker        long currTime = SystemClock.elapsedRealtime();
289b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long maxScan = maxScanTime;
290b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long minScan = minScanTime;
291b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long scanDuration = 0;
292b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
293ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (isScanning()) {
294ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            scanDuration = currTime - mScanStartTime;
295b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
296ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        minScan = Math.min(scanDuration, minScan);
297ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        maxScan = Math.max(scanDuration, maxScan);
298b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
299b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        if (minScan == Long.MAX_VALUE) {
300b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            minScan = 0;
301b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
302b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
303ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        /*TODO: Average scan time can be skewed for
304ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia         * multiple scan clients. It will show less than
305ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia         * actual value.
306ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia         * */
307b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        long avgScan = 0;
308ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        long totalScanTime = mTotalScanTime + scanDuration;
309b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        if (scansStarted > 0) {
310ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            avgScan = totalScanTime / scansStarted;
311b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
312b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
313b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        sb.append("  " + appName);
314b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        if (isRegistered) sb.append(" (Registered)");
315c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker
316c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker        if (!lastScans.isEmpty()) {
317c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker            LastScan lastScan = lastScans.get(lastScans.size() - 1);
318c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker            if (lastScan.opportunistic) sb.append(" (Opportunistic)");
319c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker            if (lastScan.background) sb.append(" (Background)");
320c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker            if (lastScan.timeout) sb.append(" (Forced-Opportunistic)");
321bd11ad34482a898a53bbd9f55bf3607c40c648ecAjay Panicker            if (lastScan.filtered) sb.append(" (Filtered)");
322c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker        }
323b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        sb.append("\n");
324b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
325b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        sb.append("  LE scans (started/stopped)         : " +
326b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  scansStarted + " / " +
327b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  scansStopped + "\n");
328b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        sb.append("  Scan time in ms (min/max/avg/total): " +
329b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  minScan + " / " +
330b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  maxScan + " / " +
331b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  avgScan + " / " +
332b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                  totalScanTime + "\n");
33312991e68679245a717ba3a94df90c57d438e259eVinay Kalia        if (mTotalSuspendTime != 0) {
33412991e68679245a717ba3a94df90c57d438e259eVinay Kalia            sb.append("  Total time suspended             : " + mTotalSuspendTime + "ms\n");
33512991e68679245a717ba3a94df90c57d438e259eVinay Kalia        }
336c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker        sb.append("  Total number of results            : " +
337c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker                  results + "\n");
338b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
339ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        long currentTime = System.currentTimeMillis();
340ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        long elapsedRt = SystemClock.elapsedRealtime();
341c82ac85eacc5607433592f476dbbe73d7670efe1Ajay Panicker        if (!lastScans.isEmpty()) {
342ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            sb.append("  Last " + lastScans.size() + " scans                       :\n");
343b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
344ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            for (int i = 0; i < lastScans.size(); i++) {
345b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                LastScan scan = lastScans.get(i);
346ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                Date timestamp = new Date(currentTime - elapsedRt + scan.timestamp);
347b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                sb.append("    " + dateFormat.format(timestamp) + " - ");
348b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                sb.append(scan.duration + "ms ");
349b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                if (scan.opportunistic) sb.append("Opp ");
350c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker                if (scan.background) sb.append("Back ");
3511fdc7c138db776b02bc751fd7a80c519ea3324d1Ajay Panicker                if (scan.timeout) sb.append("Forced ");
352bd11ad34482a898a53bbd9f55bf3607c40c648ecAjay Panicker                if (scan.filtered) sb.append("Filter ");
353c30f06668bc683b13319a33775ee8f9def3283c8Ajay Panicker                sb.append(scan.results + " results");
354ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append(" (" + scan.scannerId + ")");
355ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append("\n");
35612991e68679245a717ba3a94df90c57d438e259eVinay Kalia                if (scan.suspendDuration != 0) {
35712991e68679245a717ba3a94df90c57d438e259eVinay Kalia                    sb.append("      └"
35812991e68679245a717ba3a94df90c57d438e259eVinay Kalia                            + " Suspended Time: " + scan.suspendDuration + "ms\n");
35912991e68679245a717ba3a94df90c57d438e259eVinay Kalia                }
360ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            }
361ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        }
362ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia
363ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia        if (!ongoingScans.isEmpty()) {
364ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            sb.append("  Ongoing scans                      :\n");
365ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia            for (Integer key : ongoingScans.keySet()) {
366ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                LastScan scan = ongoingScans.get(key);
367ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                Date timestamp = new Date(currentTime - elapsedRt + scan.timestamp);
368ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append("    " + dateFormat.format(timestamp) + " - ");
369ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append((elapsedRt - scan.timestamp) + "ms ");
370ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                if (scan.opportunistic) sb.append("Opp ");
371ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                if (scan.background) sb.append("Back ");
372ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                if (scan.timeout) sb.append("Forced ");
373ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                if (scan.filtered) sb.append("Filter ");
37412991e68679245a717ba3a94df90c57d438e259eVinay Kalia                if (scan.isSuspended) sb.append("Suspended ");
375ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append(scan.results + " results");
376ff0aaa12c3204523136cc75886add779e4727d00Vinay Kalia                sb.append(" (" + scan.scannerId + ")");
377b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                sb.append("\n");
37812991e68679245a717ba3a94df90c57d438e259eVinay Kalia                if (scan.suspendStartTime != 0) {
37912991e68679245a717ba3a94df90c57d438e259eVinay Kalia                    long duration = scan.suspendDuration
38012991e68679245a717ba3a94df90c57d438e259eVinay Kalia                            + (scan.isSuspended ? (elapsedRt - scan.suspendStartTime) : 0);
38112991e68679245a717ba3a94df90c57d438e259eVinay Kalia                    sb.append("      └"
38212991e68679245a717ba3a94df90c57d438e259eVinay Kalia                            + " Suspended Time: " + duration + "ms\n");
38312991e68679245a717ba3a94df90c57d438e259eVinay Kalia                }
384b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            }
385b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
386b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
387a86ae2ce4305fb0af5522b3f46513b137e98836fAjay Panicker        ContextMap.App appEntry = contextMap.getByName(appName);
388a86ae2ce4305fb0af5522b3f46513b137e98836fAjay Panicker        if (appEntry != null && isRegistered) {
389b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            sb.append("  Application ID                     : " +
390b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                      appEntry.id + "\n");
391b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            sb.append("  UUID                               : " +
392b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                      appEntry.uuid + "\n");
393b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
394b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            List<ContextMap.Connection> connections =
395b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker              contextMap.getConnectionByApp(appEntry.id);
396b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
397b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            sb.append("  Connections: " + connections.size() + "\n");
398b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker
399b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            Iterator<ContextMap.Connection> ii = connections.iterator();
400b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            while(ii.hasNext()) {
401b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                ContextMap.Connection connection = ii.next();
402ca4e714ae37050265379fc6604a4e01db9ecae15Ajay Panicker                long connectionTime = SystemClock.elapsedRealtime() - connection.startTime;
403b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                sb.append("    " + connection.connId + ": " +
404b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker                          connection.address + " " + connectionTime + "ms\n");
405b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker            }
406b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        }
407b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker        sb.append("\n");
408b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker    }
409b7af521b6c2d1ccaaea687207dfbcd0c34489a3cAjay Panicker}
410