blob: 85231bbaa61df2785196f6d978607cd0e138dd3f [file] [log] [blame]
David Christie2ff96af2014-01-30 16:09:37 -08001package com.android.server.location;
2
3import android.os.SystemClock;
4import android.util.Log;
5
6import java.util.HashMap;
7
8/**
9 * Holds statistics for location requests (active requests by provider).
10 *
11 * <p>Must be externally synchronized.
12 */
13public class LocationRequestStatistics {
14 private static final String TAG = "LocationStats";
15
16 // Maps package name nad 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}