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

Commit 8a980761 authored by Ajay Panicker's avatar Ajay Panicker Committed by Gerrit Code Review
Browse files

Merge "Move ScanStats to its own file"

parents 5dc01a91 b7af521b
Loading
Loading
Loading
Loading
+207 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.bluetooth.gatt;

import android.bluetooth.le.ScanSettings;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import com.android.bluetooth.btservice.BluetoothProto;
/**
 * ScanStats class helps keep track of information about scans
 * on a per application basis.
 * @hide
 */
/*package*/ class AppScanStats {
    static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /* ContextMap here is needed to grab Apps and Connections */
    ContextMap contextMap;

    class LastScan {
        long duration;
        long timestamp;
        boolean opportunistic;
        boolean background;

        public LastScan(long timestamp, long duration,
                        boolean opportunistic, boolean background) {
            this.duration = duration;
            this.timestamp = timestamp;
            this.opportunistic = opportunistic;
            this.background = background;
        }
    }

    static final int NUM_SCAN_DURATIONS_KEPT = 5;

    String appName;
    int scansStarted = 0;
    int scansStopped = 0;
    boolean isScanning = false;
    boolean isRegistered = false;
    long minScanTime = Long.MAX_VALUE;
    long maxScanTime = 0;
    long totalScanTime = 0;
    List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT + 1);
    long startTime = 0;
    long stopTime = 0;

    public AppScanStats(String name, ContextMap map) {
        appName = name;
        contextMap = map;
    }

    synchronized void recordScanStart(ScanSettings settings) {
        if (isScanning)
            return;

        this.scansStarted++;
        isScanning = true;
        startTime = System.currentTimeMillis();

        LastScan scan = new LastScan(startTime, 0, false, false);
        if (settings != null) {
          scan.opportunistic = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
          scan.background = (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
        }
        lastScans.add(scan);

        BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
        scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_START);
        scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
        scanEvent.setInitiator(appName);
        scanEvent.setEventTimeMillis(System.currentTimeMillis());
        contextMap.addScanEvent(scanEvent);
    }

    synchronized void recordScanStop() {
        if (!isScanning)
          return;

        this.scansStopped++;
        isScanning = false;
        stopTime = System.currentTimeMillis();
        long scanDuration = stopTime - startTime;

        minScanTime = Math.min(scanDuration, minScanTime);
        maxScanTime = Math.max(scanDuration, maxScanTime);
        totalScanTime += scanDuration;

        LastScan curr = lastScans.get(lastScans.size() - 1);
        curr.duration = scanDuration;

        if (lastScans.size() > NUM_SCAN_DURATIONS_KEPT) {
            lastScans.remove(0);
        }

        BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
        scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_STOP);
        scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
        scanEvent.setInitiator(appName);
        scanEvent.setEventTimeMillis(System.currentTimeMillis());
        contextMap.addScanEvent(scanEvent);
    }

    synchronized void dumpToString(StringBuilder sb) {
        long currTime = System.currentTimeMillis();
        long maxScan = maxScanTime;
        long minScan = minScanTime;
        long scanDuration = 0;

        if (lastScans.isEmpty())
            return;

        if (isScanning) {
            scanDuration = currTime - startTime;
            minScan = Math.min(scanDuration, minScan);
            maxScan = Math.max(scanDuration, maxScan);
        }

        if (minScan == Long.MAX_VALUE) {
            minScan = 0;
        }

        long avgScan = 0;
        if (scansStarted > 0) {
            avgScan = (totalScanTime + scanDuration) / scansStarted;
        }

        LastScan lastScan = lastScans.get(lastScans.size() - 1);
        sb.append("  " + appName);
        if (isRegistered) sb.append(" (Registered)");
        if (lastScan.opportunistic) sb.append(" (Opportunistic)");
        if (lastScan.background) sb.append(" (Background)");
        sb.append("\n");

        sb.append("  LE scans (started/stopped)         : " +
                  scansStarted + " / " +
                  scansStopped + "\n");
        sb.append("  Scan time in ms (min/max/avg/total): " +
                  minScan + " / " +
                  maxScan + " / " +
                  avgScan + " / " +
                  totalScanTime + "\n");

        if (lastScans.size() != 0) {
            int lastScansSize = scansStopped < NUM_SCAN_DURATIONS_KEPT ?
                                scansStopped : NUM_SCAN_DURATIONS_KEPT;
            sb.append("  Last " + lastScansSize +
                      " scans                       :\n");

            for (int i = 0; i < lastScansSize; i++) {
                LastScan scan = lastScans.get(i);
                Date timestamp = new Date(scan.timestamp);
                sb.append("    " + dateFormat.format(timestamp) + " - ");
                sb.append(scan.duration + "ms ");
                if (scan.opportunistic) sb.append("Opp ");
                if (scan.background) sb.append("Back");
                sb.append("\n");
            }
        }

        if (isRegistered) {
            ContextMap.App appEntry = contextMap.getByName(appName);
            sb.append("  Application ID                     : " +
                      appEntry.id + "\n");
            sb.append("  UUID                               : " +
                      appEntry.uuid + "\n");

            if (isScanning) {
                sb.append("  Current scan duration in ms        : " +
                          scanDuration + "\n");
            }

            List<ContextMap.Connection> connections =
              contextMap.getConnectionByApp(appEntry.id);

            sb.append("  Connections: " + connections.size() + "\n");

            Iterator<ContextMap.Connection> ii = connections.iterator();
            while(ii.hasNext()) {
                ContextMap.Connection connection = ii.next();
                long connectionTime = System.currentTimeMillis() - connection.startTime;
                sb.append("    " + connection.connId + ": " +
                          connection.address + " " + connectionTime + "ms\n");
            }
        }
        sb.append("\n");
    }
}
+29 −218
Original line number Diff line number Diff line
@@ -15,7 +15,6 @@
 */
package com.android.bluetooth.gatt;

import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -23,13 +22,9 @@ import android.os.IBinder.DeathRecipient;
import android.os.IInterface;
import android.os.RemoteException;
import android.util.Log;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -38,7 +33,6 @@ import java.util.HashMap;
import java.util.Map;

import com.android.bluetooth.btservice.BluetoothProto;

/**
 * Helper class that keeps track of registered GATT applications.
 * This class manages application callbacks and keeps track of GATT connections.
@@ -46,202 +40,7 @@ import com.android.bluetooth.btservice.BluetoothProto;
 */
/*package*/ class ContextMap<T> {
    private static final String TAG = GattServiceConfig.TAG_PREFIX + "ContextMap";

    static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    static final int NUM_SCAN_EVENTS_KEPT = 20;
    ArrayList<BluetoothProto.ScanEvent> mScanEvents =
      new ArrayList<BluetoothProto.ScanEvent>(NUM_SCAN_EVENTS_KEPT);

    /**
     * ScanStats class helps keep track of information about scans
     * on a per application basis.
     */
    class ScanStats {

        class LastScan {
            long durration;
            long timestamp;
            boolean opportunistic;
            boolean background;

            public LastScan(long timestamp, long durration,
                            boolean opportunistic, boolean background) {
                this.durration = durration;
                this.timestamp = timestamp;
                this.opportunistic = opportunistic;
                this.background = background;
            }
        }

        static final int NUM_SCAN_DURATIONS_KEPT = 5;

        String appName;
        int scansStarted = 0;
        int scansStopped = 0;
        boolean isScanning = false;
        boolean isRegistered = false;
        boolean isOpportunisticScan = false;
        boolean isBackgroundScan = false;
        long minScanTime = Long.MAX_VALUE;
        long maxScanTime = 0;
        long totalScanTime = 0;
        List<LastScan> lastScans = new ArrayList<LastScan>(NUM_SCAN_DURATIONS_KEPT + 1);
        long startTime = 0;
        long stopTime = 0;

        public ScanStats(String name) {
            appName = name;
        }

        synchronized void recordScanStart(ScanSettings settings) {
            if (isScanning)
                return;

            this.scansStarted++;
            isScanning = true;
            startTime = System.currentTimeMillis();

            BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
            scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_START);
            scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
            scanEvent.setInitiator(appName);
            scanEvent.setEventTimeMillis(System.currentTimeMillis());

            lastScans.add(new LastScan(startTime, 0, false, false));

            if (settings != null) {
              isOpportunisticScan = settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
              isBackgroundScan = (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
            }

            synchronized(mScanEvents) {
                if(mScanEvents.size() == NUM_SCAN_EVENTS_KEPT)
                    mScanEvents.remove(0);
                mScanEvents.add(scanEvent);
            }
        }

        synchronized void recordScanStop() {
            if (!isScanning)
              return;

            this.scansStopped++;
            isScanning = false;
            stopTime = System.currentTimeMillis();
            long currTime = stopTime - startTime;

            minScanTime = Math.min(currTime, minScanTime);
            maxScanTime = Math.max(currTime, maxScanTime);
            totalScanTime += currTime;

            LastScan curr = lastScans.get(lastScans.size() - 1);
            curr.durration = currTime;
            curr.opportunistic = isOpportunisticScan;
            curr.background = isBackgroundScan;

            isOpportunisticScan = false;
            isBackgroundScan = false;

            if (lastScans.size() > NUM_SCAN_DURATIONS_KEPT) {
                lastScans.remove(0);
            }

            BluetoothProto.ScanEvent scanEvent = new BluetoothProto.ScanEvent();
            scanEvent.setScanEventType(BluetoothProto.ScanEvent.SCAN_EVENT_STOP);
            scanEvent.setScanTechnologyType(BluetoothProto.ScanEvent.SCAN_TECH_TYPE_LE);
            scanEvent.setInitiator(appName);
            scanEvent.setEventTimeMillis(System.currentTimeMillis());
            synchronized(mScanEvents) {
                if (mScanEvents.size() == NUM_SCAN_EVENTS_KEPT)
                    mScanEvents.remove(0);
                mScanEvents.add(scanEvent);
            }
        }

        synchronized void dumpToString(StringBuilder sb) {
            long currTime = System.currentTimeMillis();
            long maxScan = maxScanTime;
            long minScan = minScanTime;
            long currScan = 0;

            if (isScanning) {
                currScan = currTime - startTime;
                minScan = Math.min(currScan, minScan);
                maxScan = Math.max(currScan, maxScan);
            }

            if (minScan == Long.MAX_VALUE) {
                minScan = 0;
            }

            long lastScan = 0;
            if (stopTime != 0) {
                lastScan = currTime - stopTime;
            }

            long avgScan = 0;
            if (scansStarted > 0) {
                avgScan = (totalScanTime + currScan) / scansStarted;
            }

            sb.append("  " + appName);
            if (isRegistered) sb.append(" (Registered)");
            if (isOpportunisticScan) sb.append(" (Opportunistic)");
            if (isBackgroundScan) sb.append(" (Background)");
            sb.append("\n");

            sb.append("  LE scans (started/stopped)         : " +
                      scansStarted + " / " +
                      scansStopped + "\n");
            sb.append("  Scan time in ms (min/max/avg/total): " +
                      minScan + " / " +
                      maxScan + " / " +
                      avgScan + " / " +
                      totalScanTime + "\n");

            if (lastScans.size() != 0) {
                int lastScansSize = scansStopped < NUM_SCAN_DURATIONS_KEPT ?
                                    scansStopped : NUM_SCAN_DURATIONS_KEPT;
                sb.append("  Last " + lastScansSize +
                          " scans                       :\n");

                for (int i = 0; i < lastScansSize; i++) {
                    LastScan scan = lastScans.get(i);
                    Date timestamp = new Date(scan.timestamp);
                    sb.append("    " + dateFormat.format(timestamp) + " - ");
                    sb.append(scan.durration + "ms ");
                    if (scan.opportunistic) sb.append("Opp ");
                    if (scan.background) sb.append("Back");
                    sb.append("\n");
                }
            }

            if (isRegistered) {
                App appEntry = getByName(appName);
                sb.append("  Application ID                     : " +
                          appEntry.id + "\n");
                sb.append("  UUID                               : " +
                          appEntry.uuid + "\n");

                if (isScanning) {
                    sb.append("  Current scan duration in ms        : " +
                              currScan + "\n");
                }

                List<Connection> connections = getConnectionByApp(appEntry.id);
                sb.append("  Connections: " + connections.size() + "\n");

                Iterator<Connection> ii = connections.iterator();
                while(ii.hasNext()) {
                    Connection connection = ii.next();
                    long connectionTime = System.currentTimeMillis() - connection.startTime;
                    sb.append("    " + connection.connId + ": " +
                              connection.address + " " + connectionTime + "ms\n");
                }
            }
            sb.append("\n");
        }
    }

    /**
     * Connection class helps map connection IDs to device addresses.
@@ -335,7 +134,11 @@ import com.android.bluetooth.btservice.BluetoothProto;
    List<App> mApps = new ArrayList<App>();

    /** Internal map to keep track of logging information by app name */
    HashMap<String, ScanStats> mScanStats = new HashMap<String, ScanStats>();
    HashMap<String, AppScanStats> mAppScanStats = new HashMap<String, AppScanStats>();

    /** Internal list of scan events to use with the proto */
    ArrayList<BluetoothProto.ScanEvent> mScanEvents =
        new ArrayList<BluetoothProto.ScanEvent>(NUM_SCAN_EVENTS_KEPT);

    /** Internal list of connected devices **/
    Set<Connection> mConnections = new HashSet<Connection>();
@@ -352,12 +155,12 @@ import com.android.bluetooth.btservice.BluetoothProto;
        }
        synchronized (mApps) {
            mApps.add(new App(uuid, callback, appName));
            ScanStats scanStats = mScanStats.get(appName);
            if (scanStats == null) {
                scanStats = new ScanStats(appName);
                mScanStats.put(appName, scanStats);
            AppScanStats appScanStats = mAppScanStats.get(appName);
            if (appScanStats == null) {
                appScanStats = new AppScanStats(appName, this);
                mAppScanStats.put(appName, appScanStats);
            }
            scanStats.isRegistered = true;
            appScanStats.isRegistered = true;
        }
    }

@@ -371,7 +174,7 @@ import com.android.bluetooth.btservice.BluetoothProto;
                App entry = i.next();
                if (entry.uuid.equals(uuid)) {
                    entry.unlinkToDeath();
                    mScanStats.get(entry.name).isRegistered = false;
                    mAppScanStats.get(entry.name).isRegistered = false;
                    i.remove();
                    break;
                }
@@ -389,7 +192,7 @@ import com.android.bluetooth.btservice.BluetoothProto;
                App entry = i.next();
                if (entry.id == id) {
                    entry.unlinkToDeath();
                    mScanStats.get(entry.name).isRegistered = false;
                    mAppScanStats.get(entry.name).isRegistered = false;
                    i.remove();
                    break;
                }
@@ -467,10 +270,10 @@ import com.android.bluetooth.btservice.BluetoothProto;
    /**
     * Get Logging info by ID
     */
    ScanStats getScanStatsById(int id) {
    AppScanStats getAppScanStatsById(int id) {
        App temp = getById(id);
        if (temp != null) {
            return mScanStats.get(temp.name);
            return mAppScanStats.get(temp.name);
        }
        return null;
    }
@@ -478,8 +281,8 @@ import com.android.bluetooth.btservice.BluetoothProto;
    /**
     * Get Logging info by application name
     */
    ScanStats getScanStatsByName(String name) {
        return mScanStats.get(name);
    AppScanStats getAppScanStatsByName(String name) {
        return mAppScanStats.get(name);
    }

    /**
@@ -577,19 +380,27 @@ import com.android.bluetooth.btservice.BluetoothProto;
        return connectedmap;
    }

    void addScanEvent(BluetoothProto.ScanEvent event) {
        synchronized(mScanEvents) {
            if (mScanEvents.size() == NUM_SCAN_EVENTS_KEPT)
                mScanEvents.remove(0);
            mScanEvents.add(event);
        }
    }

    /**
     * Logs debug information.
     */
    void dump(StringBuilder sb) {
        sb.append("  Entries: " + mScanStats.size() + "\n\n");
        sb.append("  Entries: " + mAppScanStats.size() + "\n\n");

        Iterator<Map.Entry<String,ScanStats>> it = mScanStats.entrySet().iterator();
        Iterator<Map.Entry<String, AppScanStats>> it = mAppScanStats.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, ScanStats> entry = it.next();
            Map.Entry<String, AppScanStats> entry = it.next();

            String name = entry.getKey();
            ScanStats scanStats = entry.getValue();
            scanStats.dumpToString(sb);
            AppScanStats appScanStats = entry.getValue();
            appScanStats.dumpToString(sb);
        }
    }

+2 −2
Original line number Diff line number Diff line
@@ -1338,7 +1338,7 @@ public class GattService extends ProfileService {
        scanClient.hasPeersMacAddressPermission = Utils.checkCallerHasPeersMacAddressPermission(
                this);
        scanClient.legacyForegroundApp = Utils.isLegacyForegroundApp(this, callingPackage);
        mClientMap.getScanStatsById(appIf).recordScanStart(settings);
        mClientMap.getAppScanStatsById(appIf).recordScanStart(settings);
        mScanManager.startScan(scanClient);
    }

@@ -1353,7 +1353,7 @@ public class GattService extends ProfileService {
        int scanQueueSize = mScanManager.getBatchScanQueue().size() +
                mScanManager.getRegularScanQueue().size();
        if (DBG) Log.d(TAG, "stopScan() - queue size =" + scanQueueSize);
        mClientMap.getScanStatsById(client.clientIf).recordScanStop();
        mClientMap.getAppScanStatsById(client.clientIf).recordScanStop();
        mScanManager.stopScan(client);
    }