Home | History | Annotate | Download | only in location
      1 package com.android.server.location;
      2 
      3 import android.os.SystemClock;
      4 import android.util.Log;
      5 
      6 import java.util.HashMap;
      7 
      8 /**
      9  * Holds statistics for location requests (active requests by provider).
     10  *
     11  * <p>Must be externally synchronized.
     12  */
     13 public class LocationRequestStatistics {
     14     private static final String TAG = "LocationStats";
     15 
     16     // Maps package name and provider to location request statistics.
     17     public final HashMap<PackageProviderKey, PackageStatistics> statistics
     18             = new HashMap<PackageProviderKey, PackageStatistics>();
     19 
     20     /**
     21      * Signals that a package has started requesting locations.
     22      *
     23      * @param packageName Name of package that has requested locations.
     24      * @param providerName Name of provider that is requested (e.g. "gps").
     25      * @param intervalMs The interval that is requested in ms.
     26      */
     27     public void startRequesting(String packageName, String providerName, long intervalMs) {
     28         PackageProviderKey key = new PackageProviderKey(packageName, providerName);
     29         PackageStatistics stats = statistics.get(key);
     30         if (stats == null) {
     31             stats = new PackageStatistics();
     32             statistics.put(key, stats);
     33         }
     34         stats.startRequesting(intervalMs);
     35     }
     36 
     37     /**
     38      * Signals that a package has stopped requesting locations.
     39      *
     40      * @param packageName Name of package that has stopped requesting locations.
     41      * @param providerName Provider that is no longer being requested.
     42      */
     43     public void stopRequesting(String packageName, String providerName) {
     44         PackageProviderKey key = new PackageProviderKey(packageName, providerName);
     45         PackageStatistics stats = statistics.get(key);
     46         if (stats != null) {
     47             stats.stopRequesting();
     48         } else {
     49             // This shouldn't be a possible code path.
     50             Log.e(TAG, "Couldn't find package statistics when removing location request.");
     51         }
     52     }
     53 
     54     /**
     55      * A key that holds both package and provider names.
     56      */
     57     public static class PackageProviderKey {
     58         /**
     59          * Name of package requesting location.
     60          */
     61         public final String packageName;
     62         /**
     63          * Name of provider being requested (e.g. "gps").
     64          */
     65         public final String providerName;
     66 
     67         public PackageProviderKey(String packageName, String providerName) {
     68             this.packageName = packageName;
     69             this.providerName = providerName;
     70         }
     71 
     72         @Override
     73         public boolean equals(Object other) {
     74             if (!(other instanceof PackageProviderKey)) {
     75                 return false;
     76             }
     77 
     78             PackageProviderKey otherKey = (PackageProviderKey) other;
     79             return packageName.equals(otherKey.packageName)
     80                     && providerName.equals(otherKey.providerName);
     81         }
     82 
     83         @Override
     84         public int hashCode() {
     85             return packageName.hashCode() + 31 * providerName.hashCode();
     86         }
     87     }
     88 
     89     /**
     90      * Usage statistics for a package/provider pair.
     91      */
     92     public static class PackageStatistics {
     93         // Time when this package first requested location.
     94         private final long mInitialElapsedTimeMs;
     95         // Number of active location requests this package currently has.
     96         private int mNumActiveRequests;
     97         // Time when this package most recently went from not requesting location to requesting.
     98         private long mLastActivitationElapsedTimeMs;
     99         // The fastest interval this package has ever requested.
    100         private long mFastestIntervalMs;
    101         // The slowest interval this package has ever requested.
    102         private long mSlowestIntervalMs;
    103         // The total time this app has requested location (not including currently running requests).
    104         private long mTotalDurationMs;
    105 
    106         private PackageStatistics() {
    107             mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
    108             mNumActiveRequests = 0;
    109             mTotalDurationMs = 0;
    110             mFastestIntervalMs = Long.MAX_VALUE;
    111             mSlowestIntervalMs = 0;
    112         }
    113 
    114         private void startRequesting(long intervalMs) {
    115             if (mNumActiveRequests == 0) {
    116                 mLastActivitationElapsedTimeMs = SystemClock.elapsedRealtime();
    117             }
    118 
    119             if (intervalMs < mFastestIntervalMs) {
    120                 mFastestIntervalMs = intervalMs;
    121             }
    122 
    123             if (intervalMs > mSlowestIntervalMs) {
    124                 mSlowestIntervalMs = intervalMs;
    125             }
    126 
    127             mNumActiveRequests++;
    128         }
    129 
    130         private void stopRequesting() {
    131             if (mNumActiveRequests <= 0) {
    132                 // Shouldn't be a possible code path
    133                 Log.e(TAG, "Reference counting corrupted in usage statistics.");
    134                 return;
    135             }
    136 
    137             mNumActiveRequests--;
    138             if (mNumActiveRequests == 0) {
    139                 long lastDurationMs
    140                         = SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
    141                 mTotalDurationMs += lastDurationMs;
    142             }
    143         }
    144 
    145         /**
    146          * Returns the duration that this request has been active.
    147          */
    148         public long getDurationMs() {
    149             long currentDurationMs = mTotalDurationMs;
    150             if (mNumActiveRequests > 0) {
    151                 currentDurationMs
    152                         += SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
    153             }
    154             return currentDurationMs;
    155         }
    156 
    157         /**
    158          * Returns the time since the initial request in ms.
    159          */
    160         public long getTimeSinceFirstRequestMs() {
    161             return SystemClock.elapsedRealtime() - mInitialElapsedTimeMs;
    162         }
    163 
    164         /**
    165          * Returns the fastest interval that has been tracked.
    166          */
    167         public long getFastestIntervalMs() {
    168             return mFastestIntervalMs;
    169         }
    170 
    171         /**
    172          * Returns the slowest interval that has been tracked.
    173          */
    174         public long getSlowestIntervalMs() {
    175             return mSlowestIntervalMs;
    176         }
    177 
    178         /**
    179          * Returns true if a request is active for these tracked statistics.
    180          */
    181         public boolean isActive() {
    182             return mNumActiveRequests > 0;
    183         }
    184 
    185         @Override
    186         public String toString() {
    187             StringBuilder s = new StringBuilder();
    188             if (mFastestIntervalMs == mSlowestIntervalMs) {
    189                 s.append("Interval ").append(mFastestIntervalMs / 1000).append(" seconds");
    190             } else {
    191                 s.append("Min interval ").append(mFastestIntervalMs / 1000).append(" seconds");
    192                 s.append(": Max interval ").append(mSlowestIntervalMs / 1000).append(" seconds");
    193             }
    194             s.append(": Duration requested ")
    195                     .append((getDurationMs() / 1000) / 60)
    196                     .append(" out of the last ")
    197                     .append((getTimeSinceFirstRequestMs() / 1000) / 60)
    198                     .append(" minutes");
    199             if (isActive()) {
    200                 s.append(": Currently active");
    201             }
    202             return s.toString();
    203         }
    204     }
    205 }
    206