diff options
136 files changed, 2018 insertions, 988 deletions
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS index a1719c9c31d1..76ab30334634 100644 --- a/apct-tests/perftests/core/src/android/os/OWNERS +++ b/apct-tests/perftests/core/src/android/os/OWNERS @@ -1 +1,4 @@ -per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file +per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS + +# Bug component: 345036 +per-file VibratorPerfTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index ceaf337b2122..2584f047295d 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -119,7 +119,7 @@ public class Handler { * crashes (if a handler is sometimes created on a thread without a Looper active), or race * conditions, where the thread a handler is associated with is not what the author * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper - * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or + * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or * similar. If the implicit thread local behavior is required for compatibility, use * {@code new Handler(Looper.myLooper())} to make it clear to readers. * @@ -144,7 +144,7 @@ public class Handler { * crashes (if a handler is sometimes created on a thread without a Looper active), or race * conditions, where the thread a handler is associated with is not what the author * anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper - * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or + * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or * similar. If the implicit thread local behavior is required for compatibility, use * {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers. */ diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS index 28a242037f6a..0809de25b45c 100644 --- a/core/java/android/print/OWNERS +++ b/core/java/android/print/OWNERS @@ -1,4 +1,4 @@ # Bug component: 47273 -svetoslavganov@android.com -svetoslavganov@google.com +anothermark@google.com +kumarashishg@google.com diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS deleted file mode 100644 index 28a242037f6a..000000000000 --- a/core/java/android/print/pdf/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Bug component: 47273 - -svetoslavganov@android.com -svetoslavganov@google.com diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS index 28a242037f6a..0809de25b45c 100644 --- a/core/java/android/printservice/OWNERS +++ b/core/java/android/printservice/OWNERS @@ -1,4 +1,4 @@ # Bug component: 47273 -svetoslavganov@android.com -svetoslavganov@google.com +anothermark@google.com +kumarashishg@google.com diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS deleted file mode 100644 index 28a242037f6a..000000000000 --- a/core/java/android/printservice/recommendation/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Bug component: 47273 - -svetoslavganov@android.com -svetoslavganov@google.com diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java index 8ba8b8cca5ed..0699bc1cb734 100644 --- a/core/java/android/view/animation/AnimationUtils.java +++ b/core/java/android/view/animation/AnimationUtils.java @@ -32,6 +32,7 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.TimeUtils; import android.util.Xml; +import android.view.Choreographer; import android.view.InflateException; import org.xmlpull.v1.XmlPullParser; @@ -153,7 +154,13 @@ public class AnimationUtils { */ public static long getExpectedPresentationTimeNanos() { AnimationState state = sAnimationState.get(); - return state.mExpectedPresentationTimeNanos; + if (state.animationClockLocked) { + return state.mExpectedPresentationTimeNanos; + } + // When this methoed is called outside of a Choreographer callback, + // we obtain the value of expectedPresentTimeNanos from the Choreographer. + // This helps avoid returning a time that could potentially be earlier than current time. + return Choreographer.getInstance().getLatestExpectedPresentTimeNanos(); } /** diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java index e9d7b9b25d91..a95748c6d425 100644 --- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java +++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java @@ -43,7 +43,6 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewRootImpl; -import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.AndroidFuture; import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection; import com.android.internal.inputmethod.IRemoteInputConnection; @@ -54,6 +53,7 @@ import java.lang.annotation.Retention; import java.lang.ref.WeakReference; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Supplier; @@ -158,18 +158,13 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { boolean cancellable(); } - @GuardedBy("mLock") - @Nullable - private InputConnection mInputConnection; + @NonNull + private final AtomicReference<InputConnection> mInputConnectionRef; @NonNull private final Looper mLooper; private final Handler mH; - private final Object mLock = new Object(); - @GuardedBy("mLock") - private boolean mFinished = false; - private final InputMethodManager mParentInputMethodManager; private final WeakReference<View> mServedView; @@ -185,7 +180,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { RemoteInputConnectionImpl(@NonNull Looper looper, @NonNull InputConnection inputConnection, @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) { - mInputConnection = inputConnection; + mInputConnectionRef = new AtomicReference<>(inputConnection); mLooper = looper; mH = new Handler(mLooper); mParentInputMethodManager = inputMethodManager; @@ -197,9 +192,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { */ @Nullable public InputConnection getInputConnection() { - synchronized (mLock) { - return mInputConnection; - } + return mInputConnectionRef.get(); } /** @@ -215,9 +208,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { * {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}. */ private boolean isFinished() { - synchronized (mLock) { - return mFinished; - } + return mInputConnectionRef.get() == null; } private boolean isActive() { @@ -386,10 +377,7 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { // TODO(b/199934664): See if we can remove this by providing a default impl. } } finally { - synchronized (mLock) { - mInputConnection = null; - mFinished = true; - } + mInputConnectionRef.set(null); Trace.traceEnd(Trace.TRACE_TAG_INPUT); } @@ -441,7 +429,6 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { public String toString() { return "RemoteInputConnectionImpl{" + "connection=" + getInputConnection() - + " finished=" + isFinished() + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive() + " mServedView=" + mServedView.get() + "}"; @@ -455,16 +442,14 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub { * {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}. */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { - synchronized (mLock) { - // Check that the call is initiated in the target thread of the current InputConnection - // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are - // executed on this thread. Otherwise the messages are dispatched to the correct thread - // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance - // reasons. - if ((mInputConnection instanceof DumpableInputConnection) - && mLooper.isCurrentThread()) { - ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId); - } + final InputConnection ic = mInputConnectionRef.get(); + // Check that the call is initiated in the target thread of the current InputConnection + // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are + // executed on this thread. Otherwise the messages are dispatched to the correct thread + // in IInputConnectionWrapper, but this is not wanted while dumping, for performance + // reasons. + if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) { + ((DumpableInputConnection) ic).dumpDebug(proto, fieldId); } } diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING new file mode 100644 index 000000000000..bbc2458f5d8b --- /dev/null +++ b/core/tests/coretests/src/android/content/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.content.ContentCaptureOptionsTest" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING new file mode 100644 index 000000000000..f8beac2814db --- /dev/null +++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.view.contentcapture" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING new file mode 100644 index 000000000000..3cd4e17d820b --- /dev/null +++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.view.contentprotection" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index aa0a8d9fc06a..3206dd2123d5 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -257,6 +257,7 @@ applications that come with the platform <permission name="android.permission.CLEAR_APP_CACHE"/> <permission name="android.permission.ACCESS_INSTANT_APPS" /> <permission name="android.permission.CONNECTIVITY_INTERNAL"/> + <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" /> <permission name="android.permission.DELETE_CACHE_FILES"/> <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.DUMP"/> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index a605e2bdd3d6..8bd500eac67b 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -166,7 +166,7 @@ android_library { // *.kt sources are inside a filegroup. "kotlin-annotations", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], manifest: "AndroidManifest.xml", plugins: ["dagger2-compiler"], } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt index d5d072a8d449..122dcbb3c2ce 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt @@ -94,7 +94,6 @@ class FloatingContentCoordinator constructor() { * non-overlapping. * @return The new bounds for this content. */ - @JvmDefault fun calculateNewBoundsOnOverlap( overlappingContentBounds: Rect, otherContentBounds: List<Rect> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 711df0d89936..c05af73e6765 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -368,7 +368,6 @@ class DesktopModeTaskRepository { /** * Called when the active tasks change in desktop mode. */ - @JvmDefault fun onActiveTasksChanged(displayId: Int) {} } @@ -379,17 +378,15 @@ class DesktopModeTaskRepository { /** * Called when the desktop starts or stops showing freeform tasks. */ - @JvmDefault fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} /** * Called when the desktop stashed status changes. */ - @JvmDefault fun onStashedChanged(displayId: Int, stashed: Boolean) {} } } private fun <T> Iterable<T>.toDumpString(): String { return joinToString(separator = ", ", prefix = "[", postfix = "]") -}
\ No newline at end of file +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index af8ef174b168..7699b4bfd13a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -737,12 +737,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, Intent fillInIntent2 = null; final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1); final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2); + final ActivityOptions activityOptions1 = options1 != null + ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic(); + final ActivityOptions activityOptions2 = options2 != null + ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic(); if (samePackage(packageName1, packageName2, userId1, userId2)) { if (supportMultiInstancesSplit(packageName1)) { fillInIntent1 = new Intent(); fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); fillInIntent2 = new Intent(); fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + + if (shortcutInfo1 != null) { + activityOptions1.setApplyMultipleTaskFlagForShortcut(true); + } + if (shortcutInfo2 != null) { + activityOptions2.setApplyMultipleTaskFlagForShortcut(true); + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); } else { pendingIntent2 = null; @@ -754,9 +765,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, Toast.LENGTH_SHORT).show(); } } - mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1, - pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio, - remoteTransition, instanceId); + mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, + activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2, + activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition, + instanceId); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index e52fd00e7df7..dc78c9b139f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -407,7 +407,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); } // Rotation change of independent non display window container. - if (change.getParent() == null + if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY) && change.getStartRotation() != change.getEndRotation()) { startRotationAnimation(startTransaction, change, info, ROTATION_ANIMATION_ROTATE, animations, onAnimFinish); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index a242c72db8b3..c22cc6fbea8f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -186,9 +186,12 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback) { final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget); - final IRemoteTransition remote = remoteTransition.getRemoteTransition(); + if (remoteTransition == null) return; + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s", remoteTransition); + + final IRemoteTransition remote = remoteTransition.getRemoteTransition(); if (remote == null) return; IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() { diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index ad4d97f6fe40..38e9f390835c 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -61,7 +61,7 @@ android_test { "libstaticjvmtiagent", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], plugins: ["dagger2-compiler"], diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 7ef82a7ff6e5..ffc664c2e1bc 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -61,18 +61,10 @@ namespace uirenderer { ADD_FAILURE() << "ClipState not a rect"; \ } -#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \ - TEST(test_case_name, test_name##_##pipeline) { \ - RenderPipelineType oldType = Properties::getRenderPipelineType(); \ - Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \ - functionCall; \ - Properties::overrideRenderPipelineType(oldType); \ - }; - -#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \ - INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \ - TestUtils::runOnRenderThread( \ - test_case_name##_##test_name##_RenderThreadTest::doTheThing)) +#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \ + TEST(test_case_name, test_name) { \ + TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \ + } /** * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope @@ -83,21 +75,7 @@ namespace uirenderer { public: \ static void doTheThing(renderthread::RenderThread& renderThread); \ }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ - /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ - /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ - void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ - renderthread::RenderThread& renderThread) - -/** - * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes - */ -#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \ - class test_case_name##_##test_name##_RenderThreadTest { \ - public: \ - static void doTheThing(renderthread::RenderThread& renderThread); \ - }; \ - INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ + INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \ /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \ /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \ void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp index cc7d34b3994e..89d00d3c0dcb 100644 --- a/libs/hwui/tests/unit/CacheManagerTests.cpp +++ b/libs/hwui/tests/unit/CacheManagerTests.cpp @@ -35,7 +35,7 @@ static size_t getCacheUsage(GrDirectContext* grContext) { } // TOOD(258700630): fix this test and re-enable -RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) { +RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) { int32_t width = DeviceInfo::get()->getWidth(); int32_t height = DeviceInfo::get()->getHeight(); GrDirectContext* grContext = renderThread.getGrContext(); diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 1e055c26afc5..073a8357e574 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -355,7 +355,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { EXPECT_EQ(3, canvas.getIndex()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { +RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) { class ProjectionTestCanvas : public SkCanvas { public: ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {} @@ -419,7 +419,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { EXPECT_EQ(2, canvas.getDrawCounter()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { +RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) { /* R is backward projected on B and C is a layer. A / \ @@ -1052,7 +1052,7 @@ TEST(RenderNodeDrawable, renderNode) { } // Verify that layers are composed with linear filtering. -RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { +RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) { static const int CANVAS_WIDTH = 1; static const int CANVAS_HEIGHT = 1; static const int LAYER_WIDTH = 1; @@ -1170,7 +1170,7 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) { } // Draw a vector drawable twice but with different bounds and verify correct bounds are used. -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) { +RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) { static const int CANVAS_WIDTH = 100; static const int CANVAS_HEIGHT = 200; class VectorDrawableTestCanvas : public TestCanvasBase { diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp index 9aa2e1db4461..0f8bd1368f5a 100644 --- a/libs/hwui/tests/unit/ShaderCacheTests.cpp +++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp @@ -370,9 +370,9 @@ TEST(ShaderCacheTest, testCacheValidation) { } using namespace android::uirenderer; -RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) { +RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) { if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) { - // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants. + // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants. GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan"; } if (!folderExist(getExternalStorageFolder())) { diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index 1a1ce1e9cf66..f6be7b20a9e2 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -136,7 +136,7 @@ public: } }; -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { +RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( @@ -195,7 +195,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) { canvasContext->destroy(); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) { +RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) { auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr); ContextFactory contextFactory; std::unique_ptr<CanvasContext> canvasContext( diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 6f180e7498cb..3ded540c3152 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -42,7 +42,7 @@ using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::skiapipeline; -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrame) { auto redNode = TestUtils::createSkiaNode( 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); @@ -62,7 +62,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { Paint greenPaint; @@ -89,7 +89,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { +RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto redNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); @@ -111,7 +111,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { +RENDERTHREAD_TEST(SkiaPipeline, renderLayer) { auto redNode = TestUtils::createSkiaNode( 0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); @@ -154,7 +154,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { blueNode->setLayerSurface(sk_sp<SkSurface>()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { +RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) { ScopedProperty<bool> prop(Properties::debugOverdraw, true); auto whiteNode = TestUtils::createSkiaNode( @@ -227,7 +227,7 @@ public: }; } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { +RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) { class DeferTestCanvas : public SkCanvas { public: DeferTestCanvas() : SkCanvas(800, 600) {} @@ -297,7 +297,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { EXPECT_EQ(4, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { +RENDERTHREAD_TEST(SkiaPipeline, clipped) { static const int CANVAS_WIDTH = 200; static const int CANVAS_HEIGHT = 200; class ClippedTestCanvas : public SkCanvas { @@ -330,7 +330,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { } // Test renderFrame with a dirty clip and a pre-transform matrix. -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) { +RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) { static const int CANVAS_WIDTH = 200; static const int CANVAS_HEIGHT = 100; static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1); @@ -366,7 +366,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { +RENDERTHREAD_TEST(SkiaPipeline, clip_replace) { static const int CANVAS_WIDTH = 50; static const int CANVAS_HEIGHT = 50; class ClipReplaceTestCanvas : public SkCanvas { @@ -396,7 +396,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { EXPECT_EQ(1, surface->canvas()->mDrawCounter); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { +RENDERTHREAD_TEST(SkiaPipeline, context_lost) { test::TestContext context; auto surface = context.surface(); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); @@ -410,7 +410,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { EXPECT_TRUE(pipeline->isSurfaceReady()); } -RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) { +RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) { // create a pipeline and add a picture callback auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); int callbackCount = 0; diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 10c874ec3f12..76cbc8abc808 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "gmock/gmock.h" -#include "gtest/gtest.h" +#include <getopt.h> +#include <signal.h> #include "Properties.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "hwui/Typeface.h" #include "tests/common/LeakChecker.h" -#include <signal.h> - using namespace std; using namespace android; using namespace android::uirenderer; @@ -45,6 +45,57 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { raise(sig); } +// For options that only exist in long-form. Anything in the +// 0-255 range is reserved for short options (which just use their ASCII value) +namespace LongOpts { +enum { + Reserved = 255, + Renderer, +}; +} + +static const struct option LONG_OPTIONS[] = { + {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}}; + +static RenderPipelineType parseRenderer(const char* renderer) { + // Anything that's not skiavk is skiagl + if (!strcmp(renderer, "skiavk")) { + return RenderPipelineType::SkiaVulkan; + } + return RenderPipelineType::SkiaGL; +} + +struct Options { + RenderPipelineType renderer = RenderPipelineType::SkiaGL; +}; + +Options parseOptions(int argc, char* argv[]) { + int c; + opterr = 0; + Options opts; + + while (true) { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index); + + if (c == -1) break; + + switch (c) { + case 0: + // Option set a flag, don't need to do anything + // (although none of the current LONG_OPTIONS do this...) + break; + + case LongOpts::Renderer: + opts.renderer = parseRenderer(optarg); + break; + } + } + return opts; +} + class TypefaceEnvironment : public testing::Environment { public: virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); } @@ -64,8 +115,9 @@ int main(int argc, char* argv[]) { // Avoid talking to SF Properties::isolatedProcess = true; - // Default to GLES (Vulkan-aware tests will override this) - Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL); + + auto opts = parseOptions(argc, argv); + Properties::overrideRenderPipelineType(opts.renderer); // Run the tests testing::InitGoogleTest(&argc, argv); diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS index 6d6a9f8eb98c..bbe5e06bb282 100644 --- a/media/java/android/media/OWNERS +++ b/media/java/android/media/OWNERS @@ -10,5 +10,8 @@ include platform/frameworks/av:/media/janitors/media_solutions_OWNERS per-file *Image* = file:/graphics/java/android/graphics/OWNERS +per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent +per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS + # Haptics team also works on Ringtone per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS diff --git a/media/jni/OWNERS b/media/jni/OWNERS index 96894d15d8b1..e12d828733fa 100644 --- a/media/jni/OWNERS +++ b/media/jni/OWNERS @@ -3,3 +3,6 @@ per-file android_mtp_*.cpp=aprasath@google.com,anothermark@google.com,kumarashis # extra for TV related files per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com + +per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent +per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index dea7f03d369a..5ea98c0c6700 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -2615,7 +2615,7 @@ static void android_media_MediaCodec_native_queueLinearBlock( return; } auto cryptoInfo = - cryptoInfoObj ? NativeCryptoInfo{size} : NativeCryptoInfo{env, cryptoInfoObj}; + cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size}; if (env->ExceptionCheck()) { // Creation of cryptoInfo failed. Let the exception bubble up. return; diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp index 28b9bc04a249..fe26dc3d7feb 100644 --- a/packages/CredentialManager/Android.bp +++ b/packages/CredentialManager/Android.bp @@ -44,7 +44,7 @@ android_app { platform_apis: true, privileged: true, - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], optimize: { proguard_compatibility: false, diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp index 8699f59e4fba..0caf505c2177 100644 --- a/packages/EasterEgg/Android.bp +++ b/packages/EasterEgg/Android.bp @@ -70,5 +70,5 @@ android_app { manifest: "AndroidManifest.xml", - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java index 1d433e767e5b..5bc271954b25 100644 --- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java @@ -59,7 +59,7 @@ public abstract class AbstractWifiMacAddressPreferenceController @Override public boolean isAvailable() { - return true; + return mWifiManager != null; } @Override diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 7d8b06663f78..368115b99040 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -320,6 +320,7 @@ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> + <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" /> <uses-permission android:name="android.permission.SUSPEND_APPS" /> <uses-permission android:name="android.permission.OBSERVE_APP_USAGE" /> <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7be60431b91b..0f16d930dcf7 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -203,7 +203,7 @@ android_library { manifest: "AndroidManifest.xml", javacflags: ["-Adagger.fastInit=enabled"], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], plugins: ["dagger2-compiler"], @@ -394,7 +394,7 @@ android_library { "android.test.base", "android.test.mock", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], aaptflags: [ "--extra-packages", "com.android.systemui", @@ -516,7 +516,7 @@ android_app { certificate: "platform", privileged: true, - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], dxflags: ["--multi-dex"], optimize: { diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index 764a8556a54d..8306620b3de6 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -302,10 +302,9 @@ class ActivityLaunchAnimator( interface Callback { /** Whether we are currently on the keyguard or not. */ - @JvmDefault fun isOnKeyguard(): Boolean = false + fun isOnKeyguard(): Boolean = false /** Hide the keyguard and animate using [runner]. */ - @JvmDefault fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) { throw UnsupportedOperationException() } @@ -316,16 +315,16 @@ class ActivityLaunchAnimator( interface Listener { /** Called when an activity launch animation started. */ - @JvmDefault fun onLaunchAnimationStart() {} + fun onLaunchAnimationStart() {} /** * Called when an activity launch animation is finished. This will be called if and only if * [onLaunchAnimationStart] was called earlier. */ - @JvmDefault fun onLaunchAnimationEnd() {} + fun onLaunchAnimationEnd() {} /** Called when an activity launch animation made progress. */ - @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {} + fun onLaunchAnimationProgress(linearProgress: Float) {} } /** diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp index 6119e96e5bac..06d94ac5400e 100644 --- a/packages/SystemUI/compose/core/tests/Android.bp +++ b/packages/SystemUI/compose/core/tests/Android.bp @@ -44,5 +44,5 @@ android_test { "androidx.compose.ui_ui-test-manifest", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp index ff534bd01fd3..c7c9140b53ed 100644 --- a/packages/SystemUI/compose/features/tests/Android.bp +++ b/packages/SystemUI/compose/features/tests/Android.bp @@ -44,5 +44,5 @@ android_test { "androidx.compose.ui_ui-test-manifest", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp index dc450bb71dfe..fc37b355494f 100644 --- a/packages/SystemUI/customization/Android.bp +++ b/packages/SystemUI/customization/Android.bp @@ -48,5 +48,5 @@ android_library { ], min_sdk_version: "current", plugins: ["dagger2-compiler"], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], } diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 9c864abca9dd..a38c629b7741 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -366,10 +366,16 @@ <!-- Whether or not notifications that can be expanded will always be in their expanded state. This value only affects notifications that are not a group of notifications from the same - applications. If this value is false, then only the first notification will be expanded; - the other notifications need to be manually expanded by the user. --> + applications. If this value is false, then only the first notification will be expanded + when config_autoExpandFirstNotification is true; the other notifications need to be + manually expanded by the user. --> <bool name="config_alwaysExpandNonGroupedNotifications">false</bool> + <!-- Whether or not the first expandable notification will be expanded automatically by the + system. This value only affects notifications that are not a group of notifications from + the same applications and when config_alwaysExpandNonGroupedNotifications is false. --> + <bool name="config_autoExpandFirstNotification">true</bool> + <!-- Whether or not an expandable notification can be manually expanded or collapsed by the user. Grouped notifications are still expandable even if this value is false. --> <bool name="config_enableNonGroupedNotificationExpand">true</bool> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 28e786b71874..ca30e159a0ff 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -65,7 +65,7 @@ android_library { ], min_sdk_version: "current", plugins: ["dagger2-compiler"], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], } java_library { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 0670ec380861..79a1728470dc 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -254,7 +254,7 @@ object Flags { /** Migrate the indication area to the new keyguard root view. */ // TODO(b/280067944): Tracking bug. @JvmField - val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) + val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area") /** * Migrate the bottom area to the new keyguard root view. @@ -294,6 +294,11 @@ object Flags { @JvmField val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") + /** Migrate the status view from the notification panel to keyguard root view. */ + // TODO(b/291767565): Tracking bug. + @JvmField + val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view") + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite") diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt new file mode 100644 index 000000000000..eaecda52a5a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 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.flags + +import android.util.Log +import com.android.systemui.Dependency + +/** + * This class promotes best practices for flag guarding System UI view refactors. + * * [isEnabled] allows changing an implementation. + * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and + * ensure that it is not being invoked accidentally in the post-flag refactor. + * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on + * flag-disabled builds, but with a check that should crash eng builds or tests when the + * expectation is violated. + * + * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it, + * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes + * inside views where injecting flag values after initialization can be error-prone. + */ +class ViewRefactorFlag +private constructor( + private val injectedFlags: FeatureFlags?, + private val flag: BooleanFlag, + private val readFlagValue: (FeatureFlags) -> Boolean +) { + @JvmOverloads + constructor( + flags: FeatureFlags? = null, + flag: UnreleasedFlag + ) : this(flags, flag, { it.isEnabled(flag) }) + + @JvmOverloads + constructor( + flags: FeatureFlags? = null, + flag: ReleasedFlag + ) : this(flags, flag, { it.isEnabled(flag) }) + + /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */ + val isEnabled by lazy { + @Suppress("DEPRECATION") + val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java) + readFlagValue(featureFlags) + } + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + * + * Example usage: + * ``` + * public void setController(NotificationShelfController notificationShelfController) { + * mShelfRefactor.assertDisabled(); + * mController = notificationShelfController; + * } + * ```` + */ + fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." } + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + * + * Example usage: + * ``` + * public void setShelfIcons(NotificationIconContainer icons) { + * if (mShelfRefactor.expectEnabled()) { + * mShelfIcons = icons; + * } + * } + * ``` + */ + fun expectEnabled(): Boolean { + if (!isEnabled) { + val message = "Code path not supported when $flag is disabled." + Log.wtf(TAG, message, Exception(message)) + } + return isEnabled + } + + private companion object { + private const val TAG = "ViewRefactorFlag" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 364614421567..489d2ab4d342 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -171,7 +171,6 @@ class KeyguardUnlockAnimationController @Inject constructor( * for the canned animation (if applicable) so interested parties can sync with it. If no * canned animation is playing, these are both 0. */ - @JvmDefault fun onUnlockAnimationStarted( playingCannedAnimation: Boolean, isWakeAndUnlockNotFromDream: Boolean, @@ -184,7 +183,6 @@ class KeyguardUnlockAnimationController @Inject constructor( * The keyguard is no longer visible in this state and the app/launcher behind the keyguard * is now completely visible. */ - @JvmDefault fun onUnlockAnimationFinished() {} } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index ddd4a2bf4453..e0834bb894b5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -2982,6 +2982,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void onKeyguardExitFinished() { + if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()"); // only play "unlock" noises if not on a call (since the incall UI // disables the keyguard) if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) { @@ -3203,13 +3204,14 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, flags |= StatusBarManager.DISABLE_RECENT; } - if (mPowerGestureIntercepted) { + if (mPowerGestureIntercepted && mOccluded && isSecure()) { flags |= StatusBarManager.DISABLE_RECENT; } if (DEBUG) { Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons + + " mPowerGestureIntercepted=" + mPowerGestureIntercepted + " --> flags=0x" + Integer.toHexString(flags)); } @@ -3437,6 +3439,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, pw.print(" mPendingLock: "); pw.println(mPendingLock); pw.print(" wakeAndUnlocking: "); pw.println(mWakeAndUnlocking); pw.print(" mPendingPinLock: "); pw.println(mPendingPinLock); + pw.print(" mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted); } /** diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt index d652889f0082..d949a2a0afe5 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt @@ -145,13 +145,10 @@ class PrivacyConfig @Inject constructor( } interface Callback { - @JvmDefault fun onFlagMicCameraChanged(flag: Boolean) {} - @JvmDefault fun onFlagLocationChanged(flag: Boolean) {} - @JvmDefault fun onFlagMediaProjectionChanged(flag: Boolean) {} } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index a676150f44a2..eb8ef9bf3e50 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -216,7 +216,6 @@ class PrivacyItemController @Inject constructor( interface Callback : PrivacyConfig.Callback { fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>) - @JvmDefault fun onFlagAllChanged(flag: Boolean) {} } diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt index bb7f721ad61f..468a75d8276e 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt @@ -52,12 +52,12 @@ interface DisplayTracker { interface Callback { /** Notifies that a display has been added. */ - @JvmDefault fun onDisplayAdded(displayId: Int) {} + fun onDisplayAdded(displayId: Int) {} /** Notifies that a display has been removed. */ - @JvmDefault fun onDisplayRemoved(displayId: Int) {} + fun onDisplayRemoved(displayId: Int) {} /** Notifies a display has been changed */ - @JvmDefault fun onDisplayChanged(displayId: Int) {} + fun onDisplayChanged(displayId: Int) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt index 33a3125d1c68..93a3e905f0e0 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt @@ -71,7 +71,6 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be * auto-decremented after the completion of this method. */ - @JvmDefault fun onUserChanging(newUser: Int, userContext: Context) {} /** @@ -82,7 +81,6 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { * user switch duration. When overriding this method, countDown() MUST be called on the * latch once execution is complete. */ - @JvmDefault fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) { onUserChanging(newUser, userContext) latch.countDown() @@ -93,13 +91,11 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { * Override this method to run things after the screen is unfrozen for the user switch. * Please see {@link #onUserChanging} if you need to hide jank. */ - @JvmDefault fun onUserChanged(newUser: Int, userContext: Context) {} /** * Notifies that the current user's profiles have changed. */ - @JvmDefault fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt index 56bb1a6020cf..5804040a8676 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt @@ -29,12 +29,12 @@ interface ShadeStateEvents { interface ShadeStateEventsListener { /** Invoked when the notification panel starts or stops collapsing. */ - @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {} + fun onPanelCollapsingChanged(isCollapsing: Boolean) {} /** * Invoked when the notification panel starts or stops launching an [android.app.Activity]. */ - @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {} + fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {} /** * Invoked when the "expand immediate" attribute changes. @@ -45,6 +45,6 @@ interface ShadeStateEvents { * Another example is when full QS is showing, and we swipe up from the bottom. Instead of * going to QQS, the panel fully collapses. */ - @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {} + fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java index 4ec5f46e7771..7a989cfe227a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import android.view.View; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.AmbientState; @@ -52,7 +51,6 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC mActivatableNotificationViewController = activatableNotificationViewController; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; - mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index fb88a96c38c2..763400b307fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -27,10 +27,12 @@ import android.app.Notification; import android.app.WallpaperManager; import android.content.Context; import android.graphics.Bitmap; +import android.graphics.Point; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; +import android.hardware.display.DisplayManager; import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; @@ -41,6 +43,7 @@ import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.Log; +import android.view.Display; import android.view.View; import android.widget.ImageView; @@ -74,11 +77,15 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import dagger.Lazy; @@ -138,6 +145,14 @@ public class NotificationMediaManager implements Dumpable { private BackDropView mBackdrop; private ImageView mBackdropFront; private ImageView mBackdropBack; + private final Point mTmpDisplaySize = new Point(); + + private final DisplayManager mDisplayManager; + @Nullable + private List<String> mSmallerInternalDisplayUids; + private Display mCurrentDisplay; + + private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable; private final MediaController.Callback mMediaListener = new MediaController.Callback() { @Override @@ -184,7 +199,8 @@ public class NotificationMediaManager implements Dumpable { SysuiColorExtractor colorExtractor, KeyguardStateController keyguardStateController, DumpManager dumpManager, - WallpaperManager wallpaperManager) { + WallpaperManager wallpaperManager, + DisplayManager displayManager) { mContext = context; mMediaArtworkProcessor = mediaArtworkProcessor; mKeyguardBypassController = keyguardBypassController; @@ -200,6 +216,7 @@ public class NotificationMediaManager implements Dumpable { mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; mKeyguardStateController = keyguardStateController; + mDisplayManager = displayManager; mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled(); setupNotifPipeline(); @@ -477,6 +494,48 @@ public class NotificationMediaManager implements Dumpable { } /** + * Notify lockscreen wallpaper drawable the current internal display. + */ + public void onDisplayUpdated(Display display) { + Trace.beginSection("NotificationMediaManager#onDisplayUpdated"); + mCurrentDisplay = display; + if (mWallapperDrawable != null) { + mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays()); + } + Trace.endSection(); + } + + private boolean isOnSmallerInternalDisplays() { + if (mSmallerInternalDisplayUids == null) { + mSmallerInternalDisplayUids = findSmallerInternalDisplayUids(); + } + return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId()); + } + + private List<String> findSmallerInternalDisplayUids() { + if (mSmallerInternalDisplayUids != null) { + return mSmallerInternalDisplayUids; + } + List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays( + DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) + .filter(display -> display.getType() == Display.TYPE_INTERNAL) + .collect(Collectors.toList()); + if (internalDisplays.isEmpty()) { + return List.of(); + } + Display largestDisplay = internalDisplays.stream() + .max(Comparator.comparingInt(this::getRealDisplayArea)) + .orElse(internalDisplays.get(0)); + internalDisplays.remove(largestDisplay); + return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList()); + } + + private int getRealDisplayArea(Display display) { + display.getRealSize(mTmpDisplaySize); + return mTmpDisplaySize.x * mTmpDisplaySize.y; + } + + /** * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper. */ public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) { @@ -551,7 +610,7 @@ public class NotificationMediaManager implements Dumpable { mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null; if (lockWallpaper != null) { artworkDrawable = new LockscreenWallpaper.WallpaperDrawable( - mBackdropBack.getResources(), lockWallpaper); + mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays()); // We're in the SHADE mode on the SIM screen - yet we still need to show // the lockscreen wallpaper in that mode. allowWhenShade = mStatusBarStateController.getState() == KEYGUARD; @@ -611,6 +670,10 @@ public class NotificationMediaManager implements Dumpable { mBackdropBack.setBackgroundColor(0xFFFFFFFF); mBackdropBack.setImageDrawable(new ColorDrawable(c)); } else { + if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) { + mWallapperDrawable = + (LockscreenWallpaper.WallpaperDrawable) artworkDrawable; + } mBackdropBack.setImageDrawable(artworkDrawable); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 0e20df6ecfba..b624115dc5e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -553,7 +553,6 @@ class NotificationShadeDepthController @Inject constructor( */ fun onWallpaperZoomOutChanged(zoomOut: Float) - @JvmDefault fun onBlurRadiusChanged(blurRadius: Int) {} } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 25a1dc6322ba..3f37c60bee8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -24,7 +24,6 @@ import android.content.res.Resources; import android.graphics.Rect; import android.util.AttributeSet; import android.util.IndentingPrintWriter; -import android.util.Log; import android.util.MathUtils; import android.view.View; import android.view.ViewGroup; @@ -40,6 +39,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; +import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -95,8 +96,10 @@ public class NotificationShelf extends ActivatableNotificationView implements St private float mCornerAnimationDistance; private NotificationShelfController mController; private float mActualWidth = -1; - private boolean mSensitiveRevealAnimEnabled; - private boolean mShelfRefactorFlagEnabled; + private final ViewRefactorFlag mSensitiveRevealAnim = + new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM); + private final ViewRefactorFlag mShelfRefactor = + new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR); private boolean mCanModifyColorOfNotifications; private boolean mCanInteract; private NotificationStackScrollLayout mHostLayout; @@ -130,7 +133,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St public void bind(AmbientState ambientState, NotificationStackScrollLayoutController hostLayoutController) { - assertRefactorFlagDisabled(); + mShelfRefactor.assertDisabled(); mAmbientState = ambientState; mHostLayoutController = hostLayoutController; hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> { @@ -140,7 +143,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout, NotificationRoundnessManager roundnessManager) { - if (!checkRefactorFlagEnabled()) return; + if (!mShelfRefactor.expectEnabled()) return; mAmbientState = ambientState; mHostLayout = hostLayout; mRoundnessManager = roundnessManager; @@ -268,7 +271,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight(); - if (mSensitiveRevealAnimEnabled && viewState.hidden) { + if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) { // if the shelf is hidden, position it at the end of the stack (plus the clip // padding), such that when it appears animated, it will smoothly move in from the // bottom, without jump cutting any notifications @@ -279,7 +282,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private int getSpeedBumpIndex() { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mHostLayout.getSpeedBumpIndex(); } else { return mHostLayoutController.getSpeedBumpIndex(); @@ -413,7 +416,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St expandingAnimated, isLastChild, shelfClipStart); // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount - if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf()) + if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf()) || backgroundForceHidden)) || aboveShelf) { notificationClipEnd = shelfStart + getIntrinsicHeight(); } else { @@ -462,7 +465,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St // if the shelf is visible, but if the shelf is hidden, it causes incorrect curling. // notificationClipEnd handles the discrepancy between a visible and hidden shelf, // so we use that when on the keyguard (and while animating away) to reduce curling. - final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled + final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled() && mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart; updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart); } @@ -504,7 +507,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private ExpandableView getHostLayoutChildAt(int index) { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return (ExpandableView) mHostLayout.getChildAt(index); } else { return mHostLayoutController.getChildAt(index); @@ -512,7 +515,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private int getHostLayoutChildCount() { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mHostLayout.getChildCount(); } else { return mHostLayoutController.getChildCount(); @@ -520,7 +523,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private boolean canModifyColorOfNotifications() { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded(); } else { return mController.canModifyColorOfNotifications(); @@ -583,7 +586,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private boolean isViewAffectedBySwipe(ExpandableView expandableView) { - if (!mShelfRefactorFlagEnabled) { + if (!mShelfRefactor.isEnabled()) { return mHostLayoutController.isViewAffectedBySwipe(expandableView); } else { return mRoundnessManager.isViewAffectedBySwipe(expandableView); @@ -607,7 +610,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private View getHostLayoutTransientView(int index) { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mHostLayout.getTransientView(index); } else { return mHostLayoutController.getTransientView(index); @@ -615,7 +618,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private int getHostLayoutTransientViewCount() { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mHostLayout.getTransientViewCount(); } else { return mHostLayoutController.getTransientViewCount(); @@ -961,7 +964,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St @Override public void onStateChanged(int newState) { - assertRefactorFlagDisabled(); + mShelfRefactor.assertDisabled(); mStatusBarState = newState; updateInteractiveness(); } @@ -975,7 +978,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private boolean canInteract() { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mCanInteract; } else { return mStatusBarState == StatusBarState.KEYGUARD; @@ -1018,32 +1021,18 @@ public class NotificationShelf extends ActivatableNotificationView implements St return false; } - private void assertRefactorFlagDisabled() { - if (mShelfRefactorFlagEnabled) { - NotificationShelfController.throwIllegalFlagStateError(false); - } - } - - private boolean checkRefactorFlagEnabled() { - if (!mShelfRefactorFlagEnabled) { - Log.wtf(TAG, - "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled."); - } - return mShelfRefactorFlagEnabled; - } - public void setController(NotificationShelfController notificationShelfController) { - assertRefactorFlagDisabled(); + mShelfRefactor.assertDisabled(); mController = notificationShelfController; } public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) { - if (!checkRefactorFlagEnabled()) return; + if (!mShelfRefactor.expectEnabled()) return; mCanModifyColorOfNotifications = canModifyColorOfNotifications; } public void setCanInteract(boolean canInteract) { - if (!checkRefactorFlagEnabled()) return; + if (!mShelfRefactor.expectEnabled()) return; mCanInteract = canInteract; updateInteractiveness(); } @@ -1053,27 +1042,15 @@ public class NotificationShelf extends ActivatableNotificationView implements St } private int getIndexOfViewInHostLayout(ExpandableView child) { - if (mShelfRefactorFlagEnabled) { + if (mShelfRefactor.isEnabled()) { return mHostLayout.indexOfChild(child); } else { return mHostLayoutController.indexOfChild(child); } } - /** - * Set whether the sensitive reveal animation feature flag is enabled - * @param enabled true if enabled - */ - public void setSensitiveRevealAnimEnabled(boolean enabled) { - mSensitiveRevealAnimEnabled = enabled; - } - - public void setRefactorFlagEnabled(boolean enabled) { - mShelfRefactorFlagEnabled = enabled; - } - public void requestRoundnessResetFor(ExpandableView child) { - if (!checkRefactorFlagEnabled()) return; + if (!mShelfRefactor.expectEnabled()) return; child.requestRoundnessReset(SHELF_SCROLL); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt index 1619ddaac85c..8a3e21756c2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt @@ -16,11 +16,8 @@ package com.android.systemui.statusbar -import android.util.Log import android.view.View import android.view.View.OnClickListener -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout @@ -49,29 +46,4 @@ interface NotificationShelfController { /** @see View.setOnClickListener */ fun setOnClickListener(listener: OnClickListener) - - companion object { - @JvmStatic - fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) { - if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { - throwIllegalFlagStateError(expected = false) - } - } - - @JvmStatic - fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean = - featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled -> - if (!enabled) { - Log.wtf("NotifShelf", getErrorMessage(expected = true)) - } - } - - @JvmStatic - fun throwIllegalFlagStateError(expected: Boolean): Nothing = - error(getErrorMessage(expected)) - - private fun getErrorMessage(expected: Boolean): String = - "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " + - if (expected) "disabled" else "enabled" - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt index 599beecb0e00..6be407af581d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt @@ -29,7 +29,6 @@ interface SignalCallback { * * @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon */ - @JvmDefault fun setWifiIndicators(wifiIndicators: WifiIndicators) {} /** @@ -42,7 +41,6 @@ interface SignalCallback { * NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be * indexed based on its [subId][MobileDataIndicators.subId] */ - @JvmDefault fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {} /** @@ -51,7 +49,6 @@ interface SignalCallback { * * @param subs a [SubscriptionInfo] for each subscription that we know about */ - @JvmDefault fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {} /** @@ -63,7 +60,6 @@ interface SignalCallback { * @param show whether or not to show a "no sim" view * @param simDetected whether any SIM is detected or not */ - @JvmDefault fun setNoSims(show: Boolean, simDetected: Boolean) {} /** @@ -72,7 +68,6 @@ interface SignalCallback { * * @param icon an [IconState] for the current ethernet status */ - @JvmDefault fun setEthernetIndicators(icon: IconState) {} /** @@ -80,7 +75,6 @@ interface SignalCallback { * * @param icon an [IconState] for the current airplane mode status */ - @JvmDefault fun setIsAirplaneMode(icon: IconState) {} /** @@ -88,7 +82,6 @@ interface SignalCallback { * * @param enabled the current mobile data feature ennabled state */ - @JvmDefault fun setMobileDataEnabled(enabled: Boolean) {} /** @@ -97,7 +90,6 @@ interface SignalCallback { * @param noValidatedNetwork whether there is any validated network. * @param noNetworksAvailable whether there is any WiFi networks available. */ - @JvmDefault fun setConnectivityStatus( noDefaultNetwork: Boolean, noValidatedNetwork: Boolean, @@ -109,7 +101,6 @@ interface SignalCallback { * @param statusIcon the icon for the call indicator * @param subId subscription ID for which to update the UI */ - @JvmDefault fun setCallIndicator(statusIcon: IconState, subId: Int) {} } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index e5ba3ce1fdae..1c7a1860dbb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.dagger; import android.app.IActivityManager; import android.app.WallpaperManager; import android.content.Context; +import android.hardware.display.DisplayManager; import android.os.RemoteException; import android.service.dreams.IDreamManager; import android.util.Log; @@ -146,7 +147,8 @@ public interface CentralSurfacesDependenciesModule { SysuiColorExtractor colorExtractor, KeyguardStateController keyguardStateController, DumpManager dumpManager, - WallpaperManager wallpaperManager) { + WallpaperManager wallpaperManager, + DisplayManager displayManager) { return new NotificationMediaManager( context, centralSurfacesOptionalLazy, @@ -162,7 +164,8 @@ public interface CentralSurfacesDependenciesModule { colorExtractor, keyguardStateController, dumpManager, - wallpaperManager); + wallpaperManager, + displayManager); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt index 2a18f1f51ace..ef9089099a86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt @@ -49,11 +49,10 @@ interface SystemStatusAnimationCallback { fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null } // Best method name, change my mind - @JvmDefault fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? { return null } - @JvmDefault fun onHidePersistentDot(): Animator? { return null } + fun onHidePersistentDot(): Animator? { return null } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 9ba219903272..8d1e8d0ab524 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -613,20 +613,20 @@ constructor( interface WakeUpListener { /** Called whenever the notifications are fully hidden or shown */ - @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {} + fun onFullyHiddenChanged(isFullyHidden: Boolean) {} /** * Called whenever the pulseExpansion changes * * @param expandingChanged if the user has started or stopped expanding */ - @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {} + fun onPulseExpansionChanged(expandingChanged: Boolean) {} /** * Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running * after the start delay, or after it ends/is cancelled. */ - @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {} + fun onDelayedDozeAmountAnimationRunning(running: Boolean) {} } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 1cf9c1e1f299..8029f48152f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,10 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange -import com.android.systemui.Dependency import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.flags.ViewRefactorFlag import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -27,37 +27,31 @@ interface Roundable { /** Current top roundness */ @get:FloatRange(from = 0.0, to = 1.0) - @JvmDefault val topRoundness: Float get() = roundableState.topRoundness /** Current bottom roundness */ @get:FloatRange(from = 0.0, to = 1.0) - @JvmDefault val bottomRoundness: Float get() = roundableState.bottomRoundness /** Max radius in pixel */ - @JvmDefault val maxRadius: Float get() = roundableState.maxRadius /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */ - @JvmDefault val topCornerRadius: Float get() = - if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius + if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius else topRoundness * maxRadius /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */ - @JvmDefault val bottomCornerRadius: Float get() = - if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius + if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius else bottomRoundness * maxRadius /** Get and update the current radii */ - @JvmDefault val updatedRadii: FloatArray get() = roundableState.radiiBuffer.also { radii -> @@ -80,7 +74,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @return Whether the roundness was changed. */ - @JvmDefault fun requestTopRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, sourceType: SourceType, @@ -125,7 +118,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @return Whether the roundness was changed. */ - @JvmDefault fun requestTopRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, sourceType: SourceType, @@ -149,7 +141,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @return Whether the roundness was changed. */ - @JvmDefault fun requestBottomRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, sourceType: SourceType, @@ -194,7 +185,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @return Whether the roundness was changed. */ - @JvmDefault fun requestBottomRoundness( @FloatRange(from = 0.0, to = 1.0) value: Float, sourceType: SourceType, @@ -219,7 +209,6 @@ interface Roundable { * @param animate true if it should animate to that value. * @return Whether the roundness was changed. */ - @JvmDefault fun requestRoundness( @FloatRange(from = 0.0, to = 1.0) top: Float, @FloatRange(from = 0.0, to = 1.0) bottom: Float, @@ -246,7 +235,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @return Whether the roundness was changed. */ - @JvmDefault fun requestRoundness( @FloatRange(from = 0.0, to = 1.0) top: Float, @FloatRange(from = 0.0, to = 1.0) bottom: Float, @@ -270,7 +258,6 @@ interface Roundable { * @param sourceType the source from which the request for roundness comes. * @param animate true if it should animate to that value. */ - @JvmDefault fun requestRoundnessReset(sourceType: SourceType, animate: Boolean) { requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType, animate = animate) } @@ -284,19 +271,16 @@ interface Roundable { * * @param sourceType the source from which the request for roundness comes. */ - @JvmDefault fun requestRoundnessReset(sourceType: SourceType) { requestRoundnessReset(sourceType = sourceType, animate = roundableState.targetView.isShown) } /** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */ - @JvmDefault fun applyRoundnessAndInvalidate() { roundableState.targetView.invalidate() } /** @return true if top or bottom roundness is not zero. */ - @JvmDefault fun hasRoundedCorner(): Boolean { return topRoundness != 0f || bottomRoundness != 0f } @@ -307,7 +291,6 @@ interface Roundable { * * This method reuses the previous [radii] for performance reasons. */ - @JvmDefault fun updateRadii( topCornerRadius: Float, bottomCornerRadius: Float, @@ -335,13 +318,12 @@ constructor( internal val targetView: View, private val roundable: Roundable, maxRadius: Float, - private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java) + featureFlags: FeatureFlags? = null ) { internal var maxRadius = maxRadius private set - internal val newHeadsUpAnimFlagEnabled - get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS) + internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS) /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt index 1494574b26f0..93a34afc9fc9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt @@ -48,6 +48,14 @@ class RowAppearanceCoordinator @Inject internal constructor( private val mAlwaysExpandNonGroupedNotification = context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications) + /** + * `true` if the first non-group expandable notification should be expanded automatically + * when possible. If `false`, then the first non-group expandable notification should not + * be expanded. + */ + private val mAutoExpandFirstNotification = + context.resources.getBoolean(R.bool.config_autoExpandFirstNotification) + override fun attach(pipeline: NotifPipeline) { pipeline.addOnBeforeRenderListListener(::onBeforeRenderList) pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry) @@ -61,8 +69,10 @@ class RowAppearanceCoordinator @Inject internal constructor( private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) { // If mAlwaysExpandNonGroupedNotification is false, then only expand the - // very first notification and if it's not a child of grouped notifications. - controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand) + // very first notification if it's not a child of grouped notifications and when + // mAutoExpandFirstNotification is true. + controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || + (mAutoExpandFirstNotification && entry == entryToExpand)) // Show/hide the feedback icon controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry)) // Show the "alerted" bell icon diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 189608072ec7..9ecf50ee4f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.collection.inflation; -import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC; @@ -176,8 +175,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { entry.setRow(row); mNotifBindPipeline.manageRow(entry, row); mPresenter.onBindRow(row); - row.setInlineReplyAnimationFlagEnabled( - mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION)); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 908c11a1d076..36a8e9833d39 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -566,7 +566,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getTopCornerRadius() { - if (isNewHeadsUpAnimFlagEnabled()) { + if (mImprovedHunAnimation.isEnabled()) { return super.getTopCornerRadius(); } @@ -576,7 +576,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getBottomCornerRadius() { - if (isNewHeadsUpAnimFlagEnabled()) { + if (mImprovedHunAnimation.isEnabled()) { return super.getBottomCornerRadius(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index b34c28163abb..42b99a1dc68c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -77,6 +77,7 @@ import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -275,7 +276,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private OnExpandClickListener mOnExpandClickListener; private View.OnClickListener mOnFeedbackClickListener; private Path mExpandingClipPath; - private boolean mIsInlineReplyAnimationFlagEnabled = false; + private final ViewRefactorFlag mInlineReplyAnimation = + new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); // Listener will be called when receiving a long click event. // Use #setLongPressPosition to optionally assign positional data with the long press. @@ -3121,10 +3123,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering(); } - public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) { - mIsInlineReplyAnimationFlagEnabled = isEnabled; - } - @Override public void setActualHeight(int height, boolean notifyListeners) { boolean changed = height != getActualHeight(); @@ -3144,7 +3142,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } int contentHeight = Math.max(getMinHeight(), height); for (NotificationContentView l : mLayouts) { - if (mIsInlineReplyAnimationFlagEnabled) { + if (mInlineReplyAnimation.isEnabled()) { l.setContentHeight(height); } else { l.setContentHeight(contentHeight); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 7f23c1b89b51..c8f13a6302cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,10 +28,9 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -50,7 +49,8 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); - private final FeatureFlags mFeatureFlags; + protected final ViewRefactorFlag mImprovedHunAnimation = + new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS); /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when @@ -126,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { return EMPTY_PATH; } float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius(); - if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) { + if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) { float overShoot = topRadius + bottomRadius - height; float currentTopRoundness = getTopRoundness(); float currentBottomRoundness = getBottomRoundness(); @@ -167,7 +167,6 @@ public abstract class ExpandableOutlineView extends ExpandableView { super(context, attrs); setOutlineProvider(mProvider); initDimens(); - mFeatureFlags = Dependency.get(FeatureFlags.class); } @Override @@ -376,8 +375,4 @@ public abstract class ExpandableOutlineView extends ExpandableView { }); } - // TODO(b/290365128) replace with ViewRefactorFlag - protected boolean isNewHeadsUpAnimFlagEnabled() { - return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 23a58d252ba6..22a87a7c9432 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -21,7 +21,6 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl @@ -66,7 +65,7 @@ class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() : override fun setOnClickListener(listener: View.OnClickListener) = unsupported private val unsupported: Nothing - get() = NotificationShelfController.throwIllegalFlagStateError(expected = true) + get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled") } /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */ @@ -80,8 +79,6 @@ object NotificationShelfViewBinder { ) { ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) shelf.apply { - setRefactorFlagEnabled(true) - setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind() notificationIconAreaController.setShelfIcons(shelfIcons) repeatWhenAttached { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java index b0f3f598cb91..95e74f210c5d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java @@ -29,7 +29,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; @@ -57,7 +56,6 @@ public class AmbientState implements Dumpable { private final SectionProvider mSectionProvider; private final BypassController mBypassController; private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; - private final FeatureFlags mFeatureFlags; /** * Used to read bouncer states. */ @@ -261,13 +259,12 @@ public class AmbientState implements Dumpable { @NonNull SectionProvider sectionProvider, @NonNull BypassController bypassController, @Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager, - @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator, - @NonNull FeatureFlags featureFlags) { + @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator + ) { mSectionProvider = sectionProvider; mBypassController = bypassController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mLargeScreenShadeInterpolator = largeScreenShadeInterpolator; - mFeatureFlags = featureFlags; reload(context); dumpManager.registerDumpable(this); } @@ -753,10 +750,6 @@ public class AmbientState implements Dumpable { return mLargeScreenShadeInterpolator; } - public FeatureFlags getFeatureFlags() { - return mFeatureFlags; - } - @Override public void dump(PrintWriter pw, String[] args) { pw.println("mTopPadding=" + mTopPadding); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index c1ceb3ce5ab6..d71bc2fd6470 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -89,6 +89,7 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.shade.ShadeController; @@ -198,7 +199,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private Set<Integer> mDebugTextUsedYPositions; private final boolean mDebugRemoveAnimation; private final boolean mSensitiveRevealAnimEndabled; - private boolean mAnimatedInsets; + private final ViewRefactorFlag mAnimatedInsets; + private final ViewRefactorFlag mShelfRefactor; private int mContentHeight; private float mIntrinsicContentHeight; @@ -621,7 +623,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); - setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS)); + mAnimatedInsets = + new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS); + mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); mSectionsManager = Dependency.get(NotificationSectionsManager.class); mScreenOffAnimationController = Dependency.get(ScreenOffAnimationController.class); @@ -660,7 +664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mGroupMembershipManager = Dependency.get(GroupMembershipManager.class); mGroupExpansionManager = Dependency.get(GroupExpansionManager.class); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - if (mAnimatedInsets) { + if (mAnimatedInsets.isEnabled()) { setWindowInsetsAnimationCallback(mInsetsCallback); } } @@ -730,11 +734,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } @VisibleForTesting - void setAnimatedInsetsEnabled(boolean enabled) { - mAnimatedInsets = enabled; - } - - @VisibleForTesting public void updateFooter() { if (mFooterView == null) { return; @@ -1773,7 +1772,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return; } mForcedScroll = v; - if (mAnimatedInsets) { + if (mAnimatedInsets.isEnabled()) { updateForcedScroll(); } else { scrollTo(v); @@ -1822,7 +1821,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (!mAnimatedInsets) { + if (!mAnimatedInsets.isEnabled()) { mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom; } mWaterfallTopInset = 0; @@ -1830,11 +1829,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (cutout != null) { mWaterfallTopInset = cutout.getWaterfallInsets().top; } - if (mAnimatedInsets && !mIsInsetAnimationRunning) { + if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) { // update bottom inset e.g. after rotation updateBottomInset(insets); } - if (!mAnimatedInsets) { + if (!mAnimatedInsets.isEnabled()) { int range = getScrollRange(); if (mOwnScrollY > range) { // HACK: We're repeatedly getting staggered insets here while the IME is @@ -2714,7 +2713,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * @param listener callback for notification removed */ public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) { - NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags()); + mShelfRefactor.assertDisabled(); mOnNotificationRemovedListener = listener; } @@ -2727,7 +2726,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable if (!mChildTransferInProgress) { onViewRemovedInternal(expandableView, this); } - if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + if (mShelfRefactor.isEnabled()) { mShelf.requestRoundnessResetFor(expandableView); } else { if (mOnNotificationRemovedListener != null) { @@ -4943,18 +4942,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @Nullable public ExpandableView getShelf() { - if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) { - return mShelf; - } else { - return null; - } + if (!mShelfRefactor.expectEnabled()) return null; + return mShelf; } public void setShelf(NotificationShelf shelf) { - if (!NotificationShelfController.checkRefactorFlagEnabled( - mAmbientState.getFeatureFlags())) { - return; - } + if (!mShelfRefactor.expectEnabled()) return; int index = -1; if (mShelf != null) { index = indexOfChild(mShelf); @@ -4968,7 +4961,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } public void setShelfController(NotificationShelfController notificationShelfController) { - NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags()); + mShelfRefactor.assertDisabled(); int index = -1; if (mShelf != null) { index = indexOfChild(mShelf); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index ef7375aa690b..4668aa433533 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -65,6 +65,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; @@ -205,6 +206,7 @@ public class NotificationStackScrollLayoutController { private boolean mIsInTransitionToAod = false; private final FeatureFlags mFeatureFlags; + private final ViewRefactorFlag mShelfRefactor; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; private final NotificationDismissibilityProvider mDismissibilityProvider; @@ -718,6 +720,7 @@ public class NotificationStackScrollLayoutController { mShadeController = shadeController; mNotifIconAreaController = notifIconAreaController; mFeatureFlags = featureFlags; + mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); mNotificationTargetsHelper = notificationTargetsHelper; mSecureSettings = secureSettings; mDismissibilityProvider = dismissibilityProvider; @@ -1432,7 +1435,7 @@ public class NotificationStackScrollLayoutController { } public void setShelfController(NotificationShelfController notificationShelfController) { - NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); + mShelfRefactor.assertDisabled(); mView.setShelfController(notificationShelfController); } @@ -1645,12 +1648,12 @@ public class NotificationStackScrollLayoutController { } public void setShelf(NotificationShelf shelf) { - if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return; + if (!mShelfRefactor.expectEnabled()) return; mView.setShelf(shelf); } public int getShelfHeight() { - if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) { + if (!mShelfRefactor.expectEnabled()) { return 0; } ExpandableView shelf = mView.getShelf(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 8361d6ba1801..c3c9a61df2ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2061,6 +2061,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { void updateDisplaySize() { mDisplay.getMetrics(mDisplayMetrics); mDisplay.getSize(mCurrentDisplaySize); + mMediaManager.onDisplayUpdated(mDisplay); if (DEBUG_GESTURES) { mGestureRec.tag("display", String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java index b2c39f7e289f..92c786fb569f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java @@ -306,19 +306,25 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen /** * Drawable that aligns left horizontally and center vertically (like ImageWallpaper). + * + * <p>Aligns to the center when showing on the smaller internal display of a multi display + * device. */ public static class WallpaperDrawable extends DrawableWrapper { private final ConstantState mState; private final Rect mTmpRect = new Rect(); + private boolean mIsOnSmallerInternalDisplays; - public WallpaperDrawable(Resources r, Bitmap b) { - this(r, new ConstantState(b)); + public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) { + this(r, new ConstantState(b), isOnSmallerInternalDisplays); } - private WallpaperDrawable(Resources r, ConstantState state) { + private WallpaperDrawable(Resources r, ConstantState state, + boolean isOnSmallerInternalDisplays) { super(new BitmapDrawable(r, state.mBackground)); mState = state; + mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays; } @Override @@ -357,10 +363,17 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen } dy = (vheight - dheight * scale) * 0.5f; + int offsetX = 0; + // Offset to show the center area of the wallpaper on a smaller display for multi + // display device + if (mIsOnSmallerInternalDisplays) { + offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2); + } + mTmpRect.set( - bounds.left, + bounds.left + offsetX, bounds.top + Math.round(dy), - bounds.left + Math.round(dwidth * scale), + bounds.left + Math.round(dwidth * scale) + offsetX, bounds.top + Math.round(dheight * scale + dy)); super.onBoundsChange(mTmpRect); @@ -371,6 +384,17 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen return mState; } + /** + * Update bounds when the hosting display or the display size has changed. + * + * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal + * displays with the smaller area. + */ + public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) { + mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays; + onBoundsChange(getBounds()); + } + static class ConstantState extends Drawable.ConstantState { private final Bitmap mBackground; @@ -386,7 +410,7 @@ public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implemen @Override public Drawable newDrawable(@Nullable Resources res) { - return new WallpaperDrawable(res, this); + return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index e18c9d86d74b..0bf0f4b504b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -24,6 +24,8 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.flags.ViewRefactorFlag; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -88,7 +90,7 @@ public class NotificationIconAreaController implements private final ArrayList<Rect> mTintAreas = new ArrayList<>(); private final Context mContext; - private final FeatureFlags mFeatureFlags; + private final ViewRefactorFlag mShelfRefactor; private int mAodIconAppearTranslation; @@ -120,12 +122,13 @@ public class NotificationIconAreaController implements Optional<Bubbles> bubblesOptional, DemoModeController demoModeController, DarkIconDispatcher darkIconDispatcher, - FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController, + FeatureFlags featureFlags, + StatusBarWindowController statusBarWindowController, ScreenOffAnimationController screenOffAnimationController) { mContrastColorUtil = ContrastColorUtil.getInstance(context); mContext = context; mStatusBarStateController = statusBarStateController; - mFeatureFlags = featureFlags; + mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR); mStatusBarStateController.addCallback(this); mMediaManager = notificationMediaManager; mDozeParameters = dozeParameters; @@ -179,12 +182,12 @@ public class NotificationIconAreaController implements } public void setupShelf(NotificationShelfController notificationShelfController) { - NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); + mShelfRefactor.assertDisabled(); mShelfIcons = notificationShelfController.getShelfIcons(); } public void setShelfIcons(NotificationIconContainer icons) { - if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) { + if (mShelfRefactor.expectEnabled()) { mShelfIcons = icons; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index a9135ac91e44..ad8530df0523 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -79,7 +79,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu private final HeadsUpManagerPhone mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; - private final CentralSurfaces mCentralSurfaces; private final NotificationsInteractor mNotificationsInteractor; private final NotificationStackScrollLayoutController mNsslController; private final LockscreenShadeTransitionController mShadeTransitionController; @@ -107,7 +106,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu NotificationShadeWindowController notificationShadeWindowController, DynamicPrivacyController dynamicPrivacyController, KeyguardStateController keyguardStateController, - CentralSurfaces centralSurfaces, NotificationsInteractor notificationsInteractor, LockscreenShadeTransitionController shadeTransitionController, PowerInteractor powerInteractor, @@ -128,8 +126,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu mQsController = quickSettingsController; mHeadsUpManager = headsUp; mDynamicPrivacyController = dynamicPrivacyController; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mCentralSurfaces = centralSurfaces; mNotificationsInteractor = notificationsInteractor; mNsslController = stackScrollerController; mShadeTransitionController = shadeTransitionController; @@ -283,7 +279,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu @Override public boolean suppressAwakeHeadsUp(NotificationEntry entry) { final StatusBarNotification sbn = entry.getSbn(); - if (mCentralSurfaces.isOccluded()) { + if (mKeyguardStateController.isOccluded()) { boolean devicePublic = mLockscreenUserManager .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId()); boolean userPublic = devicePublic diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 5d09e064604a..a501e87902a8 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -20,6 +20,11 @@ import android.content.Intent; import com.android.systemui.Dependency; +/** + * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead + * or {@code SettingsObserver} to be able to specify the handler. + */ +@Deprecated public abstract class TunerService { public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER"; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 8cfe2eac3d33..ccc0a79d2cfe 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -56,7 +56,11 @@ import javax.inject.Inject; /** + * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead + * or {@code SettingsObserver} to be able to specify the handler. + * This class will interact with SecureSettings using the main looper. */ +@Deprecated @SysUISingleton public class TunerServiceImpl extends TunerService { diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt index 693c2708b0f7..5582ced1bad6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt +++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt @@ -23,7 +23,6 @@ import android.os.UserHandle * changes. */ interface UserAwareController { - @JvmDefault fun changeUser(newUser: UserHandle) {} val currentUserId: Int diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java index 6decb88ee148..5867a40c78fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java @@ -30,6 +30,8 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; @@ -43,12 +45,14 @@ import org.junit.runner.RunWith; @RunWithLooper public class ExpandHelperTest extends SysuiTestCase { + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private ExpandableNotificationRow mRow; private ExpandHelper mExpandHelper; private ExpandHelper.Callback mCallback; @Before public void setUp() throws Exception { + mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION); mDependency.injectMockDependency(KeyguardUpdateMonitor.class); mDependency.injectMockDependency(NotificationMediaManager.class); allowTestableLooperAsMainThread(); @@ -56,7 +60,8 @@ public class ExpandHelperTest extends SysuiTestCase { NotificationTestHelper helper = new NotificationTestHelper( mContext, mDependency, - TestableLooper.get(this)); + TestableLooper.get(this), + mFeatureFlags); mRow = helper.createRow(); mCallback = mock(ExpandHelper.Callback.class); mExpandHelper = new ExpandHelper(context, mCallback, 10, 100); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 608778e05dad..1dc8453a90ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -87,6 +88,7 @@ import java.util.function.Consumer; @RunWithLooper public class ExpandableNotificationRowTest extends SysuiTestCase { + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private NotificationTestHelper mNotificationTestHelper; @Rule public MockitoRule mockito = MockitoJUnit.rule(); @@ -96,12 +98,10 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mNotificationTestHelper = new NotificationTestHelper( mContext, mDependency, - TestableLooper.get(this)); + TestableLooper.get(this), + mFeatureFlags); mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL); - - FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags(); - fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false); - mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags); + mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM); } @Test @@ -183,6 +183,14 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test + public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue() + throws Exception { + FakeFeatureFlags flags = mFeatureFlags; + flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); + testSetSensitiveOnNotifRowNotifiesOfHeightChange(); + } + + @Test public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception { // GIVEN a sensitive notification row that's currently redacted ExpandableNotificationRow row = mNotificationTestHelper.createRow(); @@ -199,10 +207,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { // WHEN the row is set to no longer be sensitive row.setSensitive(false, true); + boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); // VERIFY that the height change listener is invoked assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout()); assertThat(row.getIntrinsicHeight()).isGreaterThan(0); - verify(listener).onHeightChanged(eq(row), eq(false)); + verify(listener).onHeightChanged(eq(row), eq(expectAnimation)); + } + + @Test + public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue() + throws Exception { + FakeFeatureFlags flags = mFeatureFlags; + flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); + testSetSensitiveOnGroupRowNotifiesOfHeightChange(); } @Test @@ -222,10 +239,19 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { // WHEN the row is set to no longer be sensitive group.setSensitive(false, true); + boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); // VERIFY that the height change listener is invoked assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout()); assertThat(group.getIntrinsicHeight()).isGreaterThan(0); - verify(listener).onHeightChanged(eq(group), eq(false)); + verify(listener).onHeightChanged(eq(group), eq(expectAnimation)); + } + + @Test + public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue() + throws Exception { + FakeFeatureFlags flags = mFeatureFlags; + flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); + testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange(); } @Test @@ -254,7 +280,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0); assertThat(publicRow.getPrivateLayout().getMinHeight()) .isEqualTo(publicRow.getPublicLayout().getMinHeight()); - verify(listener, never()).onHeightChanged(eq(publicRow), eq(false)); + verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean()); } private void measureAndLayout(ExpandableNotificationRow row) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 1a644d3540b0..d21029d33d5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -48,12 +48,16 @@ import android.text.TextUtils; import android.view.LayoutInflater; import android.widget.RemoteViews; +import androidx.annotation.NonNull; + import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.TestableDependency; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -90,6 +94,7 @@ import com.android.systemui.wmshell.BubblesTestActivity; import org.mockito.ArgumentCaptor; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -130,14 +135,24 @@ public class NotificationTestHelper { private final NotificationDismissibilityProvider mDismissibilityProvider; public final Runnable mFutureDismissalRunnable; private @InflationFlag int mDefaultInflationFlags; - private FeatureFlags mFeatureFlags; + private final FakeFeatureFlags mFeatureFlags; public NotificationTestHelper( Context context, TestableDependency dependency, TestableLooper testLooper) { + this(context, dependency, testLooper, new FakeFeatureFlags()); + } + + public NotificationTestHelper( + Context context, + TestableDependency dependency, + TestableLooper testLooper, + @NonNull FakeFeatureFlags featureFlags) { mContext = context; mTestLooper = testLooper; + mFeatureFlags = Objects.requireNonNull(featureFlags); + dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); dependency.injectMockDependency(NotificationMediaManager.class); dependency.injectMockDependency(NotificationShadeWindowController.class); dependency.injectMockDependency(MediaOutputDialogFactory.class); @@ -183,17 +198,12 @@ public class NotificationTestHelper { mFutureDismissalRunnable = mock(Runnable.class); when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt())) .thenReturn(mFutureDismissalRunnable); - mFeatureFlags = mock(FeatureFlags.class); } public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) { mDefaultInflationFlags = defaultInflationFlags; } - public void setFeatureFlags(FeatureFlags featureFlags) { - mFeatureFlags = featureFlags; - } - public ExpandableNotificationRowLogger getMockLogger() { return mMockLogger; } @@ -527,6 +537,10 @@ public class NotificationTestHelper { @InflationFlag int extraInflationFlags, int importance) throws Exception { + // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be + // set, but we do not want to override an existing value that is needed by a specific test. + mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS); + LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( mContext.LAYOUT_INFLATER_SERVICE); mRow = (ExpandableNotificationRow) inflater.inflate( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt index 09382ec1945e..3d752880f423 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt @@ -20,7 +20,6 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager @@ -42,7 +41,6 @@ class AmbientStateTest : SysuiTestCase() { private val bypassController = StackScrollAlgorithm.BypassController { false } private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>() private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>() - private val featureFlags = mock<FeatureFlags>() private lateinit var sut: AmbientState @@ -55,8 +53,7 @@ class AmbientStateTest : SysuiTestCase() { sectionProvider, bypassController, statusBarKeyguardViewManager, - largeScreenShadeInterpolator, - featureFlags + largeScreenShadeInterpolator ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index f38881c5b521..4b145d8b0dd2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -30,7 +30,6 @@ import android.view.ViewGroup; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; @@ -65,7 +64,6 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { @Mock private SectionHeaderController mPeopleHeaderController; @Mock private SectionHeaderController mAlertingHeaderController; @Mock private SectionHeaderController mSilentHeaderController; - @Mock private FeatureFlags mFeatureFlag; private NotificationSectionsManager mSectionsManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index 8d751e3b2808..1dc0ab07349b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -9,7 +9,9 @@ import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerPr import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation +import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarIconView @@ -20,6 +22,7 @@ import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -34,22 +37,32 @@ import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper -class NotificationShelfTest : SysuiTestCase() { +open class NotificationShelfTest : SysuiTestCase() { + + open val useShelfRefactor: Boolean = false + open val useSensitiveReveal: Boolean = false + private val flags = FakeFeatureFlags() @Mock private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator @Mock - private lateinit var flags: FeatureFlags - @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var hostLayoutController: NotificationStackScrollLayoutController + @Mock + private lateinit var hostLayout: NotificationStackScrollLayout + @Mock + private lateinit var roundnessManager: NotificationRoundnessManager private lateinit var shelf: NotificationShelf @Before fun setUp() { MockitoAnnotations.initMocks(this) + mDependency.injectTestDependency(FeatureFlags::class.java, flags) + flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor) + flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal) + flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS) val root = FrameLayout(context) shelf = LayoutInflater.from(root.context) .inflate(/* resource = */ R.layout.status_bar_notification_shelf, @@ -57,10 +70,13 @@ class NotificationShelfTest : SysuiTestCase() { /* attachToRoot = */false) as NotificationShelf whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator) - whenever(ambientState.featureFlags).thenReturn(flags) whenever(ambientState.isSmallScreen).thenReturn(true) - shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController) + if (useShelfRefactor) { + shelf.bind(ambientState, hostLayout, roundnessManager) + } else { + shelf.bind(ambientState, hostLayoutController) + } shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5) } @@ -345,7 +361,7 @@ class NotificationShelfTest : SysuiTestCase() { @Test fun updateState_withNullLastVisibleBackgroundChild_hideShelf() { // GIVEN - shelf.setSensitiveRevealAnimEnabled(true) + assumeTrue(useSensitiveReveal) whenever(ambientState.stackY).thenReturn(100f) whenever(ambientState.stackHeight).thenReturn(100f) val paddingBetweenElements = @@ -372,7 +388,7 @@ class NotificationShelfTest : SysuiTestCase() { @Test fun updateState_withNullFirstViewInShelf_hideShelf() { // GIVEN - shelf.setSensitiveRevealAnimEnabled(true) + assumeTrue(useSensitiveReveal) whenever(ambientState.stackY).thenReturn(100f) whenever(ambientState.stackHeight).thenReturn(100f) val paddingBetweenElements = @@ -399,7 +415,7 @@ class NotificationShelfTest : SysuiTestCase() { @Test fun updateState_withCollapsedShade_hideShelf() { // GIVEN - shelf.setSensitiveRevealAnimEnabled(true) + assumeTrue(useSensitiveReveal) whenever(ambientState.stackY).thenReturn(100f) whenever(ambientState.stackHeight).thenReturn(100f) val paddingBetweenElements = @@ -426,7 +442,7 @@ class NotificationShelfTest : SysuiTestCase() { @Test fun updateState_withHiddenSectionBeforeShelf_hideShelf() { // GIVEN - shelf.setSensitiveRevealAnimEnabled(true) + assumeTrue(useSensitiveReveal) whenever(ambientState.stackY).thenReturn(100f) whenever(ambientState.stackHeight).thenReturn(100f) val paddingBetweenElements = @@ -486,3 +502,25 @@ class NotificationShelfTest : SysuiTestCase() { assertEquals(expectedAlpha, shelf.viewState.alpha) } } + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class NotificationShelfWithRefactorTest : NotificationShelfTest() { + override val useShelfRefactor: Boolean = true +} + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() { + override val useSensitiveReveal: Boolean = true +} + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class NotificationShelfWithBothFlagsTest : NotificationShelfTest() { + override val useShelfRefactor: Boolean = true + override val useSensitiveReveal: Boolean = true +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index ee8325ec02b5..07eadf7c9bb4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -54,7 +54,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; @@ -119,6 +118,7 @@ import java.util.Optional; @RunWith(AndroidTestingRunner.class) public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationsController mNotificationsController; @Mock private NotificationVisibilityProvider mVisibilityProvider; @@ -157,7 +157,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private StackStateLogger mStackLogger; @Mock private NotificationStackScrollLogger mLogger; @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; - private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private NotificationTargetsHelper mNotificationTargetsHelper; @Mock private SecureSettings mSecureSettings; @Mock private NotificationIconAreaController mIconAreaController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 8ad271bef2e4..72fcdec3c44c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -68,7 +68,9 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; @@ -106,6 +108,7 @@ import java.util.ArrayList; @TestableLooper.RunWithLooper public class NotificationStackScrollLayoutTest extends SysuiTestCase { + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private NotificationStackScrollLayout mStackScroller; // Normally test this private NotificationStackScrollLayout mStackScrollerInternal; // See explanation below private AmbientState mAmbientState; @@ -129,7 +132,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; - @Mock private FeatureFlags mFeatureFlags; @Before public void setUp() throws Exception { @@ -143,11 +145,25 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mNotificationSectionsManager, mBypassController, mStatusBarKeyguardViewManager, - mLargeScreenShadeInterpolator, - mFeatureFlags + mLargeScreenShadeInterpolator )); + // Register the debug flags we use + assertFalse(Flags.NSSL_DEBUG_LINES.getDefault()); + assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault()); + mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false); + mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false); + + // Register the feature flags we use + // TODO: Ideally we wouldn't need to set these unless a test actually reads them, + // and then we would test both configurations, but currently they are all read + // in the constructor. + mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM); + mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS); + mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR); + // Inject dependencies before initializing the layout + mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState); mDependency.injectMockDependency(ShadeController.class); mDependency.injectTestDependency( @@ -176,13 +192,18 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper, mNotificationStackSizeCalculator); mStackScroller = spy(mStackScrollerInternal); - mStackScroller.setShelfController(notificationShelfController); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mStackScroller.setShelfController(notificationShelfController); + } mStackScroller.setNotificationsController(mNotificationsController); mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true); when(mStackScrollLayoutController.getNotificationRoundnessManager()) .thenReturn(mNotificationRoundnessManager); mStackScroller.setController(mStackScrollLayoutController); + if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mStackScroller.setShelf(mNotificationShelf); + } doNothing().when(mGroupExpansionManager).collapseGroups(); doNothing().when(mExpandHelper).cancelImmediately(); @@ -899,7 +920,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void testWindowInsetAnimationProgress_updatesBottomInset() { int bottomImeInset = 100; - mStackScrollerInternal.setAnimatedInsetsEnabled(true); WindowInsets windowInsets = new WindowInsets.Builder() .setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build(); ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index df65c09eb8a9..85a2bdd21073 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -47,6 +47,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; @@ -83,7 +84,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { private Handler mHandler; private ExpandableNotificationRow mNotificationRow; private Runnable mFalsingCheck; - private FeatureFlags mFeatureFlags; + private final FeatureFlags mFeatureFlags = new FakeFeatureFlags(); private static final int FAKE_ROW_WIDTH = 20; private static final int FAKE_ROW_HEIGHT = 20; @@ -96,7 +97,6 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); mNotificationRoundnessManager = mock(NotificationRoundnessManager.class); - mFeatureFlags = mock(FeatureFlags.class); mSwipeHelper = spy(new NotificationSwipeHelper( mContext.getResources(), ViewConfiguration.get(mContext), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt index 45725ced521c..e30947ce84bd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt @@ -18,6 +18,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @RunWithLooper class NotificationTargetsHelperTest : SysuiTestCase() { + private val featureFlags = FakeFeatureFlags() lateinit var notificationTestHelper: NotificationTestHelper private val sectionsManager: NotificationSectionsManager = mock() private val stackScrollLayout: NotificationStackScrollLayout = mock() @@ -26,10 +27,10 @@ class NotificationTargetsHelperTest : SysuiTestCase() { fun setUp() { allowTestableLooperAsMainThread() notificationTestHelper = - NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) + NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags) } - private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags()) + private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags) @Test fun targetsForFirstNotificationInGroup() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 4c97d20c5da8..987861d3f133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -8,7 +8,6 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation.getContentAlpha import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.EmptyShadeView import com.android.systemui.statusbar.NotificationShelf @@ -45,7 +44,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() { private val dumpManager = mock<DumpManager>() private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>() private val notificationShelf = mock<NotificationShelf>() - private val featureFlags = mock<FeatureFlags>() private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply { layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100) } @@ -56,7 +54,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() { /* bypassController */ { false }, mStatusBarKeyguardViewManager, largeScreenShadeInterpolator, - featureFlags, ) private val testableResources = mContext.getOrCreateTestableResources() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index 9c7f6190de44..33144f233a71 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -64,7 +64,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.classifier.FalsingCollectorFake; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; @@ -117,6 +117,7 @@ import java.util.Optional; public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private static final int DISPLAY_ID = 0; + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private AssistManager mAssistManager; @@ -256,7 +257,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), mPowerInteractor, - mock(FeatureFlags.class), + mFeatureFlags, mUserTracker ); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index cd8aaa2685c2..9c52788dc2eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -79,7 +79,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private CommandQueue mCommandQueue; private FakeMetricsLogger mMetricsLogger; private final ShadeController mShadeController = mock(ShadeController.class); - private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class); private final NotificationsInteractor mNotificationsInteractor = mock(NotificationsInteractor.class); private final KeyguardStateController mKeyguardStateController = @@ -118,7 +117,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class), mKeyguardStateController, - mCentralSurfaces, mNotificationsInteractor, mock(LockscreenShadeTransitionController.class), mock(PowerInteractor.class), @@ -202,7 +200,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { when(mKeyguardStateController.isShowing()).thenReturn(true); when(mKeyguardStateController.isOccluded()).thenReturn(false); - when(mCentralSurfaces.isOccluded()).thenReturn(false); assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java index 391c8ca4d286..7c285b8aa1a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java @@ -102,6 +102,8 @@ public class RemoteInputViewTest extends SysuiTestCase { "com.android.sysuitest.dummynotificationsender"; private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1; + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); + @Mock private RemoteInputController mController; @Mock private ShortcutManager mShortcutManager; @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; @@ -453,8 +455,7 @@ public class RemoteInputViewTest extends SysuiTestCase { private RemoteInputViewController bindController( RemoteInputView view, NotificationEntry entry) { - FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags(); - fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true); + mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true); RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl( view, entry, @@ -462,7 +463,7 @@ public class RemoteInputViewTest extends SysuiTestCase { mController, mShortcutManager, mUiEventLoggerFake, - fakeFeatureFlags + mFeatureFlags ); viewController.bind(); return viewController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 4839eeba2124..17bb73b94ac2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -92,7 +92,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -305,6 +305,7 @@ public class BubblesTest extends SysuiTestCase { private TestableLooper mTestableLooper; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); + private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); private UserHandle mUser0; @@ -423,7 +424,7 @@ public class BubblesTest extends SysuiTestCase { mCommonNotifCollection, mNotifPipeline, mSysUiState, - mock(FeatureFlags.class), + mFeatureFlags, mNotifPipelineFlags, syncExecutor); mBubblesManager.addNotifCallback(mNotifCallback); @@ -432,7 +433,8 @@ public class BubblesTest extends SysuiTestCase { mNotificationTestHelper = new NotificationTestHelper( mContext, mDependency, - TestableLooper.get(this)); + TestableLooper.get(this), + mFeatureFlags); mRow = mNotificationTestHelper.createBubble(mDeleteIntent); mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent); mNonBubbleNotifRow = mNotificationTestHelper.createRow(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt index b94f816e1ca4..36fa7e65d8ec 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt @@ -62,6 +62,32 @@ class FakeFeatureFlags : FeatureFlags { } } + /** + * Set the given flag's default value if no other value has been set. + * + * REMINDER: You should always test your code with your flag in both configurations, so + * generally you should be setting a particular value. This method should be reserved for + * situations where the flag needs to be read (e.g. in the class constructor), but its + * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use + * this method than to hard-code `false` or `true` because then at least if you're wrong, + * and the flag value *does* matter, you'll notice when the flag is flipped and tests + * start failing. + */ + fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default) + + /** + * Set the given flag's default value if no other value has been set. + * + * REMINDER: You should always test your code with your flag in both configurations, so + * generally you should be setting a particular value. This method should be reserved for + * situations where the flag needs to be read (e.g. in the class constructor), but its + * value shouldn't affect the actual test cases. In those cases, it's mildly safer to use + * this method than to hard-code `false` or `true` because then at least if you're wrong, + * and the flag value *does* matter, you'll notice when the flag is flipped and tests + * start failing. + */ + fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default) + private fun notifyFlagChanged(flag: Flag<*>) { flagListeners[flag.id]?.let { listeners -> listeners.forEach { listener -> diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp index 2e0a9462ffbe..1f0181f2e5bd 100644 --- a/packages/SystemUI/unfold/Android.bp +++ b/packages/SystemUI/unfold/Android.bp @@ -33,7 +33,7 @@ android_library { "dagger2", "jsr330", ], - kotlincflags: ["-Xjvm-default=enable"], + kotlincflags: ["-Xjvm-default=all"], java_version: "1.8", sdk_version: "current", min_sdk_version: "current", diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt index fee485d97afa..896444d4df5a 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt @@ -35,14 +35,12 @@ interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgre interface TransitionProgressListener { /** Called when transition is started */ - @JvmDefault fun onTransitionStarted() {} /** * Called whenever transition progress is updated, [progress] is a value of the animation * where 0 is fully folded, 1 is fully unfolded */ - @JvmDefault fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {} /** @@ -51,11 +49,9 @@ interface UnfoldTransitionProgressProvider : CallbackController<TransitionProgre * For example, in [PhysicsBasedUnfoldTransitionProgressProvider] this could happen when the * animation is not tied to the hinge angle anymore and it is about to run fixed animation. */ - @JvmDefault fun onTransitionFinishing() {} /** Called when transition is completely finished */ - @JvmDefault fun onTransitionFinished() {} } } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index 0af372f9da24..bce7e8849903 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -31,9 +31,9 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { val isFinishedOpening: Boolean interface FoldUpdatesListener { - @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {} - @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {} - @JvmDefault fun onUnfoldedScreenAvailable() {} + fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {} + fun onFoldUpdate(@FoldUpdate update: Int) {} + fun onUnfoldedScreenAvailable() {} } @IntDef( diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index 1482d078fa8c..3f3fa3419117 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -246,6 +246,31 @@ public class FullScreenMagnificationController implements } @GuardedBy("mLock") + boolean isAtEdge() { + return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge(); + } + + @GuardedBy("mLock") + boolean isAtLeftEdge() { + return getOffsetX() == getMaxOffsetXLocked(); + } + + @GuardedBy("mLock") + boolean isAtRightEdge() { + return getOffsetX() == getMinOffsetXLocked(); + } + + @GuardedBy("mLock") + boolean isAtTopEdge() { + return getOffsetY() == getMaxOffsetYLocked(); + } + + @GuardedBy("mLock") + boolean isAtBottomEdge() { + return getOffsetY() == getMinOffsetYLocked(); + } + + @GuardedBy("mLock") float getCenterX() { return (mMagnificationBounds.width() / 2.0f + mMagnificationBounds.left - getOffsetX()) / getScale(); @@ -1086,6 +1111,87 @@ public class FullScreenMagnificationController implements } /** + * Returns whether the user is at one of the edges (left, right, top, bottom) + * of the magnification viewport + * + * @param displayId + * @return if user is at the edge of the view + */ + public boolean isAtEdge(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isAtEdge(); + } + } + + /** + * Returns whether the user is at the left edge of the viewport + * + * @param displayId + * @return if user is at left edge of view + */ + public boolean isAtLeftEdge(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isAtLeftEdge(); + } + } + + /** + * Returns whether the user is at the right edge of the viewport + * + * @param displayId + * @return if user is at right edge of view + */ + public boolean isAtRightEdge(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isAtRightEdge(); + } + } + + /** + * Returns whether the user is at the top edge of the viewport + * + * @param displayId + * @return if user is at top edge of view + */ + public boolean isAtTopEdge(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isAtTopEdge(); + } + } + + /** + * Returns whether the user is at the bottom edge of the viewport + * + * @param displayId + * @return if user is at bottom edge of view + */ + public boolean isAtBottomEdge(int displayId) { + synchronized (mLock) { + final DisplayMagnification display = mDisplays.get(displayId); + if (display == null) { + return false; + } + return display.isAtBottomEdge(); + } + } + + /** * Returns the Y offset of the magnification viewport. If an animation * is in progress, this reflects the end state of the animation. * diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java index 038847e2a759..4aebbf11c7af 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java @@ -137,6 +137,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting final DetectingState mDetectingState; @VisibleForTesting final PanningScalingState mPanningScalingState; @VisibleForTesting final ViewportDraggingState mViewportDraggingState; + @VisibleForTesting final SinglePanningState mSinglePanningState; private final ScreenStateReceiver mScreenStateReceiver; private final WindowMagnificationPromptController mPromptController; @@ -146,7 +147,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH private PointerCoords[] mTempPointerCoords; private PointerProperties[] mTempPointerProperties; - + @VisibleForTesting boolean mIsSinglePanningEnabled; public FullScreenMagnificationGestureHandler(@UiContext Context context, FullScreenMagnificationController fullScreenMagnificationController, AccessibilityTraceManager trace, @@ -202,6 +203,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH mDetectingState = new DetectingState(context); mViewportDraggingState = new ViewportDraggingState(); mPanningScalingState = new PanningScalingState(context); + mSinglePanningState = new SinglePanningState(context); + setSinglePanningEnabled(false); if (mDetectShortcutTrigger) { mScreenStateReceiver = new ScreenStateReceiver(context, this); @@ -213,6 +216,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH transitionTo(mDetectingState); } + @VisibleForTesting + void setSinglePanningEnabled(boolean isEnabled) { + mIsSinglePanningEnabled = isEnabled; + } + @Override void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) { handleEventWith(mCurrentState, event, rawEvent, policyFlags); @@ -223,6 +231,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH // To keep InputEventConsistencyVerifiers within GestureDetectors happy mPanningScalingState.mScrollGestureDetector.onTouchEvent(event); mPanningScalingState.mScaleGestureDetector.onTouchEvent(event); + mSinglePanningState.mScrollGestureDetector.onTouchEvent(event); try { stateHandler.onMotionEvent(event, rawEvent, policyFlags); @@ -669,7 +678,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @Override public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - // Ensures that the state at the end of delegation is consistent with the last delegated // UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise switch (event.getActionMasked()) { @@ -726,6 +734,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this); + private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN); + DetectingState(Context context) { mLongTapMinDelay = ViewConfiguration.getLongPressTimeout(); mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout() @@ -765,10 +775,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH cacheDelayedMotionEvent(event, rawEvent, policyFlags); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { - mLastDetectingDownEventTime = event.getDownTime(); mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); + mFirstPointerDownLocation.set(event.getX(), event.getY()); + if (!mFullScreenMagnificationController.magnificationRegionContains( mDisplayId, event.getX(), event.getY())) { @@ -800,7 +811,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH break; case ACTION_POINTER_DOWN: { if (isActivated() && event.getPointerCount() == 2) { - storeSecondPointerDownLocation(event); + storePointerDownLocation(mSecondPointerDownLocation, event); mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE, ViewConfiguration.getTapTimeout()); } else { @@ -815,7 +826,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH case ACTION_MOVE: { if (isFingerDown() && distance(mLastDown, /* move */ event) > mSwipeMinDistance) { - // Swipe detected - transition immediately // For convenience, viewport dragging takes precedence @@ -826,10 +836,15 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } else if (isActivated() && event.getPointerCount() == 2) { //Primary pointer is swiping, so transit to PanningScalingState transitToPanningScalingStateAndClear(); + } else if (mIsSinglePanningEnabled + && isActivated() + && event.getPointerCount() == 1 + && !isOverscroll(event)) { + transitToSinglePanningStateAndClear(); } else { transitionToDelegatingStateAndClear(); } - } else if (isActivated() && secondPointerDownValid() + } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation) && distanceClosestPointerToPoint( mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) { //Second pointer is swiping, so transit to PanningScalingState @@ -843,11 +858,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH if (!mFullScreenMagnificationController.magnificationRegionContains( mDisplayId, event.getX(), event.getY())) { - transitionToDelegatingStateAndClear(); } else if (isMultiTapTriggered(3 /* taps */)) { - onTripleTap(/* up */ event); } else if ( @@ -856,7 +869,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH //TODO long tap should never happen here && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay) || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) { - transitionToDelegatingStateAndClear(); } @@ -865,14 +877,28 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH } } - private void storeSecondPointerDownLocation(MotionEvent event) { + private boolean isOverscroll(MotionEvent event) { + if (!pointerDownValid(mFirstPointerDownLocation)) { + return false; + } + float dX = event.getX() - mFirstPointerDownLocation.x; + float dY = event.getY() - mFirstPointerDownLocation.y; + boolean didOverscroll = + mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0 + || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0 + || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0 + || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0; + return didOverscroll; + } + + private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) { final int index = event.getActionIndex(); - mSecondPointerDownLocation.set(event.getX(index), event.getY(index)); + pointerDownLocation.set(event.getX(index), event.getY(index)); } - private boolean secondPointerDownValid() { - return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN( - mSecondPointerDownLocation.y)); + private boolean pointerDownValid(PointF pointerDownLocation) { + return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN( + pointerDownLocation.y)); } private void transitToPanningScalingStateAndClear() { @@ -880,6 +906,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH clear(); } + private void transitToSinglePanningStateAndClear() { + transitionTo(mSinglePanningState); + clear(); + } + public boolean isMultiTapTriggered(int numTaps) { // Shortcut acts as the 2 initial taps @@ -947,6 +978,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH setShortcutTriggered(false); removePendingDelayedMessages(); clearDelayedMotionEvents(); + mFirstPointerDownLocation.set(Float.NaN, Float.NaN); mSecondPointerDownLocation.set(Float.NaN, Float.NaN); } @@ -1165,12 +1197,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH + ", mDelegatingState=" + mDelegatingState + ", mMagnifiedInteractionState=" + mPanningScalingState + ", mViewportDraggingState=" + mViewportDraggingState + + ", mSinglePanningState=" + mSinglePanningState + ", mDetectTripleTap=" + mDetectTripleTap + ", mDetectShortcutTrigger=" + mDetectShortcutTrigger + ", mCurrentState=" + State.nameOf(mCurrentState) + ", mPreviousState=" + State.nameOf(mPreviousState) + ", mMagnificationController=" + mFullScreenMagnificationController + ", mDisplayId=" + mDisplayId + + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled + '}'; } @@ -1285,8 +1319,67 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH * Indicates an error with a gesture handler or state. */ private static class GestureException extends Exception { + GestureException(String message) { super(message); } } + + final class SinglePanningState extends SimpleOnGestureListener implements State { + private final GestureDetector mScrollGestureDetector; + private MotionEventInfo mEvent; + + SinglePanningState(Context context) { + mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain()); + } + + @Override + public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { + int action = event.getActionMasked(); + switch (action) { + case ACTION_UP: + case ACTION_CANCEL: + clear(); + transitionTo(mDetectingState); + break; + } + } + + @Override + public boolean onScroll( + MotionEvent first, MotionEvent second, float distanceX, float distanceY) { + if (mCurrentState != mSinglePanningState) { + return true; + } + mFullScreenMagnificationController.offsetMagnifiedRegion( + mDisplayId, + distanceX, + distanceY, + AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); + if (DEBUG_PANNING_SCALING) { + Slog.i( + mLogTag, + "SinglePanningState Panned content by scrollX: " + + distanceX + + " scrollY: " + + distanceY + + " isAtEdge: " + + mFullScreenMagnificationController.isAtEdge(mDisplayId)); + } + // TODO: b/280812104 Dispatch events before Delegation + if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) { + clear(); + transitionTo(mDelegatingState); + } + return /* event consumed: */ true; + } + + @Override + public String toString() { + return "SinglePanningState{" + + "isEdgeOfView=" + + mFullScreenMagnificationController.isAtEdge(mDisplayId) + + "}"; + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index c9769b3f9932..26bcb9805baa 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5083,6 +5083,10 @@ public class ActivityManagerService extends IActivityManager.Stub // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); SystemProperties.set("dev.bootcomplete", "1"); + + // Start PSI monitoring in LMKD if it was skipped earlier. + ProcessList.startPsiMonitoringAfterBoot(); + mUserController.onBootComplete( new IIntentReceiver.Stub() { @Override diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index acd9771c7750..e484a6cf251b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -357,6 +357,7 @@ public final class ProcessList { static final byte LMK_UPDATE_PROPS = 7; static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed + static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier // Low Memory Killer Daemon command codes. // These must be kept in sync with async_event_type definitions in lmkd.h @@ -1568,6 +1569,15 @@ public final class ProcessList { return true; } + /** + * {@hide} + */ + public static void startPsiMonitoringAfterBoot() { + ByteBuffer buf = ByteBuffer.allocate(4); + buf.putInt(LMK_START_MONITORING); + writeLmkd(buf, null); + } + private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) { if (!sLmkdConnection.isConnected()) { // try to connect immediately and then keep retrying diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 2db724f51918..8e1ad6529bc0 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2491,16 +2491,6 @@ public class NotificationManagerService extends SystemService { getContext().registerReceiver(mReviewNotificationPermissionsReceiver, ReviewNotificationPermissionsReceiver.getFilter(), Context.RECEIVER_NOT_EXPORTED); - - mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null, - new AppOpsManager.OnOpChangedInternalListener() { - @Override - public void onOpChanged(@NonNull String op, @NonNull String packageName, - int userId) { - mHandler.post( - () -> handleNotificationPermissionChange(packageName, userId)); - } - }); } /** @@ -3560,16 +3550,20 @@ public class NotificationManagerService extends SystemService { } mPermissionHelper.setNotificationPermission( pkg, UserHandle.getUserId(uid), enabled, true); + sendAppBlockStateChangedBroadcast(pkg, uid, !enabled); mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES) .setType(MetricsEvent.TYPE_ACTION) .setPackageName(pkg) .setSubtype(enabled ? 1 : 0)); mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled); + // Now, cancel any outstanding notifications that are part of a just-disabled app + if (!enabled) { + cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, + UserHandle.getUserId(uid), REASON_PACKAGE_BANNED); + } - // Outstanding notifications from this package will be cancelled, and the package will - // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the - // callback from AppOpsManager. + handleSavePolicyFile(); } /** @@ -5889,21 +5883,6 @@ public class NotificationManagerService extends SystemService { } }; - private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) { - int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId); - if (uid == INVALID_UID) { - Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId)); - return; - } - boolean hasPermission = mPermissionHelper.hasPermission(uid); - sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission); - if (!hasPermission) { - cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null, - /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId, - REASON_PACKAGE_BANNED); - } - } - protected void checkNotificationListenerAccess() { if (!isCallerSystemOrPhone()) { getContext().enforceCallingPermission( diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 36a0b0c0d8e9..1f5bd3e0cc60 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -75,6 +75,7 @@ import android.util.StatsEvent; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.internal.logging.MetricsLogger; @@ -108,30 +109,34 @@ public class ZenModeHelper { static final int RULE_LIMIT_PER_PACKAGE = 100; // pkg|userId => uid - protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>(); + @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>(); private final Context mContext; private final H mHandler; private final SettingsObserver mSettingsObserver; private final AppOpsManager mAppOps; - @VisibleForTesting protected final NotificationManager mNotificationManager; + private final NotificationManager mNotificationManager; private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory; - @VisibleForTesting protected ZenModeConfig mDefaultConfig; + private ZenModeConfig mDefaultConfig; private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); private final ZenModeFiltering mFiltering; - protected final RingerModeDelegate mRingerModeDelegate = new + private final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate(); @VisibleForTesting protected final ZenModeConditions mConditions; - Object mConfigsLock = new Object(); + private final Object mConfigsArrayLock = new Object(); + @GuardedBy("mConfigsArrayLock") @VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>(); private final Metrics mMetrics = new Metrics(); private final ConditionProviders.Config mServiceConfig; - private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver; - @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger; + private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver; + private final ZenModeEventLogger mZenModeEventLogger; @VisibleForTesting protected int mZenMode; @VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy; private int mUser = UserHandle.USER_SYSTEM; + + private final Object mConfigLock = new Object(); + @GuardedBy("mConfigLock") @VisibleForTesting protected ZenModeConfig mConfig; @VisibleForTesting protected AudioManagerInternal mAudioManager; protected PackageManager mPm; @@ -159,7 +164,7 @@ public class ZenModeHelper { mDefaultConfig = readDefaultConfig(mContext.getResources()); updateDefaultAutomaticRuleNames(); mConfig = mDefaultConfig.copy(); - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { mConfigs.put(UserHandle.USER_SYSTEM, mConfig); } mConsolidatedPolicy = mConfig.toNotificationPolicy(); @@ -186,7 +191,7 @@ public class ZenModeHelper { public boolean matchesCallFilter(UserHandle userHandle, Bundle extras, ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity, int callingUid) { - synchronized (mConfig) { + synchronized (mConfigLock) { return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy, userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity, callingUid); @@ -206,7 +211,7 @@ public class ZenModeHelper { } public boolean shouldIntercept(NotificationRecord record) { - synchronized (mConfig) { + synchronized (mConfigLock) { return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record); } } @@ -221,7 +226,7 @@ public class ZenModeHelper { public void initZenMode() { if (DEBUG) Log.d(TAG, "initZenMode"); - synchronized (mConfig) { + synchronized (mConfigLock) { // "update" config to itself, which will have no effect in the case where a config // was read in via XML, but will initialize zen mode if nothing was read in and the // config remains the default. @@ -250,7 +255,7 @@ public class ZenModeHelper { public void onUserRemoved(int user) { if (user < UserHandle.USER_SYSTEM) return; if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { mConfigs.remove(user); } } @@ -268,7 +273,7 @@ public class ZenModeHelper { mUser = user; if (DEBUG) Log.d(TAG, reason + " u=" + user); ZenModeConfig config = null; - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { if (mConfigs.get(user) != null) { config = mConfigs.get(user).copy(); } @@ -278,7 +283,7 @@ public class ZenModeHelper { config = mDefaultConfig.copy(); config.user = user; } - synchronized (mConfig) { + synchronized (mConfigLock) { setConfigLocked(config, null, reason, Process.SYSTEM_UID, true); } cleanUpZenRules(); @@ -314,7 +319,7 @@ public class ZenModeHelper { public List<ZenRule> getZenRules() { List<ZenRule> rules = new ArrayList<>(); - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return rules; for (ZenRule rule : mConfig.automaticRules.values()) { if (canManageAutomaticZenRule(rule)) { @@ -327,7 +332,7 @@ public class ZenModeHelper { public AutomaticZenRule getAutomaticZenRule(String id) { ZenRule rule; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return null; rule = mConfig.automaticRules.get(id); } @@ -364,7 +369,7 @@ public class ZenModeHelper { } ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) { throw new AndroidRuntimeException("Could not create rule"); } @@ -387,7 +392,7 @@ public class ZenModeHelper { public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule, String reason, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return false; if (DEBUG) { Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule @@ -419,7 +424,7 @@ public class ZenModeHelper { public boolean removeAutomaticZenRule(String id, String reason, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return false; newConfig = mConfig.copy(); ZenRule ruleToRemove = newConfig.automaticRules.get(id); @@ -450,7 +455,7 @@ public class ZenModeHelper { public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return false; newConfig = mConfig.copy(); for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { @@ -467,7 +472,7 @@ public class ZenModeHelper { public void setAutomaticZenRuleState(String id, Condition condition, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return; newConfig = mConfig.copy(); @@ -481,7 +486,7 @@ public class ZenModeHelper { public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return; newConfig = mConfig.copy(); @@ -491,6 +496,7 @@ public class ZenModeHelper { } } + @GuardedBy("mConfigLock") private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules, Condition condition, int callingUid, boolean fromSystemOrSystemUi) { if (rules == null || rules.isEmpty()) return; @@ -538,7 +544,7 @@ public class ZenModeHelper { return 0; } int count = 0; - synchronized (mConfig) { + synchronized (mConfigLock) { for (ZenRule rule : mConfig.automaticRules.values()) { if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) { count++; @@ -555,7 +561,7 @@ public class ZenModeHelper { return 0; } int count = 0; - synchronized (mConfig) { + synchronized (mConfigLock) { for (ZenRule rule : mConfig.automaticRules.values()) { if (pkg.equals(rule.getPkg())) { count++; @@ -588,19 +594,23 @@ public class ZenModeHelper { protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) { updateDefaultAutomaticRuleNames(); - for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) { - ZenRule currRule = mConfig.automaticRules.get(defaultRule.id); - // if default rule wasn't user-modified nor enabled, use localized name - // instead of previous system name - if (currRule != null && !currRule.modified && !currRule.enabled - && !defaultRule.name.equals(currRule.name)) { - if (canManageAutomaticZenRule(currRule)) { - if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name " - + "from " + currRule.name + " to " + defaultRule.name); - // update default rule (if locale changed, name of rule will change) - currRule.name = defaultRule.name; - updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule), - "locale changed", callingUid, fromSystemOrSystemUi); + synchronized (mConfigLock) { + for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) { + ZenRule currRule = mConfig.automaticRules.get(defaultRule.id); + // if default rule wasn't user-modified nor enabled, use localized name + // instead of previous system name + if (currRule != null && !currRule.modified && !currRule.enabled + && !defaultRule.name.equals(currRule.name)) { + if (canManageAutomaticZenRule(currRule)) { + if (DEBUG) { + Slog.d(TAG, "Locale change - updating default zen rule name " + + "from " + currRule.name + " to " + defaultRule.name); + } + // update default rule (if locale changed, name of rule will change) + currRule.name = defaultRule.name; + updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule), + "locale changed", callingUid, fromSystemOrSystemUi); + } } } } @@ -686,7 +696,7 @@ public class ZenModeHelper { private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller, boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) { ZenModeConfig newConfig; - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig == null) return; if (!Global.isValidZenMode(zenMode)) return; if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode) @@ -715,7 +725,7 @@ public class ZenModeHelper { void dump(ProtoOutputStream proto) { proto.write(ZenModeProto.ZEN_MODE, mZenMode); - synchronized (mConfig) { + synchronized (mConfigLock) { if (mConfig.manualRule != null) { mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS); } @@ -737,14 +747,14 @@ public class ZenModeHelper { pw.println(Global.zenModeToString(mZenMode)); pw.print(prefix); pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString()); - synchronized(mConfigsLock) { + synchronized (mConfigsArrayLock) { final int N = mConfigs.size(); for (int i = 0; i < N; i++) { dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i)); } } pw.print(prefix); pw.print("mUser="); pw.println(mUser); - synchronized (mConfig) { + synchronized (mConfigLock) { dump(pw, prefix, "mConfig", mConfig); } @@ -833,7 +843,7 @@ public class ZenModeHelper { Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId); } if (DEBUG) Log.d(TAG, reason); - synchronized (mConfig) { + synchronized (mConfigLock) { setConfigLocked(config, null, reason, Process.SYSTEM_UID, true); } } @@ -841,7 +851,7 @@ public class ZenModeHelper { public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId) throws IOException { - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { final int n = mConfigs.size(); for (int i = 0; i < n; i++) { if (forBackup && mConfigs.keyAt(i) != userId) { @@ -856,7 +866,9 @@ public class ZenModeHelper { * @return user-specified default notification policy for priority only do not disturb */ public Policy getNotificationPolicy() { - return getNotificationPolicy(mConfig); + synchronized (mConfigLock) { + return getNotificationPolicy(mConfig); + } } private static Policy getNotificationPolicy(ZenModeConfig config) { @@ -867,8 +879,8 @@ public class ZenModeHelper { * Sets the global notification policy used for priority only do not disturb */ public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) { - if (policy == null || mConfig == null) return; - synchronized (mConfig) { + synchronized (mConfigLock) { + if (policy == null || mConfig == null) return; final ZenModeConfig newConfig = mConfig.copy(); newConfig.applyNotificationPolicy(policy); setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid, @@ -881,7 +893,7 @@ public class ZenModeHelper { */ private void cleanUpZenRules() { long currentTime = System.currentTimeMillis(); - synchronized (mConfig) { + synchronized (mConfigLock) { final ZenModeConfig newConfig = mConfig.copy(); if (newConfig.automaticRules != null) { for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { @@ -906,7 +918,7 @@ public class ZenModeHelper { * @return a copy of the zen mode configuration */ public ZenModeConfig getConfig() { - synchronized (mConfig) { + synchronized (mConfigLock) { return mConfig.copy(); } } @@ -918,7 +930,8 @@ public class ZenModeHelper { return mConsolidatedPolicy.copy(); } - public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent, + @GuardedBy("mConfigLock") + private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent, String reason, int callingUid, boolean fromSystemOrSystemUi) { return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/, callingUid, fromSystemOrSystemUi); @@ -926,11 +939,12 @@ public class ZenModeHelper { public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason, int callingUid, boolean fromSystemOrSystemUi) { - synchronized (mConfig) { + synchronized (mConfigLock) { setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi); } } + @GuardedBy("mConfigLock") private boolean setConfigLocked(ZenModeConfig config, String reason, ComponentName triggeringComponent, boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) { @@ -942,7 +956,7 @@ public class ZenModeHelper { } if (config.user != mUser) { // simply store away for background users - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { mConfigs.put(config.user, config); } if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user); @@ -951,7 +965,7 @@ public class ZenModeHelper { // handle CPS backed conditions - danger! may modify config mConditions.evaluateConfig(config, null, false /*processSubscriptions*/); - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { mConfigs.put(config.user, config); } if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable()); @@ -979,6 +993,7 @@ public class ZenModeHelper { * Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards. * If logging is enabled, will also request logging of the outcome of this change if needed. */ + @GuardedBy("mConfigLock") private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason, boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) { final boolean logZenModeEvents = mFlagResolver.isEnabled( @@ -993,7 +1008,7 @@ public class ZenModeHelper { } final String val = Integer.toString(config.hashCode()); Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); - evaluateZenMode(reason, setRingerMode); + evaluateZenModeLocked(reason, setRingerMode); // After all changes have occurred, log if requested if (logZenModeEvents) { ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo( @@ -1025,7 +1040,8 @@ public class ZenModeHelper { } @VisibleForTesting - protected void evaluateZenMode(String reason, boolean setRingerMode) { + @GuardedBy("mConfigLock") + protected void evaluateZenModeLocked(String reason, boolean setRingerMode) { if (DEBUG) Log.d(TAG, "evaluateZenMode"); if (mConfig == null) return; final int policyHashBefore = mConsolidatedPolicy == null ? 0 @@ -1056,8 +1072,8 @@ public class ZenModeHelper { } private int computeZenMode() { - if (mConfig == null) return Global.ZEN_MODE_OFF; - synchronized (mConfig) { + synchronized (mConfigLock) { + if (mConfig == null) return Global.ZEN_MODE_OFF; if (mConfig.manualRule != null) return mConfig.manualRule.zenMode; int zen = Global.ZEN_MODE_OFF; for (ZenRule automaticRule : mConfig.automaticRules.values()) { @@ -1094,8 +1110,8 @@ public class ZenModeHelper { } private void updateConsolidatedPolicy(String reason) { - if (mConfig == null) return; - synchronized (mConfig) { + synchronized (mConfigLock) { + if (mConfig == null) return; ZenPolicy policy = new ZenPolicy(); if (mConfig.manualRule != null) { applyCustomPolicy(policy, mConfig.manualRule); @@ -1293,7 +1309,7 @@ public class ZenModeHelper { * Generate pulled atoms about do not disturb configurations. */ public void pullRules(List<StatsEvent> events) { - synchronized (mConfigsLock) { + synchronized (mConfigsArrayLock) { final int numConfigs = mConfigs.size(); for (int i = 0; i < numConfigs; i++) { final int user = mConfigs.keyAt(i); @@ -1319,6 +1335,7 @@ public class ZenModeHelper { } } + @GuardedBy("mConfigsArrayLock") private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule, List<StatsEvent> events) { // Make the ID safe. @@ -1389,7 +1406,7 @@ public class ZenModeHelper { if (mZenMode == Global.ZEN_MODE_OFF || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS - && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) { + && !areAllPriorityOnlyRingerSoundsMuted())) { // in priority only with ringer not muted, save ringer mode changes // in dnd off, save ringer mode changes setPreviousRingerModeSetting(ringerModeNew); @@ -1410,8 +1427,7 @@ public class ZenModeHelper { && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS || mZenMode == Global.ZEN_MODE_ALARMS || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS - && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted( - mConfig)))) { + && areAllPriorityOnlyRingerSoundsMuted()))) { newZen = Global.ZEN_MODE_OFF; } else if (mZenMode != Global.ZEN_MODE_OFF) { ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT; @@ -1430,6 +1446,12 @@ public class ZenModeHelper { return ringerModeExternalOut; } + private boolean areAllPriorityOnlyRingerSoundsMuted() { + synchronized (mConfigLock) { + return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig); + } + } + @Override public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller, int ringerModeInternal, VolumePolicy policy) { @@ -1633,7 +1655,7 @@ public class ZenModeHelper { private void emitRules() { final long now = SystemClock.elapsedRealtime(); final long since = (now - mRuleCountLogTime); - synchronized (mConfig) { + synchronized (mConfigLock) { int numZenRules = mConfig.automaticRules.size(); if (mNumZenRules != numZenRules || since > MINIMUM_LOG_PERIOD_MS) { @@ -1651,7 +1673,7 @@ public class ZenModeHelper { private void emitDndType() { final long now = SystemClock.elapsedRealtime(); final long since = (now - mTypeLogTimeMs); - synchronized (mConfig) { + synchronized (mConfigLock) { boolean dndOn = mZenMode != Global.ZEN_MODE_OFF; int zenType = !dndOn ? DND_OFF : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC; diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java index 99479f088577..54352060cd38 100644 --- a/services/core/java/com/android/server/pm/ArchiveManager.java +++ b/services/core/java/com/android/server/pm/ArchiveManager.java @@ -77,9 +77,8 @@ final class ArchiveManager { snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "archiveApp"); verifyCaller(callerPackageName, callingPackageName); - PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user); - verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource()); + verifyInstaller(packageName, ps.getInstallSource()); List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList( ps.getPackageName(), @@ -125,7 +124,7 @@ final class ArchiveManager { Path.of("/TODO"), null); activityInfos.add(activityInfo); } - // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there + InstallSource installSource = ps.getInstallSource(); String installerPackageName = installSource.mUpdateOwnerPackageName != null ? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName; @@ -159,19 +158,13 @@ final class ArchiveManager { } } - private static void verifyInstallOwnership(String packageName, String callingPackageName, - InstallSource installSource) { - if (!TextUtils.equals(installSource.mInstallerPackageName, - callingPackageName)) { + private static void verifyInstaller(String packageName, InstallSource installSource) { + // TODO(b/291060290) Verify installer supports unarchiving + if (installSource.mUpdateOwnerPackageName == null + && installSource.mInstallerPackageName == null) { throw new SecurityException( - TextUtils.formatSimple("Caller is not the installer of record for %s.", + TextUtils.formatSimple("No installer found to archive app %s.", packageName)); } - String updateOwnerPackageName = installSource.mUpdateOwnerPackageName; - if (updateOwnerPackageName != null - && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) { - throw new SecurityException( - TextUtils.formatSimple("Caller is not the update owner for %s.", packageName)); - } } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index ead26cc0cf05..134b041cb242 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -4311,7 +4311,8 @@ final class InstallPackageHelper { // - It's an APEX or overlay package since stopped state does not affect them. // - It is enumerated with a <initial-package-state> tag having the stopped attribute // set to false - // - It doesn't have a launcher entry which means the user doesn't have a way to unstop it + // - It doesn't have an enabled and exported launcher activity, which means the user + // wouldn't have a way to un-stop it final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0; if (mPm.mShouldStopSystemPackagesByDefault && scanSystemPartition @@ -4337,7 +4338,11 @@ final class InstallPackageHelper { categories.add(Intent.CATEGORY_LAUNCHER); final List<ParsedActivity> activities = parsedPackage.getActivities(); for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) { - final List<ParsedIntentInfo> intents = activities.get(indexActivity).getIntents(); + final ParsedActivity activity = activities.get(indexActivity); + if (!activity.isEnabled() || !activity.isExported()) { + continue; + } + final List<ParsedIntentInfo> intents = activity.getIntents(); for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) { final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter(); if (intentFilter != null && intentFilter.matchCategories(categories) == null) { diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 3c846da9757f..5e23765df346 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -18,7 +18,12 @@ "name": "CtsCompilationTestCases" }, { - "name": "CtsAppEnumerationTestCases" + "name": "CtsAppEnumerationTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] }, { "name": "CtsMatchFlagTestCases" @@ -129,6 +134,9 @@ }, { "name": "PackageManagerServiceHostTests" + }, + { + "name": "CtsAppEnumerationTestCases" } ], "imports": [ diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 7bbe8781e434..5e01c4939d24 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -208,7 +208,6 @@ import com.android.internal.policy.LogDecelerateInterpolator; import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; @@ -228,6 +227,7 @@ import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener; import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.vibrator.HapticFeedbackVibrationProvider; import com.android.server.vr.VrManagerInternal; import com.android.server.wallpaper.WallpaperManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -371,12 +371,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final String TALKBACK_LABEL = "TalkBack"; private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800; - private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); - private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION); - private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = - VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); /** * Keyguard stuff @@ -450,6 +444,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { PackageManager mPackageManager; SideFpsEventHandler mSideFpsEventHandler; LockPatternUtils mLockPatternUtils; + private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider; private boolean mHasFeatureAuto; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; @@ -458,9 +453,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Assigned on main thread, accessed on UI thread volatile VrManagerInternal mVrManagerInternal; - // Vibrator pattern for haptic feedback during boot when safe mode is enabled. - long[] mSafeModeEnabledVibePattern; - /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */ boolean mEnableShiftMenuBugReports = false; @@ -558,7 +550,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mPowerVolUpBehavior; boolean mStylusButtonsEnabled = true; boolean mHasSoftInput = false; - boolean mHapticTextHandleEnabled; boolean mUseTvRouting; boolean mAllowStartActivityForLongPressOnPowerDuringSetup; MetricsLogger mLogger; @@ -2251,9 +2242,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup); - mHapticTextHandleEnabled = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_enableHapticTextHandle); - mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION; mHandleVolumeKeysInWM = mContext.getResources().getBoolean( @@ -2294,8 +2282,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { mContext.registerReceiver(mMultiuserReceiver, filter); mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); - mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), - com.android.internal.R.array.config_safeModeEnabledVibePattern); + mHapticFeedbackVibrationProvider = + new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator); mGlobalKeyManager = new GlobalKeyManager(mContext); @@ -5499,10 +5487,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - static long[] getLongIntArray(Resources r, int resid) { - return ArrayUtils.convertToLongArray(r.getIntArray(resid)); - } - private void bindKeyguard() { synchronized (mLock) { if (mKeyguardBound) { @@ -5989,138 +5973,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!mVibrator.hasVibrator()) { return false; } - VibrationEffect effect = getVibrationEffect(effectId); + VibrationEffect effect = + mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId); if (effect == null) { return false; } - VibrationAttributes attrs = getVibrationAttributes(effectId); - if (always) { - attrs = new VibrationAttributes.Builder(attrs) - .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) - .build(); - } + VibrationAttributes attrs = + mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback( + effectId, /* bypassVibrationIntensitySetting= */ always); mVibrator.vibrate(uid, packageName, effect, reason, attrs); return true; } - private VibrationEffect getVibrationEffect(int effectId) { - long[] pattern; - switch (effectId) { - case HapticFeedbackConstants.CONTEXT_CLICK: - case HapticFeedbackConstants.GESTURE_END: - case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE: - case HapticFeedbackConstants.SCROLL_TICK: - case HapticFeedbackConstants.SEGMENT_TICK: - return VibrationEffect.get(VibrationEffect.EFFECT_TICK); - - case HapticFeedbackConstants.TEXT_HANDLE_MOVE: - if (!mHapticTextHandleEnabled) { - return null; - } - // fallthrough - case HapticFeedbackConstants.CLOCK_TICK: - case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK: - return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.KEYBOARD_RELEASE: - case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: - case HapticFeedbackConstants.ENTRY_BUMP: - case HapticFeedbackConstants.DRAG_CROSSING: - return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); - - case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS - case HapticFeedbackConstants.VIRTUAL_KEY: - case HapticFeedbackConstants.EDGE_RELEASE: - case HapticFeedbackConstants.CALENDAR_DATE: - case HapticFeedbackConstants.CONFIRM: - case HapticFeedbackConstants.GESTURE_START: - case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: - case HapticFeedbackConstants.SCROLL_LIMIT: - return VibrationEffect.get(VibrationEffect.EFFECT_CLICK); - - case HapticFeedbackConstants.LONG_PRESS: - case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: - case HapticFeedbackConstants.DRAG_START: - case HapticFeedbackConstants.EDGE_SQUEEZE: - return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - - case HapticFeedbackConstants.REJECT: - return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); - - case HapticFeedbackConstants.SAFE_MODE_ENABLED: - pattern = mSafeModeEnabledVibePattern; - break; - - case HapticFeedbackConstants.ASSISTANT_BUTTON: - if (mVibrator.areAllPrimitivesSupported( - VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, - VibrationEffect.Composition.PRIMITIVE_TICK)) { - // quiet ramp, short pause, then sharp tick - return VibrationEffect.startComposition() - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) - .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) - .compose(); - } - // fallback for devices without composition support - return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); - - case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, - VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.TOGGLE_ON: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, - VibrationEffect.EFFECT_TICK); - - case HapticFeedbackConstants.TOGGLE_OFF: - return getScaledPrimitiveOrElseEffect( - VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f, - VibrationEffect.EFFECT_TEXTURE_TICK); - - case HapticFeedbackConstants.NO_HAPTICS: - default: - return null; - } - if (pattern.length == 0) { - // No vibration - return null; - } else if (pattern.length == 1) { - // One-shot vibration - return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE); - } else { - // Pattern vibration - return VibrationEffect.createWaveform(pattern, -1); - } - } - - private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale, - int elseEffectId) { - if (mVibrator.areAllPrimitivesSupported(primitiveId)) { - return VibrationEffect.startComposition() - .addPrimitive(primitiveId, scale) - .compose(); - } else { - return VibrationEffect.get(elseEffectId); - } - } - - private VibrationAttributes getVibrationAttributes(int effectId) { - switch (effectId) { - case HapticFeedbackConstants.EDGE_SQUEEZE: - case HapticFeedbackConstants.EDGE_RELEASE: - return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES; - case HapticFeedbackConstants.ASSISTANT_BUTTON: - case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: - case HapticFeedbackConstants.SCROLL_TICK: - case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: - case HapticFeedbackConstants.SCROLL_LIMIT: - return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; - default: - return TOUCH_VIBRATION_ATTRIBUTES; - } - } @Override public void keepScreenOnStartedLw() { @@ -6258,8 +6122,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup="); pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup); pw.print(prefix); - pw.print("mHasSoftInput="); pw.print(mHasSoftInput); - pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + pw.print("mHasSoftInput="); pw.println(mHasSoftInput); pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed); pw.print(" mIncallPowerBehavior="); @@ -6284,6 +6147,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout); pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive); + mHapticFeedbackVibrationProvider.dump(prefix, pw); mGlobalKeyManager.dump(prefix, pw); mKeyCombinationManager.dump(prefix, pw); mSingleKeyGestureDetector.dump(prefix, pw); diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java new file mode 100644 index 000000000000..308ce4f9cb68 --- /dev/null +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -0,0 +1,194 @@ +/* + * Copyright 2023 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.vibrator; + +import android.annotation.Nullable; +import android.content.res.Resources; +import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.os.Vibrator; +import android.view.HapticFeedbackConstants; + +import java.io.PrintWriter; + +/** + * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback. + * + * @hide + */ +public final class HapticFeedbackVibrationProvider { + private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH); + private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION); + private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES = + VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK); + + private final Vibrator mVibrator; + private final boolean mHapticTextHandleEnabled; + // Vibrator effect for haptic feedback during boot when safe mode is enabled. + private final VibrationEffect mSafeModeEnabledVibrationEffect; + + /** @hide */ + public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) { + mVibrator = vibrator; + mHapticTextHandleEnabled = res.getBoolean( + com.android.internal.R.bool.config_enableHapticTextHandle); + mSafeModeEnabledVibrationEffect = + VibrationSettings.createEffectFromResource( + res, com.android.internal.R.array.config_safeModeEnabledVibePattern); + } + + /** + * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in + * {@link HapticFeedbackConstants}). + * + * @param effectId the haptic feedback effect ID whose respective vibration we want to get. + * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if + * the provided effect ID is not supported. + */ + @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) { + switch (effectId) { + case HapticFeedbackConstants.CONTEXT_CLICK: + case HapticFeedbackConstants.GESTURE_END: + case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE: + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SEGMENT_TICK: + return VibrationEffect.get(VibrationEffect.EFFECT_TICK); + + case HapticFeedbackConstants.TEXT_HANDLE_MOVE: + if (!mHapticTextHandleEnabled) { + return null; + } + // fallthrough + case HapticFeedbackConstants.CLOCK_TICK: + case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK: + return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.KEYBOARD_RELEASE: + case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE: + case HapticFeedbackConstants.ENTRY_BUMP: + case HapticFeedbackConstants.DRAG_CROSSING: + return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false); + + case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS + case HapticFeedbackConstants.VIRTUAL_KEY: + case HapticFeedbackConstants.EDGE_RELEASE: + case HapticFeedbackConstants.CALENDAR_DATE: + case HapticFeedbackConstants.CONFIRM: + case HapticFeedbackConstants.GESTURE_START: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + return VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + + case HapticFeedbackConstants.LONG_PRESS: + case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: + case HapticFeedbackConstants.DRAG_START: + case HapticFeedbackConstants.EDGE_SQUEEZE: + return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + + case HapticFeedbackConstants.REJECT: + return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK); + + case HapticFeedbackConstants.SAFE_MODE_ENABLED: + return mSafeModeEnabledVibrationEffect; + + case HapticFeedbackConstants.ASSISTANT_BUTTON: + if (mVibrator.areAllPrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, + VibrationEffect.Composition.PRIMITIVE_TICK)) { + // quiet ramp, short pause, then sharp tick + return VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50) + .compose(); + } + // fallback for devices without composition support + return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK); + + case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f, + VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.TOGGLE_ON: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, + VibrationEffect.EFFECT_TICK); + + case HapticFeedbackConstants.TOGGLE_OFF: + return getScaledPrimitiveOrElseEffect( + VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f, + VibrationEffect.EFFECT_TEXTURE_TICK); + + case HapticFeedbackConstants.NO_HAPTICS: + default: + return null; + } + } + + /** + * Provides the {@link VibrationAttributes} that should be used for a haptic feedback. + * + * @param effectId the haptic feedback effect ID whose respective vibration attributes we want + * to get. + * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass + * vibration intensity settings. {@code false} otherwise. + * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback. + */ + public VibrationAttributes getVibrationAttributesForHapticFeedback( + int effectId, boolean bypassVibrationIntensitySetting) { + VibrationAttributes attrs; + switch (effectId) { + case HapticFeedbackConstants.EDGE_SQUEEZE: + case HapticFeedbackConstants.EDGE_RELEASE: + attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES; + break; + case HapticFeedbackConstants.ASSISTANT_BUTTON: + case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON: + case HapticFeedbackConstants.SCROLL_TICK: + case HapticFeedbackConstants.SCROLL_ITEM_FOCUS: + case HapticFeedbackConstants.SCROLL_LIMIT: + attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES; + break; + default: + attrs = TOUCH_VIBRATION_ATTRIBUTES; + } + if (bypassVibrationIntensitySetting) { + attrs = new VibrationAttributes.Builder(attrs) + .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF) + .build(); + } + return attrs; + } + + /** Dumps relevant state. */ + public void dump(String prefix, PrintWriter pw) { + pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled); + } + + private VibrationEffect getScaledPrimitiveOrElseEffect( + int primitiveId, float scale, int elseEffectId) { + if (mVibrator.areAllPrimitivesSupported(primitiveId)) { + return VibrationEffect.startComposition() + .addPrimitive(primitiveId, scale) + .compose(); + } else { + return VibrationEffect.get(elseEffectId); + } + } +} diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING new file mode 100644 index 000000000000..f0a7e470f8fd --- /dev/null +++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "FrameworksVibratorServicesTests", + "options": [ + {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, + {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, + {"exclude-annotation": "androidx.test.filters.FlakyTest"}, + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ], + "postsubmit": [ + { + "name": "FrameworksVibratorServicesTests", + "options": [ + {"exclude-annotation": "org.junit.Ignore"} + ] + } + ] +} diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 4ae7c77c104e..dbd6bf461e85 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -698,7 +698,23 @@ final class VibrationSettings { @Nullable private VibrationEffect createEffectFromResource(int resId) { - long[] timings = getLongIntArray(mContext.getResources(), resId); + return createEffectFromResource(mContext.getResources(), resId); + } + + /** + * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource.. + * + * <p>If the timings array is {@code null} or empty, it returns {@code null}. + * + * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that + * is equal to the single value in the array. + * + * <p>If the timings array has more than one values, it returns a non-repeating wave-form + * vibration with off-on timings as per the provided timings array. + */ + @Nullable + static VibrationEffect createEffectFromResource(Resources res, int resId) { + long[] timings = getLongIntArray(res, resId); return createEffectFromTimings(timings); } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 799643444b3c..b55af76e3799 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -1016,8 +1016,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub && mWallpaper.userId == mCurrentUserId) { Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent + ", reverting to built-in wallpaper!"); - clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, - null); + clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null); } } }; @@ -1197,7 +1196,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } else { // Timeout Slog.w(TAG, "Reverting to built-in wallpaper!"); - clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); + clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null); final String flattened = wpService.flattenToString(); EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED, flattened.substring(0, Math.min(flattened.length(), @@ -1236,8 +1235,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } else { if (mLmkLimitRebindRetries <= 0) { Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!"); - clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, - null); + clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null); mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES; return; } @@ -1256,7 +1254,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME > SystemClock.uptimeMillis()) { Slog.w(TAG, "Reverting to built-in wallpaper!"); - clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null); + clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null); } else { mWallpaper.lastDiedTime = SystemClock.uptimeMillis(); tryToRebind(); @@ -1497,7 +1495,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper, null)) { Slog.w(TAG, "Wallpaper " + wpService + " no longer available; reverting to default"); - clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); + clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null); } } } @@ -1585,7 +1583,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (doit) { Slog.w(TAG, "Wallpaper uninstalled, removing: " + wallpaper.wallpaperComponent); - clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); + clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null); } } } @@ -1606,7 +1604,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } catch (NameNotFoundException e) { Slog.w(TAG, "Wallpaper component gone, removing: " + wallpaper.wallpaperComponent); - clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); + clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null); } } if (wallpaper.nextWallpaperComponent != null @@ -1638,7 +1636,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mShuttingDown = false; mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); - mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context); + ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context); + mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent; mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mIPackageManager = AppGlobals.getPackageManager(); @@ -1721,7 +1720,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (DEBUG) { Slog.i(TAG, "Unable to regenerate crop; resetting"); } - clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null); + clearWallpaperLocked(FLAG_SYSTEM, UserHandle.USER_SYSTEM, null); } } else { if (DEBUG) { @@ -1978,7 +1977,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (si == null) { - clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply); + clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply); } else { Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked"); // We might end up persisting the current wallpaper data @@ -2002,10 +2001,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (serviceInfo == null) { if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) { - clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null); - clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply); + clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null); + clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply); } else { - clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply); + clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply); } return; } @@ -2038,9 +2037,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData data = null; synchronized (mLock) { if (mIsLockscreenLiveWallpaperEnabled) { - clearWallpaperLocked(callingPackage, false, which, userId); + clearWallpaperLocked(callingPackage, which, userId); } else { - clearWallpaperLocked(false, which, userId, null); + clearWallpaperLocked(which, userId, null); } if (which == FLAG_LOCK) { @@ -2058,8 +2057,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - private void clearWallpaperLocked(String callingPackage, boolean defaultFailed, - int which, int userId) { + private void clearWallpaperLocked(String callingPackage, int which, int userId) { // Might need to bring it in the first time to establish our rewrite if (!mWallpaperMap.contains(userId)) { @@ -2094,7 +2092,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub component = wallpaper.wallpaperComponent; finalWhich = FLAG_LOCK | FLAG_SYSTEM; } else { - component = defaultFailed ? mImageWallpaper : null; + component = null; finalWhich = which; } @@ -2114,8 +2112,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } // TODO(b/266818039) remove this version of the method - private void clearWallpaperLocked(boolean defaultFailed, int which, int userId, - IRemoteCallback reply) { + private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) { if (which != FLAG_SYSTEM && which != FLAG_LOCK) { throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear"); } @@ -2168,9 +2165,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub wallpaper.primaryColors = null; wallpaper.imageWallpaperPending = false; if (userId != mCurrentUserId) return; - if (bindWallpaperComponentLocked(defaultFailed - ? mImageWallpaper - : null, true, false, wallpaper, reply)) { + if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) { return; } } catch (IllegalArgumentException e1) { @@ -3523,11 +3518,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub try { if (componentName == null) { componentName = mDefaultWallpaperComponent; - if (componentName == null) { - // Fall back to static image wallpaper - componentName = mImageWallpaper; - if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper"); - } } int serviceUserId = wallpaper.userId; ServiceInfo si = mIPackageManager.getServiceInfo(componentName, @@ -3997,7 +3987,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM); mFallbackWallpaper.allowBackup = false; mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked(); - bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null); + bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false, + mFallbackWallpaper, null); } } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index c5f63ced989c..a6d5c19395b0 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -285,9 +285,9 @@ class ActivityMetricsLogger { final LaunchingState mLaunchingState; /** The type can be cold (new process), warm (new activity), or hot (bring to front). */ - final int mTransitionType; + int mTransitionType; /** Whether the process was already running when the transition started. */ - final boolean mProcessRunning; + boolean mProcessRunning; /** whether the process of the launching activity didn't have any active activity. */ final boolean mProcessSwitch; /** The process state of the launching activity prior to the launch */ @@ -972,6 +972,19 @@ class ActivityMetricsLogger { // App isn't attached to record yet, so match with info. if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) { info.mBindApplicationDelayMs = info.calculateCurrentDelay(); + if (info.mProcessRunning) { + // It was HOT/WARM launch, but the process was died somehow right after the + // launch request. + info.mProcessRunning = false; + info.mTransitionType = TYPE_TRANSITION_COLD_LAUNCH; + final String msg = "Process " + info.mLastLaunchedActivity.info.processName + + " restarted"; + Slog.i(TAG, msg); + if (info.mLaunchingState.mTraceName != null) { + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg + "#" + + LaunchingState.sTraceSeqId); + } + } } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index e30673cb1f45..ea06b4295850 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7991,6 +7991,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mLastReportedConfiguration.getMergedConfiguration())) { ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */, false /* ignoreVisibility */, true /* isRequestedOrientationChanged */); + if (mTransitionController.inPlayingTransition(this)) { + mTransitionController.mValidateActivityCompat.add(this); + } } mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged( @@ -9410,6 +9413,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (info.applicationInfo == null) { return info.getMinAspectRatio(); } + if (mLetterboxUiController.shouldApplyUserMinAspectRatioOverride()) { + return mLetterboxUiController.getUserMinAspectRatio(); + } if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) { return info.getMinAspectRatio(); } diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index 56edde09f747..271d71ed0ec0 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -24,6 +24,7 @@ import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; @@ -40,7 +41,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -48,7 +48,6 @@ import android.util.DisplayMetrics; import android.util.Slog; import android.view.Display; import android.view.Gravity; -import android.view.IWindowManager; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -56,7 +55,6 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.view.WindowManager; -import android.view.WindowManagerGlobal; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.Button; @@ -95,6 +93,10 @@ public class ImmersiveModeConfirmation { */ @Nullable private Context mWindowContext; + /** + * The root display area feature id that the {@link #mWindowContext} is attaching to. + */ + private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED; // Local copy of vr mode enabled state, to avoid calling into VrManager with // the lock held. private boolean mVrModeEnabled; @@ -206,12 +208,15 @@ public class ImmersiveModeConfirmation { private void handleHide() { if (mClingWindow != null) { if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation"); - // We don't care which root display area the window manager is specifying for removal. - try { - getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow); - } catch (WindowManager.InvalidDisplayException e) { - Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e); - return; + if (mWindowManager != null) { + try { + mWindowManager.removeView(mClingWindow); + } catch (WindowManager.InvalidDisplayException e) { + Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + + e); + } + mWindowManager = null; + mWindowContext = null; } mClingWindow = null; } @@ -394,26 +399,18 @@ public class ImmersiveModeConfirmation { * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the * confirmation window. */ - private WindowManager getWindowManager(int rootDisplayAreaId) { - if (mWindowManager == null || mWindowContext == null) { - // Create window context to specify the RootDisplayArea - final Bundle options = getOptionsForWindowContext(rootDisplayAreaId); - mWindowContext = mContext.createWindowContext( - IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options); - mWindowManager = mWindowContext.getSystemService(WindowManager.class); - return mWindowManager; + @NonNull + private WindowManager createWindowManager(int rootDisplayAreaId) { + if (mWindowManager != null) { + throw new IllegalStateException( + "Must not create a new WindowManager while there is an existing one"); } - - // Update the window context and window manager to specify the RootDisplayArea + // Create window context to specify the RootDisplayArea final Bundle options = getOptionsForWindowContext(rootDisplayAreaId); - final IWindowManager wms = WindowManagerGlobal.getWindowManagerService(); - try { - wms.attachWindowContextToDisplayArea(mWindowContext.getWindowContextToken(), - IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } - + mWindowContextRootDisplayAreaId = rootDisplayAreaId; + mWindowContext = mContext.createWindowContext( + IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options); + mWindowManager = mWindowContext.getSystemService(WindowManager.class); return mWindowManager; } @@ -434,14 +431,23 @@ public class ImmersiveModeConfirmation { } private void handleShow(int rootDisplayAreaId) { - if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation"); + if (mClingWindow != null) { + if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) { + if (DEBUG) Slog.d(TAG, "Immersive mode confirmation has already been shown"); + return; + } else { + // Hide the existing confirmation before show a new one in the new root. + if (DEBUG) Slog.d(TAG, "Immersive mode confirmation was shown in a different root"); + handleHide(); + } + } + if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation"); mClingWindow = new ClingWindowView(mContext, mConfirm); - // show the confirmation - WindowManager.LayoutParams lp = getClingWindowLayoutParams(); + final WindowManager.LayoutParams lp = getClingWindowLayoutParams(); try { - getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp); + createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp); } catch (WindowManager.InvalidDisplayException e) { Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e); } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 39f75703d71f..394105a646f1 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -40,6 +40,12 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.isFixedOrientation; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.screenOrientationToString; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -103,6 +109,7 @@ import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; +import android.os.RemoteException; import android.util.Slog; import android.view.InsetsSource; import android.view.InsetsState; @@ -237,6 +244,10 @@ final class LetterboxUiController { // Counter for ActivityRecord#setRequestedOrientation private int mSetOrientationRequestCounter = 0; + // The min aspect ratio override set by user + @PackageManager.UserMinAspectRatio + private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET; + // The CompatDisplayInsets of the opaque activity beneath the translucent one. private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; @@ -1059,7 +1070,7 @@ final class LetterboxUiController { private float getDefaultMinAspectRatioForUnresizableApps() { if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled() - || mActivityRecord.getDisplayContent() == null) { + || mActivityRecord.getDisplayArea() == null) { return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps() > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps() @@ -1071,8 +1082,8 @@ final class LetterboxUiController { float getSplitScreenAspectRatio() { // Getting the same aspect ratio that apps get in split screen. - final DisplayContent displayContent = mActivityRecord.getDisplayContent(); - if (displayContent == null) { + final DisplayArea displayArea = mActivityRecord.getDisplayArea(); + if (displayArea == null) { return getDefaultMinAspectRatioForUnresizableApps(); } int dividerWindowWidth = @@ -1080,7 +1091,7 @@ final class LetterboxUiController { int dividerInsets = getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets); int dividerSize = dividerWindowWidth - dividerInsets * 2; - final Rect bounds = new Rect(displayContent.getWindowConfiguration().getAppBounds()); + final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds()); if (bounds.width() >= bounds.height()) { bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0); bounds.right = bounds.centerX(); @@ -1091,14 +1102,57 @@ final class LetterboxUiController { return computeAspectRatio(bounds); } + boolean shouldApplyUserMinAspectRatioOverride() { + if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()) { + return false; + } + + try { + final int userAspectRatio = mActivityRecord.mAtmService.getPackageManager() + .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId); + mUserAspectRatio = userAspectRatio; + return userAspectRatio != USER_MIN_ASPECT_RATIO_UNSET; + } catch (RemoteException e) { + // Don't apply user aspect ratio override + Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e); + return false; + } + } + + float getUserMinAspectRatio() { + switch (mUserAspectRatio) { + case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: + return getDisplaySizeMinAspectRatio(); + case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: + return getSplitScreenAspectRatio(); + case USER_MIN_ASPECT_RATIO_16_9: + return 16 / 9f; + case USER_MIN_ASPECT_RATIO_4_3: + return 4 / 3f; + case USER_MIN_ASPECT_RATIO_3_2: + return 3 / 2f; + default: + throw new AssertionError("Unexpected user min aspect ratio override: " + + mUserAspectRatio); + } + } + + private float getDisplaySizeMinAspectRatio() { + final DisplayArea displayArea = mActivityRecord.getDisplayArea(); + if (displayArea == null) { + return mActivityRecord.info.getMinAspectRatio(); + } + final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds()); + return computeAspectRatio(bounds); + } + private float getDefaultMinAspectRatio() { - final DisplayContent displayContent = mActivityRecord.getDisplayContent(); - if (displayContent == null + if (mActivityRecord.getDisplayArea() == null || !mLetterboxConfiguration .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) { return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); } - return computeAspectRatio(new Rect(displayContent.getBounds())); + return getDisplaySizeMinAspectRatio(); } Resources getResources() { diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index b71d918b987f..f33ecaa90531 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -684,6 +684,26 @@ class RecentTasks { } } + /** + * Removes the oldest recent task that is compatible with the given one. This is possible if + * the task windowing mode changed after being added to the Recents. + */ + void removeCompatibleRecentTask(Task task) { + final int taskIndex = mTasks.indexOf(task); + if (taskIndex < 0) { + return; + } + + final int candidateIndex = findRemoveIndexForTask(task, false /* includingSelf */); + if (candidateIndex == -1) { + // Nothing to trim + return; + } + + final Task taskToRemove = taskIndex > candidateIndex ? task : mTasks.get(candidateIndex); + remove(taskToRemove); + } + void removeTasksByPackageName(String packageName, int userId) { for (int i = mTasks.size() - 1; i >= 0; --i) { final Task task = mTasks.get(i); @@ -1546,6 +1566,10 @@ class RecentTasks { * list (if any). */ private int findRemoveIndexForAddTask(Task task) { + return findRemoveIndexForTask(task, true /* includingSelf */); + } + + private int findRemoveIndexForTask(Task task, boolean includingSelf) { final int recentsCount = mTasks.size(); final Intent intent = task.intent; final boolean document = intent != null && intent.isDocument(); @@ -1601,6 +1625,8 @@ class RecentTasks { // existing task continue; } + } else if (!includingSelf) { + continue; } return i; } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 0c1f33ccedbc..92c0987d5636 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1020,7 +1020,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { final WindowContainer<?> parent = getParent(); final Task thisTask = asTask(); if (thisTask != null && parent.asTask() == null - && mTransitionController.isTransientHide(thisTask)) { + && mTransitionController.isTransientVisible(thisTask)) { // Keep transient-hide root tasks visible. Non-root tasks still follow standard rule. return TASK_FRAGMENT_VISIBILITY_VISIBLE; } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index a14354041b91..eaea53d555e2 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -407,6 +407,36 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { return false; } + /** Returns {@code true} if the task should keep visible if this is a transient transition. */ + boolean isTransientVisible(@NonNull Task task) { + if (mTransientLaunches == null) return false; + int occludedCount = 0; + final int numTransient = mTransientLaunches.size(); + for (int i = numTransient - 1; i >= 0; --i) { + final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask(); + if (transientRoot == null) continue; + final WindowContainer<?> rootParent = transientRoot.getParent(); + if (rootParent == null || rootParent.getTopChild() == transientRoot) continue; + final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor + .mOpaqueActivityHelper.getOpaqueActivity(rootParent); + if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) { + occludedCount++; + } + } + if (occludedCount == numTransient) { + for (int i = mTransientLaunches.size() - 1; i >= 0; --i) { + if (mTransientLaunches.keyAt(i).isDescendantOf(task)) { + // Keep transient activity visible until transition finished, so it won't pause + // with transient-hide tasks that may delay resuming the next top. + return true; + } + } + // Let transient-hide activities pause before transition is finished. + return false; + } + return isInTransientHide(task); + } + boolean canApplyDim(@NonNull Task task) { if (mTransientLaunches == null) return true; final Dimmer dimmer = task.getDimmer(); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 881eddc03243..dfaa17494855 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IApplicationThread; import android.app.WindowConfiguration; +import android.graphics.Point; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; @@ -141,6 +142,14 @@ class TransitionController { final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>(); /** + * List of activity-level participants. ActivityRecord is not expected to change independently, + * however, recent compatibility logic can now cause this at arbitrary times determined by + * client code. If it happens during an animation, the surface can be left at the wrong spot. + * TODO(b/290237710) remove when compat logic is moved. + */ + final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>(); + + /** * Currently playing transitions (in the order they were started). When finished, records are * removed from this list. */ @@ -468,15 +477,22 @@ class TransitionController { if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) { return true; } - for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) { - if (mWaitingTransitions.get(i).isInTransientHide(task)) return true; - } for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { if (mPlayingTransitions.get(i).isInTransientHide(task)) return true; } return false; } + boolean isTransientVisible(@NonNull Task task) { + if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) { + return true; + } + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + if (mPlayingTransitions.get(i).isTransientVisible(task)) return true; + } + return false; + } + boolean canApplyDim(@Nullable Task task) { if (task == null) { // Always allow non-activity window. @@ -896,6 +912,14 @@ class TransitionController { } } mValidateCommitVis.clear(); + for (int i = 0; i < mValidateActivityCompat.size(); ++i) { + ActivityRecord ar = mValidateActivityCompat.get(i); + if (ar.getSurfaceControl() == null) continue; + final Point tmpPos = new Point(); + ar.getRelativePosition(tmpPos); + ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y); + } + mValidateActivityCompat.clear(); } /** diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 0eb452d29736..164c8b013c84 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -1615,6 +1615,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final int count = tasksToReparent.size(); for (int i = 0; i < count; ++i) { final Task task = tasksToReparent.get(i); + final int prevWindowingMode = task.getWindowingMode(); if (syncId >= 0) { addToSyncSet(syncId, task); } @@ -1628,6 +1629,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, false /*moveParents*/, "processChildrenTaskReparentHierarchyOp"); } + // Trim the compatible Recent task (if any) after the Task is reparented and now has + // a different windowing mode, in order to prevent redundant Recent tasks after + // reparenting. + if (prevWindowingMode != task.getWindowingMode()) { + mService.mTaskSupervisor.mRecentTasks.removeCompatibleRecentTask(task); + } } if (transition != null) transition.collect(newParent); diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING index be7feb5c9bf7..648b904787ce 100644 --- a/services/incremental/TEST_MAPPING +++ b/services/incremental/TEST_MAPPING @@ -9,6 +9,9 @@ ] }, { + "name": "CtsPackageManagerIncrementalStatsHostTestCases" + }, + { "name": "CtsIncrementalInstallHostTestCases" }, { diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java index 7b1654549841..a8b0a7b5633d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java @@ -40,6 +40,7 @@ import android.os.Build; import android.os.Process; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.text.TextUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -169,14 +170,14 @@ public class ArchiveManagerTest { } @Test - public void archiveApp_callerNotInstallerOfRecord() { + public void archiveApp_noInstallerFound() { InstallSource otherInstallSource = InstallSource.create( CALLER_PACKAGE, CALLER_PACKAGE, - /* installerPackageName= */ "different", + /* installerPackageName= */ null, Binder.getCallingUid(), - CALLER_PACKAGE, + /* updateOwnerPackageName= */ null, /* installerAttributionTag= */ null, /* packageSource= */ 0); when(mPackageState.getInstallSource()).thenReturn(otherInstallSource); @@ -187,29 +188,8 @@ public class ArchiveManagerTest { mIntentSender) ); assertThat(e).hasMessageThat().isEqualTo( - String.format("Caller is not the installer of record for %s.", PACKAGE)); - } - - @Test - public void archiveApp_callerNotUpdateOwner() { - InstallSource otherInstallSource = - InstallSource.create( - CALLER_PACKAGE, - CALLER_PACKAGE, - CALLER_PACKAGE, - Binder.getCallingUid(), - /* updateOwnerPackageName= */ "different", - /* installerAttributionTag= */ null, - /* packageSource= */ 0); - when(mPackageState.getInstallSource()).thenReturn(otherInstallSource); - - Exception e = assertThrows( - SecurityException.class, - () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, - mIntentSender) - ); - assertThat(e).hasMessageThat().isEqualTo( - String.format("Caller is not the update owner for %s.", PACKAGE)); + TextUtils.formatSimple("No installer found to archive app %s.", + PACKAGE)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 32d0c98d4481..989aee06a1df 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -42,6 +43,8 @@ import static org.mockito.Mockito.when; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.Region; import android.os.Handler; import android.os.Message; import android.os.UserHandle; @@ -67,11 +70,13 @@ import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.List; @@ -123,9 +128,10 @@ public class FullScreenMagnificationGestureHandlerTest { public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8; public static final int STATE_PANNING = 9; public static final int STATE_SCALING_AND_PANNING = 10; + public static final int STATE_SINGLE_PANNING = 11; public static final int FIRST_STATE = STATE_IDLE; - public static final int LAST_STATE = STATE_SCALING_AND_PANNING; + public static final int LAST_STATE = STATE_SINGLE_PANNING; // Co-prime x and y, to potentially catch x-y-swapped errors public static final float DEFAULT_X = 301; @@ -155,6 +161,10 @@ public class FullScreenMagnificationGestureHandlerTest { private float mOriginalMagnificationPersistedScale; + static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800); + + static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS); + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -182,11 +192,19 @@ public class FullScreenMagnificationGestureHandlerTest { new MagnificationScaleProvider(mContext), () -> null, ConcurrentUtils.DIRECT_EXECUTOR) { - @Override - public boolean magnificationRegionContains(int displayId, float x, float y) { - return true; - } + @Override + public boolean magnificationRegionContains(int displayId, float x, float y) { + return true; + } }; + + doAnswer((Answer<Void>) invocationOnMock -> { + Object[] args = invocationOnMock.getArguments(); + Region regionArg = (Region) args[1]; + regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS)); + return null; + }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class)); + mFullScreenMagnificationController.register(DISPLAY_0); mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true); mClock = new OffsettableClock.Stopped(); @@ -214,6 +232,7 @@ public class FullScreenMagnificationGestureHandlerTest { mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback, detectTripleTap, detectShortcutTrigger, mWindowMagnificationPromptController, DISPLAY_0); + h.setSinglePanningEnabled(true); mHandler = new TestHandler(h.mDetectingState, mClock) { @Override protected String messageToString(Message m) { @@ -239,6 +258,7 @@ public class FullScreenMagnificationGestureHandlerTest { * {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE}) */ @Test + @Ignore("b/291925580") public void testEachState_isReachableAndRecoverable() { forEachState(state -> { goFromStateIdleTo(state); @@ -526,6 +546,75 @@ public class FullScreenMagnificationGestureHandlerTest { } @Test + public void testActionUpNotAtEdge_singlePanningState_detectingState() { + goFromStateIdleTo(STATE_SINGLE_PANNING); + + send(upEvent()); + + check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE); + assertTrue(isZoomed()); + } + + @Test + public void testScroll_SinglePanningDisabled_delegatingState() { + mMgh.setSinglePanningEnabled(false); + + goFromStateIdleTo(STATE_ACTIVATED); + allowEventDelegation(); + swipeAndHold(); + + assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState); + } + + @Test + public void testScroll_zoomedStateAndAtEdge_delegatingState() { + goFromStateIdleTo(STATE_ACTIVATED); + mFullScreenMagnificationController.setCenter( + DISPLAY_0, + INITIAL_MAGNIFICATION_BOUNDS.left, + INITIAL_MAGNIFICATION_BOUNDS.top / 2, + false, + 1); + final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1; + PointF initCoords = + new PointF( + mFullScreenMagnificationController.getCenterX(DISPLAY_0), + mFullScreenMagnificationController.getCenterY(DISPLAY_0)); + PointF endCoords = new PointF(initCoords.x, initCoords.y); + endCoords.offset(swipeMinDistance, 0); + allowEventDelegation(); + + swipeAndHold(initCoords, endCoords); + + assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState); + assertTrue(isZoomed()); + } + + @Test + public void testScroll_singlePanningAndAtEdge_delegatingState() { + goFromStateIdleTo(STATE_SINGLE_PANNING); + mFullScreenMagnificationController.setCenter( + DISPLAY_0, + INITIAL_MAGNIFICATION_BOUNDS.left, + INITIAL_MAGNIFICATION_BOUNDS.top / 2, + false, + 1); + final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1; + PointF initCoords = + new PointF( + mFullScreenMagnificationController.getCenterX(DISPLAY_0), + mFullScreenMagnificationController.getCenterY(DISPLAY_0)); + PointF endCoords = new PointF(initCoords.x, initCoords.y); + endCoords.offset(swipeMinDistance, 0); + allowEventDelegation(); + + swipeAndHold(initCoords, endCoords); + + assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState); + assertTrue(isZoomed()); + } + + @Test public void testShortcutTriggered_invokeShowWindowPromptAction() { goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED); @@ -740,6 +829,10 @@ public class FullScreenMagnificationGestureHandlerTest { state); check(mMgh.mPanningScalingState.mScaling, state); } break; + case STATE_SINGLE_PANNING: { + check(isZoomed(), state); + check(mMgh.mCurrentState == mMgh.mSinglePanningState, state); + } break; default: throw new IllegalArgumentException("Illegal state: " + state); } } @@ -803,6 +896,10 @@ public class FullScreenMagnificationGestureHandlerTest { send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4)); send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5)); } break; + case STATE_SINGLE_PANNING: { + goFromStateIdleTo(STATE_ACTIVATED); + swipeAndHold(); + } break; default: throw new IllegalArgumentException("Illegal state: " + state); } @@ -859,6 +956,10 @@ public class FullScreenMagnificationGestureHandlerTest { case STATE_SCALING_AND_PANNING: { returnToNormalFrom(STATE_PANNING); } break; + case STATE_SINGLE_PANNING: { + send(upEvent()); + returnToNormalFrom(STATE_ACTIVATED); + } break; default: throw new IllegalArgumentException("Illegal state: " + state); } } @@ -906,6 +1007,11 @@ public class FullScreenMagnificationGestureHandlerTest { send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2)); } + private void swipeAndHold(PointF start, PointF end) { + send(downEvent(start.x, start.y)); + send(moveEvent(end.x, end.y)); + } + private void longTap() { send(downEvent()); fastForward(2000); diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING new file mode 100644 index 000000000000..0ffa891ce3e1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.contentcapture" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING new file mode 100644 index 000000000000..419508ca5e17 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING @@ -0,0 +1,18 @@ +{ + "presubmit": [ + { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.contentprotection" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a109d5cddd21..f552ab2dab60 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -406,7 +406,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { UriGrantsManagerInternal mUgmInternal; @Mock AppOpsManager mAppOpsManager; - private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener; @Mock private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback; @@ -606,12 +605,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName, SEARCH_SELECTOR_PKG); - doAnswer(invocation -> { - mOnPermissionChangeListener = invocation.getArgument(2); - return null; - }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(), - any()); - mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper())); mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient, mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, @@ -3224,6 +3217,48 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testUpdateAppNotifyCreatorBlock() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + + mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + Thread.sleep(500); + waitForIdle(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); + + assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, + captor.getValue().getAction()); + assertEquals(PKG, captor.getValue().getPackage()); + assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); + } + + @Test + public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); + + mBinderService.setNotificationsEnabledForPackage(PKG, 0, false); + verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null)); + } + + @Test + public void testUpdateAppNotifyCreatorUnblock() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); + + mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true); + Thread.sleep(500); + waitForIdle(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); + + assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, + captor.getValue().getAction()); + assertEquals(PKG, captor.getValue().getPackage()); + assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); + } + + @Test public void testUpdateChannelNotifyCreatorBlock() throws Exception { mService.setPreferencesHelper(mPreferencesHelper); when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(), @@ -12139,134 +12174,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER)); } - @Test - public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage() - throws RemoteException { - // Have preexisting posted notifications from revoked package and other packages. - mService.addNotification(new NotificationRecord(mContext, - generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel)); - mService.addNotification(new NotificationRecord(mContext, - generateSbn("other", 1002, 2, 0), mTestNotificationChannel)); - // Have preexisting enqueued notifications from revoked package and other packages. - mService.addEnqueuedNotification(new NotificationRecord(mContext, - generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel)); - mService.addEnqueuedNotification(new NotificationRecord(mContext, - generateSbn("other", 1002, 4, 0), mTestNotificationChannel)); - assertThat(mService.mNotificationList).hasSize(2); - assertThat(mService.mEnqueuedNotifications).hasSize(2); - - when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001); - when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false); - - mOnPermissionChangeListener.onOpChanged( - AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0); - waitForIdle(); - - assertThat(mService.mNotificationList).hasSize(1); - assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other"); - assertThat(mService.mEnqueuedNotifications).hasSize(1); - assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo( - "other"); - } - - @Test - public void onOpChanged_permissionStillGranted_notificationsAreNotAffected() - throws RemoteException { - // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission - // being now granted, AND having previously posted notifications from said package) should - // never happen (if we trust the broadcasts are correct). So this test is for a what-if - // scenario, to verify we still handle it reasonably. - - // Have preexisting posted notifications from specific package and other packages. - mService.addNotification(new NotificationRecord(mContext, - generateSbn("granted", 1001, 1, 0), mTestNotificationChannel)); - mService.addNotification(new NotificationRecord(mContext, - generateSbn("other", 1002, 2, 0), mTestNotificationChannel)); - // Have preexisting enqueued notifications from specific package and other packages. - mService.addEnqueuedNotification(new NotificationRecord(mContext, - generateSbn("granted", 1001, 3, 0), mTestNotificationChannel)); - mService.addEnqueuedNotification(new NotificationRecord(mContext, - generateSbn("other", 1002, 4, 0), mTestNotificationChannel)); - assertThat(mService.mNotificationList).hasSize(2); - assertThat(mService.mEnqueuedNotifications).hasSize(2); - - when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001); - when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true); - - mOnPermissionChangeListener.onOpChanged( - AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0); - waitForIdle(); - - assertThat(mService.mNotificationList).hasSize(2); - assertThat(mService.mEnqueuedNotifications).hasSize(2); - } - - @Test - public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception { - when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001); - when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true); - - mOnPermissionChangeListener.onOpChanged( - AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); - waitForIdle(); - mTestableLooper.moveTimeForward(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - assertThat(captor.getValue().getAction()).isEqualTo( - NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED); - assertThat(captor.getValue().getPackage()).isEqualTo(PKG); - assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse(); - } - - @Test - public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception { - when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001); - when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false); - - mOnPermissionChangeListener.onOpChanged( - AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); - waitForIdle(); - mTestableLooper.moveTimeForward(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - assertThat(captor.getValue().getAction()).isEqualTo( - NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED); - assertThat(captor.getValue().getPackage()).isEqualTo(PKG); - assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue(); - } - - @Test - public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception { - mService.addNotification(new NotificationRecord(mContext, - generateSbn("package", 1001, 1, 0), mTestNotificationChannel)); - assertThat(mService.mNotificationList).hasSize(1); - when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001); - when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn( - true); - - // Start with granted permission and simulate effect of revoking it. - when(mPermissionHelper.hasPermission(1001)).thenReturn(true); - doAnswer(invocation -> { - when(mPermissionHelper.hasPermission(1001)).thenReturn(false); - mOnPermissionChangeListener.onOpChanged( - AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0); - return null; - }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true); - - mBinderService.setNotificationsEnabledForPackage("package", 1001, false); - waitForIdle(); - - assertThat(mService.mNotificationList).hasSize(0); - - mTestableLooper.moveTimeForward(500); - waitForIdle(); - verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null)); - } - private static <T extends Parcelable> T parcelAndUnparcel(T source, Parcelable.Creator<T> creator) { Parcel parcel = Parcel.obtain(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index dedb8f170ee0..3ee75de23fdb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -771,7 +771,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mConfig = null; // will evaluate config to zen mode off for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenMode("test", true); + mZenModeHelper.evaluateZenModeLocked("test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -798,7 +798,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenMode("test", true); + mZenModeHelper.evaluateZenModeLocked("test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -825,7 +825,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenMode("test", true); + mZenModeHelper.evaluateZenModeLocked("test", true); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -2269,7 +2269,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off // given that we don't have any zen rules active. mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.evaluateZenMode("test", true); + mZenModeHelper.evaluateZenModeLocked("test", true); // Check that the change actually took: zen mode should be off now assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode); diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml index 2a15c15fce41..a14ea5598758 100644 --- a/services/tests/vibrator/AndroidManifest.xml +++ b/services/tests/vibrator/AndroidManifest.xml @@ -31,8 +31,7 @@ <!-- Required to set always-on vibrations --> <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" /> - <application android:debuggable="true" - android:testOnly="true"> + <application android:debuggable="true"> <uses-library android:name="android.test.mock" android:required="true" /> <uses-library android:name="android.test.runner" /> </application> diff --git a/services/tests/vibrator/AndroidTest.xml b/services/tests/vibrator/AndroidTest.xml deleted file mode 100644 index d5ee3afaedb6..000000000000 --- a/services/tests/vibrator/AndroidTest.xml +++ /dev/null @@ -1,34 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2023 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. ---> - -<configuration description="Runs Frameworks Vibrator Services Tests."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-instrumentation" /> - - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="install-arg" value="-t" /> - <option name="test-file-name" value="FrameworksVibratorServicesTests.apk" /> - </target_preparer> - - <option name="test-tag" value="FrameworksVibratorServicesTests" /> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest"> - <option name="package" value="com.android.framework.services.tests.vibrator" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <option name="hidden-api-checks" value="false" /> - </test> -</configuration> diff --git a/services/tests/vibrator/TEST_MAPPING b/services/tests/vibrator/TEST_MAPPING index f0a7e470f8fd..22b72fa4ff9e 100644 --- a/services/tests/vibrator/TEST_MAPPING +++ b/services/tests/vibrator/TEST_MAPPING @@ -1,21 +1,7 @@ { - "presubmit": [ + "imports": [ { - "name": "FrameworksVibratorServicesTests", - "options": [ - {"exclude-annotation": "android.platform.test.annotations.LargeTest"}, - {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"}, - {"exclude-annotation": "org.junit.Ignore"} - ] - } - ], - "postsubmit": [ - { - "name": "FrameworksVibratorServicesTests", - "options": [ - {"exclude-annotation": "org.junit.Ignore"} - ] + "path": "frameworks/base/services/core/java/com/android/server/vibrator" } ] } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 5c3102d870d0..65e77dcd4ca9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -182,12 +182,12 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { @Test public void testLaunchState() { - final ToIntFunction<Boolean> launchTemplate = doRelaunch -> { + final ToIntFunction<Runnable> launchTemplate = action -> { clearInvocations(mLaunchObserver); onActivityLaunched(mTopActivity); notifyTransitionStarting(mTopActivity); - if (doRelaunch) { - mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity); + if (action != null) { + action.run(); } final ActivityMetricsLogger.TransitionInfoSnapshot info = notifyWindowsDrawn(mTopActivity); @@ -199,21 +199,27 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase { // Assume that the process is started (ActivityBuilder has mocked the returned value of // ATMS#getProcessController) but the activity has not attached process. mTopActivity.app = null; - assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(null)) .isEqualTo(WaitResult.LAUNCH_STATE_WARM); mTopActivity.app = app; mNewActivityCreated = false; - assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(null)) .isEqualTo(WaitResult.LAUNCH_STATE_HOT); - assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */)) + assertWithMessage("Relaunch").that(launchTemplate.applyAsInt( + () -> mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity))) .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH); + assertWithMessage("Cold launch by restart").that(launchTemplate.applyAsInt( + () -> mActivityMetricsLogger.notifyBindApplication( + mTopActivity.info.applicationInfo))) + .isEqualTo(WaitResult.LAUNCH_STATE_COLD); + mTopActivity.app = null; mNewActivityCreated = true; doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid); - assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */)) + assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(null)) .isEqualTo(WaitResult.LAUNCH_STATE_COLD); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index f23e56df2580..7cb58022c0e7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1303,6 +1303,26 @@ public class RecentTasksTest extends WindowTestsBase { assertTrue(info.supportsMultiWindow); } + @Test + public void testRemoveCompatibleRecentTask() { + final Task task1 = createTaskBuilder(".Task").setWindowingMode( + WINDOWING_MODE_FULLSCREEN).build(); + mRecentTasks.add(task1); + final Task task2 = createTaskBuilder(".Task").setWindowingMode( + WINDOWING_MODE_MULTI_WINDOW).build(); + mRecentTasks.add(task2); + assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, + true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); + + // Set windowing mode and ensure the same fullscreen task that created earlier is removed. + task2.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + mRecentTasks.removeCompatibleRecentTask(task2); + assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, + true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); + assertEquals(task2.mTaskId, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, + true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().get(0).taskId); + } + private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) { HardwareBuffer buffer = null; if (bufferSize != null) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index abf21a57dd40..7eab06ac8b95 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -656,7 +656,7 @@ public class RootTaskTests extends WindowTestsBase { topSplitPrimary.getVisibility(null /* starting */)); // Make primary split root transient-hide. spyOn(splitPrimary.mTransitionController); - doReturn(true).when(splitPrimary.mTransitionController).isTransientHide( + doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible( organizer.mPrimary); // The split root and its top become visible. assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE, diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java index b181213df003..fb957485d9eb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java @@ -91,7 +91,7 @@ public class ScreenshotTests { } @Test - public void testScreenshotSecureLayers() { + public void testScreenshotSecureLayers() throws InterruptedException { SurfaceControl secureSC = new SurfaceControl.Builder() .setName("SecureChildSurfaceControl") .setBLASTLayer() @@ -197,6 +197,8 @@ public class ScreenshotTests { private static final long WAIT_TIMEOUT_S = 5; private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final CountDownLatch mAttachedLatch = new CountDownLatch(1); + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -204,7 +206,16 @@ public class ScreenshotTests { PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); } - SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) { + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + mAttachedLatch.countDown(); + } + + SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) + throws InterruptedException { + assertTrue("Failed to wait for onAttachedToWindow", + mAttachedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS)); SurfaceControl.Transaction t = new SurfaceControl.Transaction(); CountDownLatch countDownLatch = new CountDownLatch(1); mHandler.post(() -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 3908947804cd..d5afe3b2f078 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -27,6 +27,11 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; @@ -91,9 +96,12 @@ import android.compat.testing.PlatformCompatChangeRule; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; +import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; @@ -2255,6 +2263,169 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + + float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth); + + testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN); + } + + @Test + public void testUserOverrideSplitScreenAspectRatioForPortraitDisplay() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + + float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight); + + testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN); + } + + @Test + public void testUserOverrideDisplaySizeAspectRatioForLandscapeDisplay() { + final int displayWidth = 1600; + final int displayHeight = 1400; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + + float expectedAspectRatio = 1f * displayWidth / displayHeight; + + testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE); + } + + @Test + public void testUserOverrideDisplaySizeAspectRatioForPortraitDisplay() { + final int displayWidth = 1400; + final int displayHeight = 1600; + setUpDisplaySizeWithApp(displayWidth, displayHeight); + + float expectedAspectRatio = 1f * displayHeight / displayWidth; + + testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE); + } + + @Test + public void testUserOverride32AspectRatioForPortraitDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600); + testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2); + } + + @Test + public void testUserOverride32AspectRatioForLandscapeDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400); + testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2); + } + + @Test + public void testUserOverride43AspectRatioForPortraitDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600); + testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3); + } + + @Test + public void testUserOverride43AspectRatioForLandscapeDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400); + testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3); + } + + @Test + public void testUserOverride169AspectRatioForPortraitDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1800, /* dh */ 1500); + testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9); + } + + @Test + public void testUserOverride169AspectRatioForLandscapeDisplay() { + setUpDisplaySizeWithApp(/* dw */ 1500, /* dh */ 1800); + testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9); + } + + @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testUserOverrideAspectRatioOverSystemOverride() { + setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400); + + testUserOverrideAspectRatio(false, + SCREEN_ORIENTATION_PORTRAIT, + 3 / 2f, + USER_MIN_ASPECT_RATIO_3_2, + true); + } + + @Test + public void testUserOverrideAspectRatioNotEnabled() { + setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400); + + // App aspect ratio doesn't change + testUserOverrideAspectRatio(false, + SCREEN_ORIENTATION_PORTRAIT, + 1f * 1600 / 1400, + USER_MIN_ASPECT_RATIO_3_2, + false); + } + + private void testUserOverrideAspectRatio(float expectedAspectRatio, + @PackageManager.UserMinAspectRatio int aspectRatio) { + testUserOverrideAspectRatio(true, + SCREEN_ORIENTATION_PORTRAIT, + expectedAspectRatio, + aspectRatio, + true); + + testUserOverrideAspectRatio(false, + SCREEN_ORIENTATION_PORTRAIT, + expectedAspectRatio, + aspectRatio, + true); + + testUserOverrideAspectRatio(true, + SCREEN_ORIENTATION_LANDSCAPE, + expectedAspectRatio, + aspectRatio, + true); + + testUserOverrideAspectRatio(false, + SCREEN_ORIENTATION_LANDSCAPE, + expectedAspectRatio, + aspectRatio, + true); + } + + private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation, + float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio, + boolean enabled) { + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + activity.mWmService.mLetterboxConfiguration + .setUserAppAspectRatioSettingsOverrideEnabled(enabled); + // Set user aspect ratio override + final IPackageManager pm = mAtm.getPackageManager(); + try { + doReturn(aspectRatio).when(pm) + .getUserMinAspectRatio(activity.packageName, activity.mUserId); + } catch (RemoteException ignored) { + } + + prepareLimitedBounds(activity, screenOrientation, isUnresizable); + + final Rect afterBounds = activity.getBounds(); + final int width = afterBounds.width(); + final int height = afterBounds.height(); + final float afterAspectRatio = + (float) Math.max(width, height) / (float) Math.min(width, height); + + assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f); + } + + @Test @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN}) public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() { diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index ffecafb7b4ed..5154d17f2e6b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1488,6 +1488,47 @@ public class TransitionTests extends WindowTestsBase { } @Test + public void testIsTransientVisible() { + final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true) + .setVisible(false).build(); + final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true) + .setVisible(false).build(); + final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final Task taskA = appA.getTask(); + final Task taskB = appB.getTask(); + final Task taskRecent = recent.getTask(); + registerTestTransitionPlayer(); + final TransitionController controller = mRootWindowContainer.mTransitionController; + final Transition transition = createTestTransition(TRANSIT_OPEN, controller); + controller.moveToCollecting(transition); + transition.collect(recent); + transition.collect(taskA); + transition.setTransientLaunch(recent, taskA); + taskRecent.moveToFront("move-recent-to-front"); + + // During collecting and playing, the recent is on top so it is visible naturally. + // While B needs isTransientVisible to keep visibility because it is occluded by recents. + assertFalse(controller.isTransientVisible(taskB)); + assertTrue(controller.isTransientVisible(taskA)); + assertFalse(controller.isTransientVisible(taskRecent)); + // Switch to playing state. + transition.onTransactionReady(transition.getSyncId(), mMockT); + assertTrue(controller.isTransientVisible(taskA)); + + // Switch to another task. For example, use gesture navigation to switch tasks. + taskB.moveToFront("move-b-to-front"); + // The previous app (taskA) should be paused first so it loses transient visible. Because + // visually it is taskA -> taskB, the pause -> resume order should be the same. + assertFalse(controller.isTransientVisible(taskA)); + // Keep the recent visible so there won't be 2 activities pausing at the same time. It is + // to avoid the latency to resume the current top, i.e. appB. + assertTrue(controller.isTransientVisible(taskRecent)); + // The recent is paused after the transient transition is finished. + controller.finishTransition(transition); + assertFalse(controller.isTransientVisible(taskRecent)); + } + + @Test public void testNotReadyPushPop() { final TransitionController controller = new TestTransitionController(mAtm); controller.setSyncEngine(mWm.mSyncEngine); diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt index 0f406fda28b6..845e64949230 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt @@ -70,6 +70,9 @@ class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: LegacyFlickerTest @Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {} + @FlakyTest(bugId = 291575593) + override fun entireScreenCovered() {} + @Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {} @Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt index 7a16060e3370..94b090f42c9b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt @@ -200,7 +200,7 @@ open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) : override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = super.visibleWindowsShownMoreThanOneConsecutiveEntry() - @FlakyTest(bugId = 251217585) + @FlakyTest(bugId = 285980483) @Test override fun focusChanges() { super.focusChanges() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index 4164c0d440c0..df9780ef8b98 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -21,6 +21,7 @@ import android.app.WallpaperManager import android.content.res.Resources import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER @@ -190,6 +191,16 @@ class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { } } + @Presubmit + @Test + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { + flicker.assertLayers { + this.visibleLayersShownMoreThanOneConsecutiveEntry( + VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp) + ) + } + } + companion object { private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher { val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext) |