blob: 264026eeb0f5b28206f9d6c0b6f4047cbc170002 [file] [log] [blame]
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 and 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();
}
}
}