• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
startRequesting(String packageName, String providerName, long intervalMs)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      */
stopRequesting(String packageName, String providerName)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 
PackageProviderKey(String packageName, String providerName)67         public PackageProviderKey(String packageName, String providerName) {
68             this.packageName = packageName;
69             this.providerName = providerName;
70         }
71 
72         @Override
equals(Object other)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
hashCode()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 
PackageStatistics()106         private PackageStatistics() {
107             mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
108             mNumActiveRequests = 0;
109             mTotalDurationMs = 0;
110             mFastestIntervalMs = Long.MAX_VALUE;
111             mSlowestIntervalMs = 0;
112         }
113 
startRequesting(long intervalMs)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 
stopRequesting()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          */
getDurationMs()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          */
getTimeSinceFirstRequestMs()160         public long getTimeSinceFirstRequestMs() {
161             return SystemClock.elapsedRealtime() - mInitialElapsedTimeMs;
162         }
163 
164         /**
165          * Returns the fastest interval that has been tracked.
166          */
getFastestIntervalMs()167         public long getFastestIntervalMs() {
168             return mFastestIntervalMs;
169         }
170 
171         /**
172          * Returns the slowest interval that has been tracked.
173          */
getSlowestIntervalMs()174         public long getSlowestIntervalMs() {
175             return mSlowestIntervalMs;
176         }
177 
178         /**
179          * Returns true if a request is active for these tracked statistics.
180          */
isActive()181         public boolean isActive() {
182             return mNumActiveRequests > 0;
183         }
184 
185         @Override
toString()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