Merge "Pass title and description as intent params to the picker" into main
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0947e33..5a3a8d5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.app.Instrumentation.DEBUG_FINISH_ACTIVITY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.inMultiWindowMode;
import static android.os.Process.myUid;
@@ -7297,6 +7298,9 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void finish(int finishTask) {
+ if (DEBUG_FINISH_ACTIVITY) {
+ Log.d("Instrumentation", "finishActivity: finishTask=" + finishTask, new Throwable());
+ }
if (mParent == null) {
int resultCode;
Intent resultData;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index db216b1..be27046 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -107,6 +107,8 @@
// If set, will print the stack trace for activity starts within the process
static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
+ static final boolean DEBUG_FINISH_ACTIVITY = Build.IS_DEBUGGABLE &&
+ SystemProperties.getBoolean("persist.wm.debug.finish_activity", false);
/**
* @hide
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index ba356bb..6514872 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -16,6 +16,8 @@
package android.database;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.BytesLong;
import android.annotation.IntRange;
import android.compat.annotation.UnsupportedAppUsage;
@@ -640,6 +642,7 @@
*/
public boolean putBlob(byte[] value,
@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
+ requireNonNull(value);
acquireReference();
try {
return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
@@ -658,6 +661,7 @@
*/
public boolean putString(String value,
@IntRange(from = 0) int row, @IntRange(from = 0) int column) {
+ requireNonNull(value);
acquireReference();
try {
return nativePutString(mWindowPtr, value, row - mStartPos, column);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index cbac912..ca3e3d2 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -569,7 +569,6 @@
return native_setup(
new WeakReference<>(this),
cameraId,
- ActivityThread.currentOpPackageName(),
rotationOverride,
forceSlowJpegMode,
clientAttribution.getParcel(),
@@ -660,7 +659,6 @@
private native int native_setup(
Object cameraThis,
int cameraId,
- String packageName,
int rotationOverride,
boolean forceSlowJpegMode,
Parcel clientAttributionParcel,
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 2dbd4b8..6201359 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -980,6 +980,8 @@
clientAttribution.uid = USE_CALLING_UID;
clientAttribution.pid = USE_CALLING_PID;
clientAttribution.deviceId = contextAttribution.deviceId;
+ clientAttribution.packageName = mContext.getOpPackageName();
+ clientAttribution.attributionTag = mContext.getAttributionTag();
clientAttribution.next = new AttributionSourceState[0];
return clientAttribution;
}
@@ -1041,8 +1043,6 @@
cameraService.connectDevice(
callbacks,
cameraId,
- mContext.getOpPackageName(),
- mContext.getAttributionTag(),
oomScoreOffset,
mContext.getApplicationInfo().targetSdkVersion,
rotationOverride,
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index b8fd3d0..3f74fac 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -582,8 +582,8 @@
// connect to camera service
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint cameraId, jstring clientPackageName,
- jint rotationOverride, jboolean forceSlowJpegMode,
+ jint cameraId, jint rotationOverride,
+ jboolean forceSlowJpegMode,
jobject jClientAttributionParcel,
jint devicePolicy) {
AttributionSourceState clientAttribution;
@@ -591,16 +591,8 @@
return -EACCES;
}
- // Convert jstring to String16
- const char16_t *rawClientName = reinterpret_cast<const char16_t*>(
- env->GetStringChars(clientPackageName, NULL));
- jsize rawClientNameLen = env->GetStringLength(clientPackageName);
- std::string clientName = toStdString(rawClientName, rawClientNameLen);
- env->ReleaseStringChars(clientPackageName,
- reinterpret_cast<const jchar*>(rawClientName));
-
int targetSdkVersion = android_get_application_target_sdk_version();
- sp<Camera> camera = Camera::connect(cameraId, clientName, targetSdkVersion, rotationOverride,
+ sp<Camera> camera = Camera::connect(cameraId, targetSdkVersion, rotationOverride,
forceSlowJpegMode, clientAttribution, devicePolicy);
if (camera == NULL) {
return -EACCES;
@@ -1089,7 +1081,7 @@
(void *)android_hardware_Camera_getNumberOfCameras},
{"_getCameraInfo", "(IILandroid/os/Parcel;ILandroid/hardware/Camera$CameraInfo;)V",
(void *)android_hardware_Camera_getCameraInfo},
- {"native_setup", "(Ljava/lang/Object;ILjava/lang/String;IZLandroid/os/Parcel;I)I",
+ {"native_setup", "(Ljava/lang/Object;IIZLandroid/os/Parcel;I)I",
(void *)android_hardware_Camera_native_setup},
{"native_release", "()V", (void *)android_hardware_Camera_release},
{"setPreviewSurface", "(Landroid/view/Surface;)V",
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 822a07c..544f0f3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -894,9 +894,7 @@
private static boolean isDraggingToFullscreenAllowed(
@NonNull DividerAttributes dividerAttributes) {
- // TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is
- // updated to v7.
- return false;
+ return dividerAttributes.isDraggingToFullscreenAllowed();
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index b1cbe8d..3572d16 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -97,7 +97,7 @@
adb logcat -s "SurfaceControlRegistry"
```
-## Tracing activity starts in the app process
+## Tracing activity starts & finishes in the app process
It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
@@ -113,6 +113,19 @@
adb reboot
```
+Likewise, to trace where a finish() call may be made in the app process, you can enable this system
+property:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.finish_activity true
+adb reboot
+adb logcat -s "Instrumentation"
+
+# Disabling
+adb shell setprop persist.wm.debug.finish_activity \"\"
+adb reboot
+```
+
## Dumps
Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 102d21a..43acbb1 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -2246,6 +2246,8 @@
clientAttribution.uid = -1; // USE_CALLING_UID
clientAttribution.pid = -1; // USE_CALLING_PID
clientAttribution.deviceId = contextAttribution.deviceId;
+ clientAttribution.packageName = context.getOpPackageName();
+ clientAttribution.attributionTag = context.getAttributionTag();
clientAttribution.next = new AttributionSourceState[0];
return clientAttribution;
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index ad3374a..ac85ab7 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -169,10 +169,8 @@
ICameraClient dummyCallbacks = new DummyCameraClient();
- String clientPackageName = getContext().getPackageName();
-
ICamera cameraUser = mUtils.getCameraService()
- .connect(dummyCallbacks, cameraId, clientPackageName,
+ .connect(dummyCallbacks, cameraId,
getContext().getApplicationInfo().targetSdkVersion,
ICameraService.ROTATION_OVERRIDE_NONE,
/*forceSlowJpegMode*/false,
@@ -267,8 +265,6 @@
ICameraDeviceCallbacks dummyCallbacks = new DummyCameraDeviceCallbacks();
- String clientPackageName = getContext().getPackageName();
- String clientAttributionTag = getContext().getAttributionTag();
AttributionSourceState clientAttribution =
CameraTestUtils.getClientAttribution(mContext);
clientAttribution.deviceId = DEVICE_ID_DEFAULT;
@@ -277,7 +273,6 @@
ICameraDeviceUser cameraUser =
mUtils.getCameraService().connectDevice(
dummyCallbacks, String.valueOf(cameraId),
- clientPackageName, clientAttributionTag,
0 /*oomScoreOffset*/,
getContext().getApplicationInfo().targetSdkVersion,
ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 0ab1ee9..35ad924 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -242,9 +242,6 @@
ICameraDeviceCallbacks.Stub dummyCallbacks = new DummyCameraDeviceCallbacks();
- String clientPackageName = getContext().getPackageName();
- String clientAttributionTag = getContext().getAttributionTag();
-
mMockCb = spy(dummyCallbacks);
AttributionSourceState clientAttribution = CameraTestUtils.getClientAttribution(mContext);
@@ -252,7 +249,6 @@
clientAttribution.uid = ICameraService.USE_CALLING_UID;
mCameraUser = mUtils.getCameraService().connectDevice(mMockCb, mCameraId,
- clientPackageName, clientAttributionTag,
/*oomScoreOffset*/0, getContext().getApplicationInfo().targetSdkVersion,
ICameraService.ROTATION_OVERRIDE_NONE, clientAttribution, DEVICE_POLICY_DEFAULT);
assertNotNull(String.format("Camera %s was null", mCameraId), mCameraUser);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 861c405..fd4fc20 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -740,8 +740,6 @@
// The settings provider must hold its lock when calling here.
@GuardedBy("mLock")
public void removeSettingsForPackageLocked(String packageName) {
- boolean removedSomething = false;
-
final int settingCount = mSettings.size();
for (int i = settingCount - 1; i >= 0; i--) {
String name = mSettings.keyAt(i);
@@ -752,14 +750,9 @@
}
Setting setting = mSettings.valueAt(i);
if (packageName.equals(setting.packageName)) {
- mSettings.removeAt(i);
- removedSomething = true;
+ deleteSettingLocked(setting.name);
}
}
-
- if (removedSomething) {
- scheduleWriteIfNeededLocked();
- }
}
// The settings provider must hold its lock when calling here.
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 4b4ced3..48ce49d 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -988,7 +988,40 @@
}
@Test
- public void testGetFlagOverrideToSync() {
+ public void testMemoryUsagePerPackage_StatsUpdatedOnAppDataCleared() {
+ SettingsState settingsState =
+ new SettingsState(
+ InstrumentationRegistry.getContext(), mLock, mSettingsFile, 1,
+ SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+ final String testKey1 = SETTING_NAME;
+ final String testKey2 = SETTING_NAME + "_2";
+ final String testValue1 = Strings.repeat("A", 9000);
+ final String testValue2 = Strings.repeat("A", 9001);
+ final String packageName = "p";
+ // Inserting the first setting should be okay
+ settingsState.insertSettingLocked(testKey1, testValue1, null, true, packageName);
+ int expectedMemUsageForPackage = (testKey1.length() + testValue1.length()
+ + testValue1.length() /* size for default */) * Character.BYTES;
+ assertEquals(expectedMemUsageForPackage, settingsState.getMemoryUsage(packageName));
+ // Inserting the second setting should fail
+ try {
+ settingsState.insertSettingLocked(testKey2, testValue2, null, true, packageName);
+ fail("Should throw because it exceeded max memory usage per package");
+ } catch (IllegalStateException ex) {
+ assertTrue(ex.getMessage().startsWith("You are adding too many system settings."));
+ }
+ // Now clear app data and check that the memory usage is cleared
+ settingsState.removeSettingsForPackageLocked(packageName);
+ assertEquals(0, settingsState.getMemoryUsage(packageName));
+ // Try inserting the second setting again and it should go through
+ settingsState.insertSettingLocked(testKey2, testValue2, null, true, packageName);
+ expectedMemUsageForPackage = (testKey2.length() + testValue2.length()
+ + testValue2.length() /* size for default */) * Character.BYTES;
+ assertEquals(expectedMemUsageForPackage, settingsState.getMemoryUsage(packageName));
+ }
+
+ @Test
+ public void testGetFlagOverrideToSync() {
int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
Object lock = new Object();
SettingsState settingsState =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 887e349..25e91be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.composable
+import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
@@ -48,6 +49,16 @@
fun SceneScope.Content(
modifier: Modifier = Modifier,
) {
+ val isContentVisible: Boolean by viewModel.isContentVisible.collectAsStateWithLifecycle()
+ if (!isContentVisible) {
+ // If the content isn't supposed to be visible, show a large empty box as it's needed
+ // for scene transition animations (can't just skip rendering everything or shared
+ // elements won't have correct final/initial bounds from animating in and out of the
+ // lockscreen scene).
+ Box(modifier)
+ return
+ }
+
val coroutineScope = rememberCoroutineScope()
val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle()
val view = LocalView.current
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
deleted file mode 100644
index 07d8890..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.ambient.touch;
-
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.DreamManager;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ShadeTouchHandlerTest extends SysuiTestCase {
- @Mock
- CentralSurfaces mCentralSurfaces;
-
- @Mock
- ShadeViewController mShadeViewController;
-
- @Mock
- DreamManager mDreamManager;
-
- @Mock
- TouchHandler.TouchSession mTouchSession;
-
- ShadeTouchHandler mTouchHandler;
-
- @Captor
- ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor;
- @Captor
- ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor;
-
- private static final int TOUCH_HEIGHT = 20;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController,
- mDreamManager, TOUCH_HEIGHT);
- }
-
- // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
- @Test
- public void testSwipeDown_captured() {
- final boolean captured = swipe(Direction.DOWN);
-
- assertThat(captured).isTrue();
- }
-
- // Verifies that a swipe in the upward direction is not captured.
- @Test
- public void testSwipeUp_notCaptured() {
- final boolean captured = swipe(Direction.UP);
-
- // Motion events not captured as the swipe is going in the wrong direction.
- assertThat(captured).isFalse();
- }
-
- // Verifies that a swipe down forwards captured touches to central surfaces for handling.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
- public void testSwipeDown_communalEnabled_sentToCentralSurfaces() {
- swipe(Direction.DOWN);
-
- // Both motion events are sent for central surfaces to process.
- verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
- }
-
- // Verifies that a swipe down forwards captured touches to the shade view for handling.
- @Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
- public void testSwipeDown_communalDisabled_sentToShadeView() {
- swipe(Direction.DOWN);
-
- // Both motion events are sent for the shade view to process.
- verify(mShadeViewController, times(2)).handleExternalTouch(any());
- }
-
- // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
- // handling.
- @Test
- public void testSwipeDown_dreaming_sentToShadeView() {
- when(mDreamManager.isDreaming()).thenReturn(true);
-
- swipe(Direction.DOWN);
-
- // Both motion events are sent for the shade view to process.
- verify(mShadeViewController, times(2)).handleExternalTouch(any());
- }
-
- // Verifies that a swipe up is not forwarded to central surfaces.
- @Test
- @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
- public void testSwipeUp_communalEnabled_touchesNotSent() {
- swipe(Direction.UP);
-
- // Motion events are not sent for central surfaces to process as the swipe is going in the
- // wrong direction.
- verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
- }
-
- // Verifies that a swipe up is not forwarded to the shade view.
- @Test
- @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
- public void testSwipeUp_communalDisabled_touchesNotSent() {
- swipe(Direction.UP);
-
- // Motion events are not sent for the shade view to process as the swipe is going in the
- // wrong direction.
- verify(mShadeViewController, never()).handleExternalTouch(any());
- }
-
- /**
- * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
- * touch handler's gesture listener.
- * <p>
- * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
- * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0.
- */
- private boolean swipe(Direction direction) {
- Mockito.clearInvocations(mTouchSession);
- mTouchHandler.onSessionStart(mTouchSession);
-
- verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture());
- verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture());
-
- final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0;
- final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT;
-
- // Send touches to the input and gesture listener.
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0);
- mInputListenerCaptor.getValue().onInputEvent(event1);
- mInputListenerCaptor.getValue().onInputEvent(event2);
- final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0,
- startY - endY);
-
- return captured;
- }
-
- private enum Direction {
- DOWN, UP,
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
new file mode 100644
index 0000000..4314676
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ambient.touch
+
+import android.app.DreamManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.GestureDetector
+import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.ambient.touch.TouchHandler.TouchSession
+import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shared.system.InputChannelCompat
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.google.common.truth.Truth
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShadeTouchHandlerTest : SysuiTestCase() {
+ private var mCentralSurfaces = mock<CentralSurfaces>()
+ private var mShadeViewController = mock<ShadeViewController>()
+ private var mDreamManager = mock<DreamManager>()
+ private var mTouchSession = mock<TouchSession>()
+
+ private lateinit var mTouchHandler: ShadeTouchHandler
+
+ private var mGestureListenerCaptor = argumentCaptor<GestureDetector.OnGestureListener>()
+ private var mInputListenerCaptor = argumentCaptor<InputChannelCompat.InputEventListener>()
+
+ @Before
+ fun setup() {
+ mTouchHandler =
+ ShadeTouchHandler(
+ Optional.of(mCentralSurfaces),
+ mShadeViewController,
+ mDreamManager,
+ TOUCH_HEIGHT
+ )
+ }
+
+ // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
+ @Test
+ fun testSwipeDown_captured() {
+ val captured = swipe(Direction.DOWN)
+ Truth.assertThat(captured).isTrue()
+ }
+
+ // Verifies that a swipe in the upward direction is not captured.
+ @Test
+ fun testSwipeUp_notCaptured() {
+ val captured = swipe(Direction.UP)
+
+ // Motion events not captured as the swipe is going in the wrong direction.
+ Truth.assertThat(captured).isFalse()
+ }
+
+ // Verifies that a swipe down forwards captured touches to central surfaces for handling.
+ @Test
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ fun testSwipeDown_communalEnabled_sentToCentralSurfaces() {
+ swipe(Direction.DOWN)
+
+ // Both motion events are sent for central surfaces to process.
+ verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any())
+ }
+
+ // Verifies that a swipe down forwards captured touches to the shade view for handling.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ fun testSwipeDown_communalDisabled_sentToShadeView() {
+ swipe(Direction.DOWN)
+
+ // Both motion events are sent for the shade view to process.
+ verify(mShadeViewController, times(2)).handleExternalTouch(any())
+ }
+
+ // Verifies that a swipe down while dreaming forwards captured touches to the shade view for
+ // handling.
+ @Test
+ fun testSwipeDown_dreaming_sentToShadeView() {
+ whenever(mDreamManager.isDreaming).thenReturn(true)
+ swipe(Direction.DOWN)
+
+ // Both motion events are sent for the shade view to process.
+ verify(mShadeViewController, times(2)).handleExternalTouch(any())
+ }
+
+ // Verifies that a swipe up is not forwarded to central surfaces.
+ @Test
+ @EnableFlags(Flags.FLAG_COMMUNAL_HUB)
+ fun testSwipeUp_communalEnabled_touchesNotSent() {
+ swipe(Direction.UP)
+
+ // Motion events are not sent for central surfaces to process as the swipe is going in the
+ // wrong direction.
+ verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any())
+ }
+
+ // Verifies that a swipe up is not forwarded to the shade view.
+ @Test
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ fun testSwipeUp_communalDisabled_touchesNotSent() {
+ swipe(Direction.UP)
+
+ // Motion events are not sent for the shade view to process as the swipe is going in the
+ // wrong direction.
+ verify(mShadeViewController, never()).handleExternalTouch(any())
+ }
+
+ /**
+ * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
+ * touch handler's gesture listener.
+ *
+ * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
+ * of the gesture region, [.TOUCH_HEIGHT], and goes upward to 0.
+ */
+ private fun swipe(direction: Direction): Boolean {
+ clearInvocations(mTouchSession)
+ mTouchHandler.onSessionStart(mTouchSession)
+ verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture())
+ verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture())
+ val startY = (if (direction == Direction.UP) TOUCH_HEIGHT else 0).toFloat()
+ val endY = (if (direction == Direction.UP) 0 else TOUCH_HEIGHT).toFloat()
+
+ // Send touches to the input and gesture listener.
+ val event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0f, startY, 0)
+ val event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0f, endY, 0)
+ mInputListenerCaptor.lastValue.onInputEvent(event1)
+ mInputListenerCaptor.lastValue.onInputEvent(event2)
+ return mGestureListenerCaptor.lastValue.onScroll(event1, event2, 0f, startY - endY)
+ }
+
+ private enum class Direction {
+ DOWN,
+ UP
+ }
+
+ companion object {
+ private const val TOUCH_HEIGHT = 20
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index de4b999..875e9e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.ui.viewmodel
import android.platform.test.flag.junit.FlagsParameterization
@@ -27,10 +29,13 @@
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardOcclusionRepository
import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
@@ -38,6 +43,8 @@
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import java.util.Locale
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -201,6 +208,44 @@
}
}
+ @Test
+ fun isContentVisible_whenNotOccluded_visible() =
+ with(kosmos) {
+ testScope.runTest {
+ val isContentVisible by collectLastValue(underTest.isContentVisible)
+
+ keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, null)
+ runCurrent()
+ assertThat(isContentVisible).isTrue()
+ }
+ }
+
+ @Test
+ fun isContentVisible_whenOccluded_notVisible() =
+ with(kosmos) {
+ testScope.runTest {
+ val isContentVisible by collectLastValue(underTest.isContentVisible)
+
+ keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null)
+ runCurrent()
+ assertThat(isContentVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun isContentVisible_whenOccluded_notVisible_evenIfShadeShown() =
+ with(kosmos) {
+ testScope.runTest {
+ val isContentVisible by collectLastValue(underTest.isContentVisible)
+ keyguardOcclusionRepository.setShowWhenLockedActivityInfo(true, null)
+ runCurrent()
+
+ sceneInteractor.snapToScene(Scenes.Shade, "")
+ runCurrent()
+ assertThat(isContentVisible).isFalse()
+ }
+ }
+
private fun prepareConfiguration(): Int {
val configuration = context.resources.configuration
configuration.setLayoutDirection(Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 1de0abe..8a29f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.shared.model.ClockSize
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
@@ -48,6 +49,7 @@
val shadeInteractor: ShadeInteractor,
@Application private val applicationScope: CoroutineScope,
unfoldTransitionInteractor: UnfoldTransitionInteractor,
+ occlusionInteractor: SceneContainerOcclusionInteractor,
) {
@VisibleForTesting val clockSize = clockInteractor.clockSize
@@ -93,6 +95,16 @@
initialValue = UnfoldTranslations(),
)
+ /** Whether the content of the scene UI should be shown. */
+ val isContentVisible: StateFlow<Boolean> =
+ occlusionInteractor.isOccludingActivityShown
+ .map { !it }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = true,
+ )
+
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (clockSize.value == ClockSize.LARGE) {
resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
index 233e9b5..2d510e1 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneContainerOcclusionInteractor.kt
@@ -43,8 +43,14 @@
sceneInteractor: SceneInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
- /** Whether a show-when-locked activity is at the top of the current activity stack. */
- private val isOccludingActivityShown: StateFlow<Boolean> =
+ /**
+ * Whether a show-when-locked activity is at the top of the current activity stack.
+ *
+ * Note: this isn't enough to figure out whether the scene container UI should be invisible as
+ * that also depends on the things like the state of AOD and the current scene. If the code
+ * needs that, [invisibleDueToOcclusion] should be collected instead.
+ */
+ val isOccludingActivityShown: StateFlow<Boolean> =
keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
@@ -69,6 +75,9 @@
/**
* Whether the scene container should become invisible due to "occlusion" by an in-foreground
* "show when locked" activity.
+ *
+ * Note: this returns `false` when an overlaid scene (like shade or QS) is shown above the
+ * occluding activity.
*/
val invisibleDueToOcclusion: StateFlow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 534d9d2..900201f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -186,6 +186,7 @@
interactor.configurationBasedDimensions
.map {
when {
+ !it.useSplitShade -> 0
it.useLargeScreenHeader -> it.marginTopLargeScreen
else -> it.marginTop
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 97f5efc..677d1fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -1799,8 +1799,10 @@
when {
fingerprint != null && face != null -> "coex"
fingerprint != null && fingerprint.isAnySidefpsType -> "fingerprint only, sideFps"
- fingerprint != null && !fingerprint.isAnySidefpsType ->
- "fingerprint only, non-sideFps"
+ fingerprint != null && fingerprint.isAnyUdfpsType -> "fingerprint only, udfps"
+ fingerprint != null &&
+ fingerprint.sensorType == FingerprintSensorProperties.TYPE_REAR ->
+ "fingerprint only, rearFps"
face != null -> "face only"
else -> "?"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 24e47b0..550ecb3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
@@ -34,5 +35,6 @@
shadeInteractor = shadeInteractor,
applicationScope = applicationCoroutineScope,
unfoldTransitionInteractor = unfoldTransitionInteractor,
+ occlusionInteractor = sceneContainerOcclusionInteractor,
)
}
diff --git a/services/core/java/com/android/server/CertBlacklister.java b/services/core/java/com/android/server/CertBlacklister.java
deleted file mode 100644
index e726c6a..0000000
--- a/services/core/java/com/android/server/CertBlacklister.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
-import android.os.Binder;
-import android.os.FileUtils;
-import android.provider.Settings;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import libcore.io.IoUtils;
-
-/**
- * <p>CertBlacklister provides a simple mechanism for updating the platform denylists for SSL
- * certificate public keys and serial numbers.
- */
-public class CertBlacklister extends Binder {
-
- private static final String TAG = "CertBlacklister";
-
- private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
-
- public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt";
- public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt";
-
- public static final String PUBKEY_BLACKLIST_KEY = "pubkey_blacklist";
- public static final String SERIAL_BLACKLIST_KEY = "serial_blacklist";
-
- private static class BlacklistObserver extends ContentObserver {
-
- private final String mKey;
- private final String mName;
- private final String mPath;
- private final File mTmpDir;
- private final ContentResolver mContentResolver;
-
- public BlacklistObserver(String key, String name, String path, ContentResolver cr) {
- super(null);
- mKey = key;
- mName = name;
- mPath = path;
- mTmpDir = new File(mPath).getParentFile();
- mContentResolver = cr;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- writeDenylist();
- }
-
- public String getValue() {
- return Settings.Secure.getString(mContentResolver, mKey);
- }
-
- private void writeDenylist() {
- new Thread("BlacklistUpdater") {
- public void run() {
- synchronized(mTmpDir) {
- String blacklist = getValue();
- if (blacklist != null) {
- Slog.i(TAG, "Certificate blacklist changed, updating...");
- FileOutputStream out = null;
- try {
- // create a temporary file
- File tmp = File.createTempFile("journal", "", mTmpDir);
- // mark it -rw-r--r--
- tmp.setReadable(true, false);
- // write to it
- out = new FileOutputStream(tmp);
- out.write(blacklist.getBytes());
- // sync to disk
- FileUtils.sync(out);
- // atomic rename
- tmp.renameTo(new File(mPath));
- Slog.i(TAG, "Certificate blacklist updated");
- } catch (IOException e) {
- Slog.e(TAG, "Failed to write blacklist", e);
- } finally {
- IoUtils.closeQuietly(out);
- }
- }
- }
- }
- }.start();
- }
- }
-
- public CertBlacklister(Context context) {
- registerObservers(context.getContentResolver());
- }
-
- private BlacklistObserver buildPubkeyObserver(ContentResolver cr) {
- return new BlacklistObserver(PUBKEY_BLACKLIST_KEY,
- "pubkey",
- PUBKEY_PATH,
- cr);
- }
-
- private BlacklistObserver buildSerialObserver(ContentResolver cr) {
- return new BlacklistObserver(SERIAL_BLACKLIST_KEY,
- "serial",
- SERIAL_PATH,
- cr);
- }
-
- private void registerObservers(ContentResolver cr) {
- // set up the public key denylist observer
- cr.registerContentObserver(
- Settings.Secure.getUriFor(PUBKEY_BLACKLIST_KEY),
- true,
- buildPubkeyObserver(cr)
- );
-
- // set up the serial number denylist observer
- cr.registerContentObserver(
- Settings.Secure.getUriFor(SERIAL_BLACKLIST_KEY),
- true,
- buildSerialObserver(cr)
- );
- }
-}
diff --git a/services/core/java/com/android/server/CertBlocklister.java b/services/core/java/com/android/server/CertBlocklister.java
new file mode 100644
index 0000000..9e23f88
--- /dev/null
+++ b/services/core/java/com/android/server/CertBlocklister.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Binder;
+import android.os.FileUtils;
+import android.provider.Settings;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * <p>CertBlocklister provides a simple mechanism for updating the platform denylists for SSL
+ * certificate public keys and serial numbers.
+ */
+public class CertBlocklister extends Binder {
+
+ private static final String TAG = "CertBlocklister";
+
+ private static final String DENYLIST_ROOT = System.getenv("ANDROID_DATA") + "/misc/keychain/";
+
+ /* For compatibility reasons, the name of these paths cannot be changed */
+ public static final String PUBKEY_PATH = DENYLIST_ROOT + "pubkey_blacklist.txt";
+ public static final String SERIAL_PATH = DENYLIST_ROOT + "serial_blacklist.txt";
+
+ /* For compatibility reasons, the name of these keys cannot be changed */
+ public static final String PUBKEY_BLOCKLIST_KEY = "pubkey_blacklist";
+ public static final String SERIAL_BLOCKLIST_KEY = "serial_blacklist";
+
+ private static class BlocklistObserver extends ContentObserver {
+
+ private final String mKey;
+ private final String mName;
+ private final String mPath;
+ private final File mTmpDir;
+ private final ContentResolver mContentResolver;
+
+ BlocklistObserver(String key, String name, String path, ContentResolver cr) {
+ super(null);
+ mKey = key;
+ mName = name;
+ mPath = path;
+ mTmpDir = new File(mPath).getParentFile();
+ mContentResolver = cr;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ new Thread("BlocklistUpdater") {
+ public void run() {
+ writeDenylist();
+ }
+ }.start();
+ }
+
+ public String getValue() {
+ return Settings.Secure.getStringForUser(
+ mContentResolver, mKey, mContentResolver.getUserId());
+ }
+
+ private void writeDenylist() {
+ synchronized (mTmpDir) {
+ String blocklist = getValue();
+ if (blocklist == null) {
+ return;
+ }
+ if (mPath.equals(SERIAL_PATH)) {
+ Slog.w(TAG, "The certificate blocklist based on serials is deprecated. "
+ + "Please use the pubkey blocklist instead.");
+ }
+ Slog.i(TAG, "Certificate blocklist changed, updating...");
+ FileOutputStream out = null;
+ try {
+ // Create a temporary file and rename it atomically.
+ File tmp = File.createTempFile("journal", "", mTmpDir);
+ tmp.setReadable(true /* readable */, false /* ownerOnly */);
+ out = new FileOutputStream(tmp);
+ out.write(blocklist.getBytes());
+ FileUtils.sync(out);
+ tmp.renameTo(new File(mPath));
+ Slog.i(TAG, "Certificate blocklist updated");
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write blocklist", e);
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+ }
+ }
+
+ public CertBlocklister(Context context) {
+ registerObservers(context.getContentResolver());
+ }
+
+ private BlocklistObserver buildPubkeyObserver(ContentResolver cr) {
+ return new BlocklistObserver(PUBKEY_BLOCKLIST_KEY,
+ "pubkey",
+ PUBKEY_PATH,
+ cr);
+ }
+
+ private BlocklistObserver buildSerialObserver(ContentResolver cr) {
+ return new BlocklistObserver(SERIAL_BLOCKLIST_KEY,
+ "serial",
+ SERIAL_PATH,
+ cr);
+ }
+
+ private void registerObservers(ContentResolver cr) {
+ // set up the public key denylist observer
+ cr.registerContentObserver(
+ Settings.Secure.getUriFor(PUBKEY_BLOCKLIST_KEY),
+ true,
+ buildPubkeyObserver(cr)
+ );
+
+ // set up the serial number denylist observer
+ cr.registerContentObserver(
+ Settings.Secure.getUriFor(SERIAL_BLOCKLIST_KEY),
+ true,
+ buildSerialObserver(cr)
+ );
+ }
+}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index bc83a0e..bacfd8f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -921,8 +921,7 @@
//helper function to determine if limit on num listeners applies to callingUid
private boolean doesLimitApplyForListeners(int callingUid, int exemptUid) {
- return (callingUid != Process.SYSTEM_UID
- && callingUid != Process.PHONE_UID
+ return (!TelephonyPermissions.isSystemOrPhone(callingUid)
&& callingUid != exemptUid);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0b8034..2e63cdb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -186,6 +186,7 @@
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.telephony.CarrierAppUtils;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -4492,8 +4493,7 @@
void setSystemAppHiddenUntilInstalled(@NonNull Computer snapshot, String packageName,
boolean hidden) {
final int callingUid = Binder.getCallingUid();
- final boolean calledFromSystemOrPhone = callingUid == Process.PHONE_UID
- || callingUid == Process.SYSTEM_UID;
+ final boolean calledFromSystemOrPhone = TelephonyPermissions.isSystemOrPhone(callingUid);
if (!calledFromSystemOrPhone) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
"setSystemAppHiddenUntilInstalled");
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index ff8abf8..924b36c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -92,6 +92,7 @@
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.NativeLibraryHelper;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.HexDump;
@@ -356,7 +357,7 @@
* If not, throws a {@link SecurityException}.
*/
public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) {
- if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
+ if (!TelephonyPermissions.isSystemOrPhone(callingUid)) {
throw new SecurityException(
"Cannot call " + methodName + " from UID " + callingUid);
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 791d030..e5a1ebf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -2445,11 +2445,11 @@
t.traceEnd();
}
- t.traceBegin("CertBlacklister");
+ t.traceBegin("CertBlocklister");
try {
- CertBlacklister blacklister = new CertBlacklister(context);
+ CertBlocklister blocklister = new CertBlocklister(context);
} catch (Throwable e) {
- reportWtf("starting CertBlacklister", e);
+ reportWtf("starting CertBlocklister", e);
}
t.traceEnd();
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 7aec42b..00daf41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -43,6 +43,7 @@
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
@@ -142,6 +143,7 @@
private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>();
private @Mock ActivityManager mActivityManager;
+ private @Mock ActivityManagerInternal mActivityManagerInternal;
private @Mock AlarmManager mAlarmManager;
private @Mock BiometricManager mBiometricManager;
private @Mock DevicePolicyManager mDevicePolicyManager;
@@ -158,6 +160,7 @@
private HandlerThread mHandlerThread;
private TrustManagerService mService;
private ITrustManager mTrustManager;
+ private ActivityManagerInternal mPreviousActivityManagerInternal;
@Before
public void setUp() throws Exception {
@@ -210,6 +213,11 @@
mMockContext.setMockPackageManager(mPackageManager);
mMockContext.addMockSystemService(UserManager.class, mUserManager);
doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
+ mPreviousActivityManagerInternal = LocalServices.getService(
+ ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class,
+ mActivityManagerInternal);
LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
grantPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE);
@@ -257,7 +265,14 @@
@After
public void tearDown() {
LocalServices.removeServiceForTest(SystemServiceManager.class);
- mHandlerThread.quit();
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ if (mPreviousActivityManagerInternal != null) {
+ LocalServices.addService(ActivityManagerInternal.class,
+ mPreviousActivityManagerInternal);
+ }
+ if (mHandlerThread != null) {
+ mHandlerThread.quit();
+ }
}
@Test
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index d4b6c91..feea55b 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -32,6 +32,8 @@
import android.util.Log;
import android.widget.Toast;
+import com.android.internal.telephony.TelephonyPermissions;
+import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
/**
@@ -310,10 +312,18 @@
// This avoid breaking legacy code that rely on public-facing APIs to access cell location,
// and it doesn't create an info leak risk because the cell location is stored in the phone
// process anyway, and the system server already has location access.
- if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
- || query.callingUid == Process.NETWORK_STACK_UID
- || query.callingUid == Process.ROOT_UID) {
- return LocationPermissionResult.ALLOWED;
+ if (Flags.supportPhoneUidCheckForMultiuser()) {
+ if (TelephonyPermissions.isSystemOrPhone(query.callingUid)
+ || UserHandle.isSameApp(query.callingUid, Process.NETWORK_STACK_UID)
+ || UserHandle.isSameApp(query.callingUid, Process.ROOT_UID)) {
+ return LocationPermissionResult.ALLOWED;
+ }
+ } else {
+ if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+ || query.callingUid == Process.NETWORK_STACK_UID
+ || query.callingUid == Process.ROOT_UID) {
+ return LocationPermissionResult.ALLOWED;
+ }
}
// Check the system-wide requirements. If the location main switch is off and the caller is