Merge "Do not allow setting notification access across users." into udc-dev
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index ce806a0..9253f7d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -1120,10 +1120,8 @@
       }
     }
     // Fallback done
-
-    fail_fn(CREATE_ERROR("Unable to find %s:%lld in %s", package_name.data(),
-        ce_data_inode, parent_path.data()));
-    return nullptr;
+    ALOGW("Unable to find %s:%lld in %s", package_name.data(), ce_data_inode, parent_path.data());
+    return "";
   }
 }
 
@@ -1144,11 +1142,19 @@
                         true /*call_fail_fn*/);
 
   std::string ce_data_path = getAppDataDirName(mirrorCePath, package_name, ce_data_inode, fail_fn);
+  if (ce_data_path.empty()) {
+    ALOGE("Ignoring missing CE app data dir for %s\n", package_name.data());
+    return;
+  }
   if (!createAndMountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn,
                              false /*call_fail_fn*/)) {
     // CE might unlocks and the name is decrypted
     // get the name and mount again
     ce_data_path=getAppDataDirName(mirrorCePath, package_name, ce_data_inode, fail_fn);
+    if (ce_data_path.empty()) {
+      ALOGE("Ignoring missing CE app data dir for %s\n", package_name.data());
+      return;
+    }
     mountAppData(package_name, ce_data_path, mirrorCePath, actualCePath, fail_fn);
   }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 9708d9a..06cc068 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -51,6 +51,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -145,6 +146,10 @@
     protected void setListening(boolean listening) {
         mListening = listening;
         if (listening) {
+            // System UI could be restarted while ops are active, so fetch the currently active ops
+            // once System UI starts listening again.
+            fetchCurrentActiveOps();
+
             mAppOps.startWatchingActive(OPS, this);
             mAppOps.startWatchingNoted(OPS, this);
             mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
@@ -177,6 +182,29 @@
         }
     }
 
+    private void fetchCurrentActiveOps() {
+        List<AppOpsManager.PackageOps> packageOps = mAppOps.getPackagesForOps(OPS);
+        for (AppOpsManager.PackageOps op : packageOps) {
+            for (AppOpsManager.OpEntry entry : op.getOps()) {
+                for (Map.Entry<String, AppOpsManager.AttributedOpEntry> attributedOpEntry :
+                        entry.getAttributedOpEntries().entrySet()) {
+                    if (attributedOpEntry.getValue().isRunning()) {
+                        onOpActiveChanged(
+                                entry.getOpStr(),
+                                op.getUid(),
+                                op.getPackageName(),
+                                /* attributionTag= */ attributedOpEntry.getKey(),
+                                /* active= */ true,
+                                // AppOpsManager doesn't have a way to fetch attribution flags or
+                                // chain ID given an op entry, so default them to none.
+                                AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+                                AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Adds a callback that will get notifified when an AppOp of the type the controller tracks
      * changes
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index 56ea703..68321f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -128,8 +128,9 @@
     override fun onStatusEvent(event: StatusEvent) {
         Assert.isMainThread()
 
-        // Ignore any updates until the system is up and running
-        if (isTooEarly() || !isImmersiveIndicatorEnabled()) {
+        // Ignore any updates until the system is up and running. However, for important events that
+        // request to be force visible (like privacy), ignore whether it's too early.
+        if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
index 5fa83ef..6b5a548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
@@ -93,8 +93,9 @@
     @SystemAnimationState override fun getAnimationState() = animationState
 
     override fun onStatusEvent(event: StatusEvent) {
-        // Ignore any updates until the system is up and running
-        if (isTooEarly() || !isImmersiveIndicatorEnabled()) {
+        // Ignore any updates until the system is up and running. However, for important events that
+        // request to be force visible (like privacy), ignore whether it's too early.
+        if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
             return
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 61a6512..e6c36c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -19,6 +19,8 @@
 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertFalse;
 
 import static org.junit.Assert.assertEquals;
@@ -66,6 +68,7 @@
 
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -158,6 +161,204 @@
     }
 
     @Test
+    public void startListening_fetchesCurrentActive_none() {
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of());
+
+        mController.setListening(true);
+
+        assertThat(mController.getActiveAppOps()).isEmpty();
+    }
+
+    /** Regression test for b/294104969. */
+    @Test
+    public void startListening_fetchesCurrentActive_oneActive() {
+        AppOpsManager.PackageOps packageOps = createPackageOp(
+                "package.test",
+                /* packageUid= */ 2,
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                /* isRunning= */ true);
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of(packageOps));
+
+        // WHEN we start listening
+        mController.setListening(true);
+
+        // THEN the active list has the op
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        AppOpItem first = list.get(0);
+        assertThat(first.getPackageName()).isEqualTo("package.test");
+        assertThat(first.getUid()).isEqualTo(2);
+        assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+    }
+
+    @Test
+    public void startListening_fetchesCurrentActive_multiplePackages() {
+        AppOpsManager.PackageOps packageOps1 = createPackageOp(
+                "package.one",
+                /* packageUid= */ 1,
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                /* isRunning= */ true);
+        AppOpsManager.PackageOps packageOps2 = createPackageOp(
+                "package.two",
+                /* packageUid= */ 2,
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                /* isRunning= */ false);
+        AppOpsManager.PackageOps packageOps3 = createPackageOp(
+                "package.three",
+                /* packageUid= */ 3,
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                /* isRunning= */ true);
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of(packageOps1, packageOps2, packageOps3));
+
+        // WHEN we start listening
+        mController.setListening(true);
+
+        // THEN the active list has the ops
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+
+        AppOpItem item0 = list.get(0);
+        assertThat(item0.getPackageName()).isEqualTo("package.one");
+        assertThat(item0.getUid()).isEqualTo(1);
+        assertThat(item0.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+
+        AppOpItem item1 = list.get(1);
+        assertThat(item1.getPackageName()).isEqualTo("package.three");
+        assertThat(item1.getUid()).isEqualTo(3);
+        assertThat(item1.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+    }
+
+    @Test
+    public void startListening_fetchesCurrentActive_multipleEntries() {
+        AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+        when(packageOps.getUid()).thenReturn(1);
+        when(packageOps.getPackageName()).thenReturn("package.one");
+
+        // Entry 1
+        AppOpsManager.OpEntry entry1 = mock(AppOpsManager.OpEntry.class);
+        when(entry1.getOpStr()).thenReturn(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE);
+        AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed1.isRunning()).thenReturn(true);
+        when(entry1.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed1));
+        // Entry 2
+        AppOpsManager.OpEntry entry2 = mock(AppOpsManager.OpEntry.class);
+        when(entry2.getOpStr()).thenReturn(AppOpsManager.OPSTR_CAMERA);
+        AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed2.isRunning()).thenReturn(true);
+        when(entry2.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed2));
+        // Entry 3
+        AppOpsManager.OpEntry entry3 = mock(AppOpsManager.OpEntry.class);
+        when(entry3.getOpStr()).thenReturn(AppOpsManager.OPSTR_FINE_LOCATION);
+        AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed3.isRunning()).thenReturn(false);
+        when(entry3.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed3));
+
+        when(packageOps.getOps()).thenReturn(List.of(entry1, entry2, entry3));
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of(packageOps));
+
+        // WHEN we start listening
+        mController.setListening(true);
+
+        // THEN the active list has the ops
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+
+        AppOpItem first = list.get(0);
+        assertThat(first.getPackageName()).isEqualTo("package.one");
+        assertThat(first.getUid()).isEqualTo(1);
+        assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_PHONE_CALL_MICROPHONE);
+
+        AppOpItem second = list.get(1);
+        assertThat(second.getPackageName()).isEqualTo("package.one");
+        assertThat(second.getUid()).isEqualTo(1);
+        assertThat(second.getCode()).isEqualTo(AppOpsManager.OP_CAMERA);
+    }
+
+    @Test
+    public void startListening_fetchesCurrentActive_multipleAttributes() {
+        AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+        when(packageOps.getUid()).thenReturn(1);
+        when(packageOps.getPackageName()).thenReturn("package.one");
+        AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class);
+        when(entry.getOpStr()).thenReturn(AppOpsManager.OPSTR_RECORD_AUDIO);
+
+        AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed1.isRunning()).thenReturn(false);
+        AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed2.isRunning()).thenReturn(true);
+        AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed3.isRunning()).thenReturn(true);
+        when(entry.getAttributedOpEntries()).thenReturn(
+                Map.of("attr1", attributed1, "attr2", attributed2, "attr3", attributed3));
+
+        when(packageOps.getOps()).thenReturn(List.of(entry));
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of(packageOps));
+
+        // WHEN we start listening
+        mController.setListening(true);
+
+        // THEN the active list has the ops
+        List<AppOpItem> list = mController.getActiveAppOps();
+        // Multiple attributes get merged into one entry in the active ops
+        assertEquals(1, list.size());
+
+        AppOpItem first = list.get(0);
+        assertThat(first.getPackageName()).isEqualTo("package.one");
+        assertThat(first.getUid()).isEqualTo(1);
+        assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_RECORD_AUDIO);
+    }
+
+    /** Regression test for b/294104969. */
+    @Test
+    public void addCallback_existingCallbacksNotifiedOfCurrentActive() {
+        AppOpsManager.PackageOps packageOps1 = createPackageOp(
+                "package.one",
+                /* packageUid= */ 1,
+                AppOpsManager.OPSTR_FINE_LOCATION,
+                /* isRunning= */ true);
+        AppOpsManager.PackageOps packageOps2 = createPackageOp(
+                "package.two",
+                /* packageUid= */ 2,
+                AppOpsManager.OPSTR_RECORD_AUDIO,
+                /* isRunning= */ true);
+        AppOpsManager.PackageOps packageOps3 = createPackageOp(
+                "package.three",
+                /* packageUid= */ 3,
+                AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE,
+                /* isRunning= */ true);
+        when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+                .thenReturn(List.of(packageOps1, packageOps2, packageOps3));
+
+        // WHEN we start listening
+        mController.addCallback(
+                new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
+                mCallback);
+        mTestableLooper.processAllMessages();
+
+        // THEN the callback is notified of the current active ops it cares about
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION,
+                /* uid= */ 1,
+                "package.one",
+                true);
+        verify(mCallback).onActiveStateChanged(
+                AppOpsManager.OP_RECORD_AUDIO,
+                /* uid= */ 2,
+                "package.two",
+                true);
+        verify(mCallback, never()).onActiveStateChanged(
+                AppOpsManager.OP_PHONE_CALL_MICROPHONE,
+                /* uid= */ 3,
+                "package.three",
+                true);
+    }
+
+    @Test
     public void addCallback_includedCode() {
         mController.addCallback(
                 new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
@@ -673,6 +874,22 @@
         assertEquals(AppOpsManager.OP_PHONE_CALL_CAMERA, list.get(cameraIdx).getCode());
     }
 
+    private AppOpsManager.PackageOps createPackageOp(
+            String packageName, int packageUid, String opStr, boolean isRunning) {
+        AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+        when(packageOps.getPackageName()).thenReturn(packageName);
+        when(packageOps.getUid()).thenReturn(packageUid);
+        AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class);
+        when(entry.getOpStr()).thenReturn(opStr);
+        AppOpsManager.AttributedOpEntry attributed = mock(AppOpsManager.AttributedOpEntry.class);
+        when(attributed.isRunning()).thenReturn(isRunning);
+
+        when(packageOps.getOps()).thenReturn(Collections.singletonList(entry));
+        when(entry.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed));
+
+        return packageOps;
+    }
+
     private class TestHandler extends AppOpsControllerImpl.H {
         TestHandler(Looper looper) {
             mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 914301f..8a6dfe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -91,9 +91,6 @@
                 fakeFeatureFlags
             )
 
-        // ensure that isTooEarly() check in SystemStatusAnimationScheduler does not return true
-        systemClock.advanceTime(Process.getStartUptimeMillis() + MIN_UPTIME)
-
         // StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
         whenever(statusBarContentInsetProvider.getStatusBarContentInsetsForCurrentRotation())
             .thenReturn(android.util.Pair(10, 10))
@@ -154,6 +151,21 @@
         assertEquals(0f, batteryChip.view.alpha)
     }
 
+    /** Regression test for b/294104969. */
+    @Test
+    fun testPrivacyStatusEvent_beforeSystemUptime_stillDisplayed() = runTest {
+        initializeSystemStatusAnimationScheduler(testScope = this, advancePastMinUptime = false)
+
+        // WHEN the uptime hasn't quite passed the minimum required uptime...
+        systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME / 2)
+
+        // BUT the event is a privacy event
+        createAndScheduleFakePrivacyEvent()
+
+        // THEN the privacy event still happens
+        assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState())
+    }
+
     @Test
     fun testPrivacyStatusEvent_standardAnimationLifecycle() = runTest {
         // Instantiate class under test with TestScope from runTest
@@ -530,7 +542,10 @@
         return batteryChip
     }
 
-    private fun initializeSystemStatusAnimationScheduler(testScope: TestScope) {
+    private fun initializeSystemStatusAnimationScheduler(
+        testScope: TestScope,
+        advancePastMinUptime: Boolean = true,
+    ) {
         systemStatusAnimationScheduler =
             SystemStatusAnimationSchedulerImpl(
                 systemEventCoordinator,
@@ -542,5 +557,10 @@
             )
         // add a mock listener
         systemStatusAnimationScheduler.addCallback(listener)
+
+        if (advancePastMinUptime) {
+            // ensure that isTooEarly() check in SystemStatusAnimationScheduler does not return true
+            systemClock.advanceTime(Process.getStartUptimeMillis() + MIN_UPTIME)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 8f725be..9072115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -63,6 +63,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -72,6 +73,7 @@
 
 import java.util.function.Predicate;
 
+@Ignore
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 638e394..7d2ec4c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5374,7 +5374,20 @@
                 intent = new Intent(Intent.ACTION_MAIN);
             }
             try {
-                target.send(code, intent, resolvedType, allowlistToken, null,
+                if (allowlistToken != null) {
+                    final int callingUid = Binder.getCallingUid();
+                    final String packageName;
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        packageName = AppGlobals.getPackageManager().getNameForUid(callingUid);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                    Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target."
+                            + " Calling package: " + packageName + "; intent: " + intent
+                            + "; options: " + options);
+                }
+                target.send(code, intent, resolvedType, null, null,
                         requiredPermission, options);
             } catch (RemoteException e) {
             }
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
index aa6ee09..3102639 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySaverPolicyTest.java
@@ -30,6 +30,7 @@
 import android.provider.Settings.Global;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.util.ArrayMap;
 
 import com.android.frameworks.servicestests.R;
@@ -115,6 +116,7 @@
         testServiceDefaultValue_On(ServiceType.NULL);
     }
 
+    @Suppress
     @SmallTest
     public void testGetBatterySaverPolicy_PolicyVibration_DefaultValueCorrect() {
         testServiceDefaultValue_On(ServiceType.VIBRATION);
@@ -200,6 +202,7 @@
         testServiceDefaultValue_On(ServiceType.QUICK_DOZE);
     }
 
+    @Suppress
     @SmallTest
     public void testUpdateConstants_getCorrectData() {
         mBatterySaverPolicy.updateConstantsLocked(BATTERY_SAVER_CONSTANTS, "");