Log history of location requests in LocationManager.
-Assists with debugging power issues.
Bug: 12824233
Change-Id: Iaaef0dbe00154c7668034a166587671b75d1f3c7
diff --git a/services/core/java/com/android/server/location/LocationRequestStatistics.java b/services/core/java/com/android/server/location/LocationRequestStatistics.java
new file mode 100644
index 0000000..85231bb
--- /dev/null
+++ b/services/core/java/com/android/server/location/LocationRequestStatistics.java
@@ -0,0 +1,205 @@
+package com.android.server.location;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Holds statistics for location requests (active requests by provider).
+ *
+ * <p>Must be externally synchronized.
+ */
+public class LocationRequestStatistics {
+ private static final String TAG = "LocationStats";
+
+ // Maps package name nad provider to location request statistics.
+ public final HashMap<PackageProviderKey, PackageStatistics> statistics
+ = new HashMap<PackageProviderKey, PackageStatistics>();
+
+ /**
+ * Signals that a package has started requesting locations.
+ *
+ * @param packageName Name of package that has requested locations.
+ * @param providerName Name of provider that is requested (e.g. "gps").
+ * @param intervalMs The interval that is requested in ms.
+ */
+ public void startRequesting(String packageName, String providerName, long intervalMs) {
+ PackageProviderKey key = new PackageProviderKey(packageName, providerName);
+ PackageStatistics stats = statistics.get(key);
+ if (stats == null) {
+ stats = new PackageStatistics();
+ statistics.put(key, stats);
+ }
+ stats.startRequesting(intervalMs);
+ }
+
+ /**
+ * Signals that a package has stopped requesting locations.
+ *
+ * @param packageName Name of package that has stopped requesting locations.
+ * @param providerName Provider that is no longer being requested.
+ */
+ public void stopRequesting(String packageName, String providerName) {
+ PackageProviderKey key = new PackageProviderKey(packageName, providerName);
+ PackageStatistics stats = statistics.get(key);
+ if (stats != null) {
+ stats.stopRequesting();
+ } else {
+ // This shouldn't be a possible code path.
+ Log.e(TAG, "Couldn't find package statistics when removing location request.");
+ }
+ }
+
+ /**
+ * A key that holds both package and provider names.
+ */
+ public static class PackageProviderKey {
+ /**
+ * Name of package requesting location.
+ */
+ public final String packageName;
+ /**
+ * Name of provider being requested (e.g. "gps").
+ */
+ public final String providerName;
+
+ public PackageProviderKey(String packageName, String providerName) {
+ this.packageName = packageName;
+ this.providerName = providerName;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof PackageProviderKey)) {
+ return false;
+ }
+
+ PackageProviderKey otherKey = (PackageProviderKey) other;
+ return packageName.equals(otherKey.packageName)
+ && providerName.equals(otherKey.providerName);
+ }
+
+ @Override
+ public int hashCode() {
+ return packageName.hashCode() + 31 * providerName.hashCode();
+ }
+ }
+
+ /**
+ * Usage statistics for a package/provider pair.
+ */
+ public static class PackageStatistics {
+ // Time when this package first requested location.
+ private final long mInitialElapsedTimeMs;
+ // Number of active location requests this package currently has.
+ private int mNumActiveRequests;
+ // Time when this package most recently went from not requesting location to requesting.
+ private long mLastActivitationElapsedTimeMs;
+ // The fastest interval this package has ever requested.
+ private long mFastestIntervalMs;
+ // The slowest interval this package has ever requested.
+ private long mSlowestIntervalMs;
+ // The total time this app has requested location (not including currently running requests).
+ private long mTotalDurationMs;
+
+ private PackageStatistics() {
+ mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
+ mNumActiveRequests = 0;
+ mTotalDurationMs = 0;
+ mFastestIntervalMs = Long.MAX_VALUE;
+ mSlowestIntervalMs = 0;
+ }
+
+ private void startRequesting(long intervalMs) {
+ if (mNumActiveRequests == 0) {
+ mLastActivitationElapsedTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ if (intervalMs < mFastestIntervalMs) {
+ mFastestIntervalMs = intervalMs;
+ }
+
+ if (intervalMs > mSlowestIntervalMs) {
+ mSlowestIntervalMs = intervalMs;
+ }
+
+ mNumActiveRequests++;
+ }
+
+ private void stopRequesting() {
+ if (mNumActiveRequests <= 0) {
+ // Shouldn't be a possible code path
+ Log.e(TAG, "Reference counting corrupted in usage statistics.");
+ return;
+ }
+
+ mNumActiveRequests--;
+ if (mNumActiveRequests == 0) {
+ long lastDurationMs
+ = SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
+ mTotalDurationMs += lastDurationMs;
+ }
+ }
+
+ /**
+ * Returns the duration that this request has been active.
+ */
+ public long getDurationMs() {
+ long currentDurationMs = mTotalDurationMs;
+ if (mNumActiveRequests > 0) {
+ currentDurationMs
+ += SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
+ }
+ return currentDurationMs;
+ }
+
+ /**
+ * Returns the time since the initial request in ms.
+ */
+ public long getTimeSinceFirstRequestMs() {
+ return SystemClock.elapsedRealtime() - mInitialElapsedTimeMs;
+ }
+
+ /**
+ * Returns the fastest interval that has been tracked.
+ */
+ public long getFastestIntervalMs() {
+ return mFastestIntervalMs;
+ }
+
+ /**
+ * Returns the slowest interval that has been tracked.
+ */
+ public long getSlowestIntervalMs() {
+ return mSlowestIntervalMs;
+ }
+
+ /**
+ * Returns true if a request is active for these tracked statistics.
+ */
+ public boolean isActive() {
+ return mNumActiveRequests > 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ if (mFastestIntervalMs == mSlowestIntervalMs) {
+ s.append("Interval ").append(mFastestIntervalMs / 1000).append(" seconds");
+ } else {
+ s.append("Min interval ").append(mFastestIntervalMs / 1000).append(" seconds");
+ s.append(": Max interval ").append(mSlowestIntervalMs / 1000).append(" seconds");
+ }
+ s.append(": Duration requested ")
+ .append((getDurationMs() / 1000) / 60)
+ .append(" out of the last ")
+ .append((getTimeSinceFirstRequestMs() / 1000) / 60)
+ .append(" minutes");
+ if (isActive()) {
+ s.append(": Currently active");
+ }
+ return s.toString();
+ }
+ }
+}