Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7075e05 authored by Wyatt Riley's avatar Wyatt Riley
Browse files

Adding Location Request History Foreground Tracking

Improving adb shell dumpsys location in light of
Background Location Limits

Bug: 73598719
Test: atest and on-device verification
Change-Id: I0b24d14f1557a36d9162770c9bc7081d9787d0cf
parent 7f03f1a2
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -424,7 +424,7 @@ public class LocationManagerService extends ILocationManager.Stub {
                            Log.d(TAG, "request from uid " + uid + " is now "
                                    + (foreground ? "foreground" : "background)"));
                        }
                        record.mIsForegroundUid = foreground;
                        record.updateForeground(foreground);

                        if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
                            affectedProviders.add(provider);
@@ -1902,7 +1902,17 @@ public class LocationManagerService extends ILocationManager.Stub {

            // Update statistics for historical location requests by package/provider
            mRequestStatistics.startRequesting(
                    mReceiver.mIdentity.mPackageName, provider, request.getInterval());
                    mReceiver.mIdentity.mPackageName, provider, request.getInterval(),
                    mIsForegroundUid);
        }

        /**
         * Method to be called when record changes foreground/background
         */
        void updateForeground(boolean isForeground){
            mIsForegroundUid = isForeground;
            mRequestStatistics.updateForeground(
                    mReceiver.mIdentity.mPackageName, mProvider, isForeground);
        }

        /**
+51 −5
Original line number Diff line number Diff line
@@ -24,7 +24,8 @@ public class LocationRequestStatistics {
     * @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) {
    public void startRequesting(String packageName, String providerName, long intervalMs,
            boolean isForeground) {
        PackageProviderKey key = new PackageProviderKey(packageName, providerName);
        PackageStatistics stats = statistics.get(key);
        if (stats == null) {
@@ -32,6 +33,7 @@ public class LocationRequestStatistics {
            statistics.put(key, stats);
        }
        stats.startRequesting(intervalMs);
        stats.updateForeground(isForeground);
    }

    /**
@@ -45,9 +47,20 @@ public class LocationRequestStatistics {
        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.");
        }
    }

    /**
     * Signals that a package possibly switched background/foreground.
     *
     * @param packageName Name of package that has stopped requesting locations.
     * @param providerName Provider that is no longer being requested.
     */
    public void updateForeground(String packageName, String providerName, boolean isForeground) {
        PackageProviderKey key = new PackageProviderKey(packageName, providerName);
        PackageStatistics stats = statistics.get(key);
        if (stats != null) {
            stats.updateForeground(isForeground);
        }
    }

@@ -103,12 +116,21 @@ public class LocationRequestStatistics {
        // The total time this app has requested location (not including currently running requests).
        private long mTotalDurationMs;

        // Time when this package most recently went to foreground, requesting location. 0 means
        // not currently in foreground.
        private long mLastForegroundElapsedTimeMs;
        // The time this app has requested location (not including currently running requests), while
        // in foreground.
        private long mForegroundDurationMs;

        private PackageStatistics() {
            mInitialElapsedTimeMs = SystemClock.elapsedRealtime();
            mNumActiveRequests = 0;
            mTotalDurationMs = 0;
            mFastestIntervalMs = Long.MAX_VALUE;
            mSlowestIntervalMs = 0;
            mForegroundDurationMs = 0;
            mLastForegroundElapsedTimeMs = 0;
        }

        private void startRequesting(long intervalMs) {
@@ -127,6 +149,15 @@ public class LocationRequestStatistics {
            mNumActiveRequests++;
        }

        private void updateForeground(boolean isForeground) {
            long nowElapsedTimeMs = SystemClock.elapsedRealtime();
            // if previous interval was foreground, accumulate before resetting start
            if (mLastForegroundElapsedTimeMs != 0) {
                mForegroundDurationMs += (nowElapsedTimeMs - mLastForegroundElapsedTimeMs);
            }
            mLastForegroundElapsedTimeMs = isForeground ? nowElapsedTimeMs : 0;
        }

        private void stopRequesting() {
            if (mNumActiveRequests <= 0) {
                // Shouldn't be a possible code path
@@ -139,6 +170,7 @@ public class LocationRequestStatistics {
                long lastDurationMs
                        = SystemClock.elapsedRealtime() - mLastActivitationElapsedTimeMs;
                mTotalDurationMs += lastDurationMs;
                updateForeground(false);
            }
        }

@@ -154,6 +186,18 @@ public class LocationRequestStatistics {
            return currentDurationMs;
        }

        /**
         * Returns the duration that this request has been active.
         */
        public long getForegroundDurationMs() {
            long currentDurationMs = mForegroundDurationMs;
            if (mLastForegroundElapsedTimeMs != 0 ) {
                currentDurationMs
                        += SystemClock.elapsedRealtime() - mLastForegroundElapsedTimeMs;
            }
            return currentDurationMs;
        }

        /**
         * Returns the time since the initial request in ms.
         */
@@ -193,7 +237,9 @@ public class LocationRequestStatistics {
            }
            s.append(": Duration requested ")
                    .append((getDurationMs() / 1000) / 60)
                    .append(" out of the last ")
                    .append(" total, ")
                    .append((getForegroundDurationMs() / 1000) / 60)
                    .append(" foreground, out of the last ")
                    .append((getTimeSinceFirstRequestMs() / 1000) / 60)
                    .append(" minutes");
            if (isActive()) {
+33 −11
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
     * Tests that adding a single package works correctly.
     */
    public void testSinglePackage() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);

        assertEquals(1, mStatistics.statistics.size());
        PackageProviderKey key = mStatistics.statistics.keySet().iterator().next();
@@ -47,9 +47,9 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
     * Tests that adding a single package works correctly when it is stopped and restarted.
     */
    public void testSinglePackage_stopAndRestart() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);
        mStatistics.stopRequesting(PACKAGE1, PROVIDER1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);

        assertEquals(1, mStatistics.statistics.size());
        PackageProviderKey key = mStatistics.statistics.keySet().iterator().next();
@@ -69,8 +69,8 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
     * Tests that adding a single package works correctly when multiple intervals are used.
     */
    public void testSinglePackage_multipleIntervals() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL2);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL2, true);

        assertEquals(1, mStatistics.statistics.size());
        PackageProviderKey key = mStatistics.statistics.keySet().iterator().next();
@@ -91,8 +91,8 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
     * Tests that adding a single package works correctly when multiple providers are used.
     */
    public void testSinglePackage_multipleProviders() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL2);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL2, true);

        assertEquals(2, mStatistics.statistics.size());
        PackageProviderKey key1 = new PackageProviderKey(PACKAGE1, PROVIDER1);
@@ -120,10 +120,10 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
     * Tests that adding multiple packages works correctly.
     */
    public void testMultiplePackages() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL2);
        mStatistics.startRequesting(PACKAGE2, PROVIDER1, INTERVAL1);
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL2, true);
        mStatistics.startRequesting(PACKAGE2, PROVIDER1, INTERVAL1, true);

        assertEquals(3, mStatistics.statistics.size());
        PackageProviderKey key1 = new PackageProviderKey(PACKAGE1, PROVIDER1);
@@ -165,11 +165,33 @@ public class LocationRequestStatisticsTest extends AndroidTestCase {
        assertFalse(stats3.isActive());
    }

    /**
     * Tests that switching foreground & background states accmulates time reasonably.
     */
    public void testForegroundBackground() {
        mStatistics.startRequesting(PACKAGE1, PROVIDER1, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE1, PROVIDER2, INTERVAL1, true);
        mStatistics.startRequesting(PACKAGE2, PROVIDER1, INTERVAL1, false);

        mStatistics.updateForeground(PACKAGE1, PROVIDER2, false);
        mStatistics.updateForeground(PACKAGE2, PROVIDER1, true);

        mStatistics.stopRequesting(PACKAGE1, PROVIDER1);

        for (PackageStatistics stats : mStatistics.statistics.values()) {
            verifyStatisticsTimes(stats);
        }
    }

    private void verifyStatisticsTimes(PackageStatistics stats) {
        long durationMs = stats.getDurationMs();
        long foregroundDurationMs = stats.getForegroundDurationMs();
        long timeSinceFirstRequestMs = stats.getTimeSinceFirstRequestMs();
        long maxDeltaMs = SystemClock.elapsedRealtime() - mStartElapsedRealtimeMs;
        assertTrue("Duration is too small", durationMs >= 0);
        assertTrue("Duration is too large", durationMs <= maxDeltaMs);
        assertTrue("Foreground Duration is too small", foregroundDurationMs >= 0);
        assertTrue("Foreground Duration is too large", foregroundDurationMs <= maxDeltaMs);
        assertTrue("Time since first request is too large", timeSinceFirstRequestMs <= maxDeltaMs);
    }
}