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

Commit 6d4014ce authored by Michael Cheng's avatar Michael Cheng
Browse files

Log when the location indicator switches OFF

Change-Id: I7edb3ec524f9145dbb157165db69e4edaf2144da
Fixes: 440187668
Fixes: 440445458
Flag: android.location.flags.location_indicators_enabled
parent ec53c843
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -105,7 +105,7 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis
    private static final long DEFAULT_RUNNING_TIME_MS = 5000L;
    private static final long ADDITIONAL_RUNNING_TIME_LOCATION_ONLY_MS =
            10_000L - DEFAULT_RUNNING_TIME_MS;
    // LINT.ThenChange(/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt)
    // LINT.ThenChange(/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt, /packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java)
    private static final long DEFAULT_RECENT_TIME_MS = 15000L;

    private static boolean shouldShowIndicators() {
+18 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.flags.Flags;
import android.media.AudioManager;
import android.media.AudioRecordingConfiguration;
import android.os.Handler;
@@ -70,10 +71,14 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
        AppOpsManager.OnOpNotedInternalListener, IndividualSensorPrivacyController.Callback,
        Dumpable {

    // LINT.IfChange
    // This is the minimum time that we will keep AppOps that are noted on record. If multiple
    // occurrences of the same (op, package, uid) happen in a shorter interval, they will not be
    // notified to listeners.
    private static final long NOTED_OP_TIME_DELAY_MS = 5000;
    // This is the minimum time that we will keep AppOps that are noted on record for location only.
    private static final long NOTED_OP_TIME_LOCATION_ONLY_DELAY_MS = 10_000;
    // LINT.ThenChange(/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt, /core/java/android/permission/PermissionUsageHelper.java)
    private static final String TAG = "AppOpsControllerImpl";
    private static final boolean DEBUG = false;

@@ -255,7 +260,7 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
    }

    /**
     * Adds a callback that will get notifified when an AppOp of the type the controller tracks
     * Adds a callback that will get notified when an AppOp of the type the controller tracks
     * changes
     *
     * @param callback Callback to report changes
@@ -368,7 +373,11 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
        }
        // We should keep this so we make sure it cannot time out.
        mBGHandler.removeCallbacksAndMessages(item);
        mBGHandler.scheduleRemoval(item, NOTED_OP_TIME_DELAY_MS);
        final long delay =
                (Flags.locationIndicatorsEnabled() && isOpLocation(code))
                        ? NOTED_OP_TIME_LOCATION_ONLY_DELAY_MS
                        : NOTED_OP_TIME_DELAY_MS;
        mBGHandler.scheduleRemoval(item, delay);
        return createdNew;
    }

@@ -626,6 +635,13 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon
        return false;
    }

    private boolean isOpLocation(int op) {
        for (int i = 0; i < OPS_LOC.length; i++) {
            if (op == OPS_LOC[i]) return true;
        }
        return false;
    }

    protected class H extends Handler {
        H(Looper looper) {
            super(looper);
+77 −33
Original line number Diff line number Diff line
@@ -139,14 +139,15 @@ constructor(
                        logger.logUpdatedItemFromAppOps(code, uid, packageName, active)

                        val procInfo =
                            (activityManager.runningAppProcesses
                                ?: emptyList()).find { it.uid == uid }
                            (activityManager.runningAppProcesses ?: emptyList()).find {
                                it.uid == uid
                            }
                        val importance = procInfo?.importance ?: -1 // Use -1 if process not found
                        logger.logLocationAppOps(
                            uid,
                            packageName,
                            importance,
                            !isBackgroundApp(uid)
                            !isBackgroundApp(uid),
                        )

                        dispatchOnPrivacyItemsChanged()
@@ -290,35 +291,48 @@ constructor(
    private fun logLocationAccesses() {
        // TODO(b/419834493): Add logbuffer logging here for bugreport debugging.
        synchronized(lock) {
            if (!lastHighPowerLocationOp && hasHighPowerLocationAccess) {
                uiEventLogger.log {
                    LocationIndicatorEvent.LOCATION_INDICATOR_MONITOR_HIGH_POWER.id
                }
            }
            if (!lastLocationIndicator && hasNonSystemForegroundLocationAccess) {
                uiEventLogger.log { LocationIndicatorEvent.LOCATION_INDICATOR_NON_SYSTEM_APP.id }
            }
            if (!lastLocationIndicatorWithSystem) {
                if (hasNonSystemForegroundLocationAccess || hasSystemLocationAccess) {
                    uiEventLogger.log { LocationIndicatorEvent.LOCATION_INDICATOR_SYSTEM_APP.id }
                }
            }
            if (!lastLocationIndicatorWithBackround) {
                if (hasNonSystemForegroundLocationAccess || hasBackgroundLocationAccess) {
                    uiEventLogger.log {
                        LocationIndicatorEvent.LOCATION_INDICATOR_BACKGROUND_APP.id
                    }
                }
            }
            if (!lastLocationIndicatorWithSystemAndBackround) {
                if (
            logLocationIndicatorEvent(
                lastState = lastHighPowerLocationOp,
                currentState = hasHighPowerLocationAccess,
                onEvent = LocationIndicatorEvent.LOCATION_INDICATOR_MONITOR_HIGH_POWER,
                offEvent = LocationIndicatorEvent.LOCATION_INDICATOR_MONITOR_HIGH_POWER_OFF,
            )
            logLocationIndicatorEvent(
                lastState = lastLocationIndicator,
                currentState = hasNonSystemForegroundLocationAccess,
                onEvent = LocationIndicatorEvent.LOCATION_INDICATOR_NON_SYSTEM_APP,
                offEvent = LocationIndicatorEvent.LOCATION_INDICATOR_NON_SYSTEM_APP_OFF,
            )

            // No background access
            val hasSystemAccess = hasNonSystemForegroundLocationAccess || hasSystemLocationAccess
            logLocationIndicatorEvent(
                lastState = lastLocationIndicatorWithSystem,
                currentState = hasSystemAccess,
                onEvent = LocationIndicatorEvent.LOCATION_INDICATOR_SYSTEM_APP,
                offEvent = LocationIndicatorEvent.LOCATION_INDICATOR_SYSTEM_APP_OFF,
            )

            // No system access
            val hasBackgroundAccess =
                hasNonSystemForegroundLocationAccess || hasBackgroundLocationAccess
            logLocationIndicatorEvent(
                lastState = lastLocationIndicatorWithBackround,
                currentState = hasBackgroundAccess,
                onEvent = LocationIndicatorEvent.LOCATION_INDICATOR_BACKGROUND_APP,
                offEvent = LocationIndicatorEvent.LOCATION_INDICATOR_BACKGROUND_APP_OFF,
            )

            val hasAllAccess =
                hasNonSystemForegroundLocationAccess ||
                    hasSystemLocationAccess ||
                    hasBackgroundLocationAccess
                ) {
                    uiEventLogger.log { LocationIndicatorEvent.LOCATION_INDICATOR_ALL_APP.id }
                }
            }
            logLocationIndicatorEvent(
                lastLocationIndicatorWithSystemAndBackround,
                hasAllAccess,
                LocationIndicatorEvent.LOCATION_INDICATOR_ALL_APP,
                LocationIndicatorEvent.LOCATION_INDICATOR_ALL_APP_OFF,
            )

            hasHighPowerLocationAccess = false
            hasSystemLocationAccess = false
@@ -327,6 +341,17 @@ constructor(
        }
    }

    private fun logLocationIndicatorEvent(
        lastState: Boolean,
        currentState: Boolean,
        onEvent: LocationIndicatorEvent,
        offEvent: LocationIndicatorEvent,
    ) {
        if (lastState != currentState) {
            uiEventLogger.log(if (currentState) onEvent else offEvent)
        }
    }

    @GuardedBy("lock")
    private fun privacyItemForAppOpEnabledLocked(code: Int): Boolean {
        if (code in OPS_LOCATION) {
@@ -486,24 +511,43 @@ constructor(
        // Copied from LocationControllerImpl.java
        @UiEvent(doc = "Location indicator shown for high power access")
        LOCATION_INDICATOR_MONITOR_HIGH_POWER(935),
        @UiEvent(doc = "Location indicator hidden for high power access")
        LOCATION_INDICATOR_MONITOR_HIGH_POWER_OFF(2417),
        // Copied from LocationControllerImpl.java
        @UiEvent(
            doc =
                "Location indicator shown for system, non-system, foreground app access (i.e., excluding background)"
        )
        LOCATION_INDICATOR_SYSTEM_APP(936),
        @UiEvent(
            doc =
                "Location indicator hidden for system, non-system, foreground app access (i.e., excluding background)"
        )
        LOCATION_INDICATOR_SYSTEM_APP_OFF(2418),
        // Copied from LocationControllerImpl.java
        @UiEvent(
            doc =
                "Location indicator shown for non-system, foreground app access (i.e., excluding system and background)"
        )
        LOCATION_INDICATOR_NON_SYSTEM_APP(937),
        @UiEvent(
            doc =
                "Location indicator hidden for non-system, foreground app access (i.e., excluding system and background)"
        )
        LOCATION_INDICATOR_NON_SYSTEM_APP_OFF(2419),
        @UiEvent(
            doc =
                "Location indicator shown for non-system, foreground, background app access (i.e., excluding system)"
        )
        LOCATION_INDICATOR_BACKGROUND_APP(2325),
        @UiEvent(doc = "Location indicator shown for all access") LOCATION_INDICATOR_ALL_APP(2354);
        @UiEvent(
            doc =
                "Location indicator hidden for non-system, foreground, background app access (i.e., excluding system)"
        )
        LOCATION_INDICATOR_BACKGROUND_APP_OFF(2420),
        @UiEvent(doc = "Location indicator shown for all access") LOCATION_INDICATOR_ALL_APP(2354),
        @UiEvent(doc = "Location indicator hidden for all access")
        LOCATION_INDICATOR_ALL_APP_OFF(2421);

        override fun getId(): Int {
            return id
+1 −1
Original line number Diff line number Diff line
@@ -51,7 +51,7 @@ class PrivacyItemController @Inject constructor(
        // LINT.IfChange
        @VisibleForTesting const val TIME_TO_HOLD_INDICATORS = 5_000L
        @VisibleForTesting const val TIME_TO_HOLD_INDICATORS_FOR_LOCATION = 10_000L
        // LINT.ThenChange(/core/java/android/permission/PermissionUsageHelper.java)
        // LINT.ThenChange(/core/java/android/permission/PermissionUsageHelper.java, /packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java)
    }

    @VisibleForTesting
+32 −0
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.flags.Flags;
import android.media.AudioManager;
import android.media.AudioRecordingConfiguration;
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;

import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -66,6 +68,7 @@ import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -539,6 +542,35 @@ public class AppOpsControllerTest extends SysuiTestCase {
        verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong());
    }

    @Test
    @EnableFlags(Flags.FLAG_LOCATION_INDICATORS_ENABLED)
    public void opNotedScheduledForRemoval_delayIsCorrect() {
        mController.setBGHandler(mMockHandler);
        ArgumentCaptor<AppOpItem> itemCaptor = ArgumentCaptor.forClass(AppOpItem.class);
        ArgumentCaptor<Long> delayCaptor = ArgumentCaptor.forClass(Long.class);

        // Location op is noted
        mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);

        // Non-location op is noted
        mController.onOpNoted(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME,
                TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED);

        // Check they are both scheduled for removal with the correct delays
        verify(mMockHandler, times(2)).scheduleRemoval(itemCaptor.capture(),
                delayCaptor.capture());

        List<AppOpItem> items = itemCaptor.getAllValues();
        List<Long> delays = delayCaptor.getAllValues();

        assertEquals(AppOpsManager.OP_FINE_LOCATION, items.get(0).getCode());
        assertEquals(10000L, delays.get(0).longValue());

        assertEquals(AppOpsManager.OP_RECORD_AUDIO, items.get(1).getCode());
        assertEquals(5000L, delays.get(1).longValue());
    }

    @Test
    public void noItemsAfterStopListening() {
        mController.setBGHandler(mMockHandler);
Loading