LocationRequestStatistics.java revision 2ff96af24de2c22a21de9b56ea8543dccdbdcb9d
1package 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}
206