diff options
121 files changed, 2264 insertions, 651 deletions
diff --git a/core/java/Android.bp b/core/java/Android.bp index 980d9737aba7..7123ee7b5777 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -17,19 +17,19 @@ aidl_library { filegroup { name: "framework-core-sources", srcs: [ - "**/*.java", "**/*.aidl", - ":systemfeatures-gen-srcs", + "**/*.java", ":framework-nfc-non-updatable-sources", ":messagequeue-gen", ":ranging_stack_mock_initializer", + ":systemfeatures-gen-srcs", ], // Exactly one MessageQueue.java will be added to srcs by messagequeue-gen exclude_srcs: [ + "**/*_ravenwood.java", + ":dynamic_instrumentation_manager_aidl_sources", "android/os/*MessageQueue/**/*.java", "android/ranging/**/*.java", - ":dynamic_instrumentation_manager_aidl_sources", - "**/*_ravenwood.java", ], visibility: ["//frameworks/base"], } @@ -52,9 +52,9 @@ soong_config_module_type { "release_ranging_stack", ], properties: [ - "srcs", "cmd", "out", + "srcs", ], } @@ -153,9 +153,9 @@ filegroup { filegroup { name: "framework-core-sources-for-test-mock", srcs: [ + "android/accounts/AccountManager.java", "android/accounts/AccountManagerCallback.java", "android/accounts/AccountManagerFuture.java", - "android/accounts/AccountManager.java", "android/accounts/AccountsException.java", "android/accounts/AuthenticatorException.java", "android/accounts/OperationCanceledException.java", @@ -163,8 +163,8 @@ filegroup { "android/app/IApplicationThread.aidl", "android/app/IServiceConnection.aidl", "android/app/PackageDeleteObserver.java", - "android/content/ComponentCallbacks2.java", "android/content/ComponentCallbacks.java", + "android/content/ComponentCallbacks2.java", "android/content/ContentInterface.java", "android/content/ContentProvider.java", "android/content/ContentProviderNative.java", @@ -178,8 +178,8 @@ filegroup { "android/content/OperationApplicationException.java", "android/content/pm/ActivityInfo.java", "android/content/pm/ApplicationInfo.java", - "android/content/pm/InstantAppInfo.java", "android/content/pm/IPackageDataObserver.aidl", + "android/content/pm/InstantAppInfo.java", "android/content/pm/KeySet.java", "android/content/pm/PackageManager.java", "android/content/pm/VerifierDeviceIdentity.java", @@ -192,8 +192,8 @@ filegroup { "android/os/Bundle.java", "android/os/IBinder.java", "android/os/IInterface.java", - "android/os/Parcelable.java", "android/os/ParcelFileDescriptor.java", + "android/os/Parcelable.java", "android/os/RemoteException.java", "android/os/storage/VolumeInfo.java", "android/util/AndroidException.java", @@ -215,24 +215,24 @@ filegroup { filegroup { name: "libvibrator_aidl", srcs: [ + "android/os/ExternalVibrationScale.aidl", "android/os/IExternalVibrationController.aidl", "android/os/IExternalVibratorService.aidl", - "android/os/ExternalVibrationScale.aidl", ], } filegroup { name: "libpowermanager_aidl", srcs: [ - "android/os/Temperature.aidl", "android/os/CoolingDevice.aidl", + "android/os/IPowerManager.aidl", + "android/os/IScreenTimeoutPolicyListener.aidl", "android/os/IThermalEventListener.aidl", "android/os/IThermalHeadroomListener.aidl", - "android/os/IThermalStatusListener.aidl", "android/os/IThermalService.aidl", - "android/os/IPowerManager.aidl", + "android/os/IThermalStatusListener.aidl", "android/os/IWakeLockCallback.aidl", - "android/os/IScreenTimeoutPolicyListener.aidl", + "android/os/Temperature.aidl", ], } @@ -282,8 +282,8 @@ genrule { java_library { name: "uieventloggerlib", srcs: [ - "com/android/internal/logging/UiEventLoggerImpl.java", ":statslog-framework-java-gen", + "com/android/internal/logging/UiEventLoggerImpl.java", ], libs: [ "androidx.annotation_annotation", @@ -353,11 +353,11 @@ filegroup { name: "framework-permission-s-shared-srcs", srcs: [ ":modules-utils-preconditions-srcs", - "com/android/internal/infra/AndroidFuture.java", - "com/android/internal/infra/ServiceConnector.java", + "android/os/HandlerExecutor.java", "com/android/internal/infra/AndroidFuture.aidl", + "com/android/internal/infra/AndroidFuture.java", "com/android/internal/infra/IAndroidFuture.aidl", - "android/os/HandlerExecutor.java", + "com/android/internal/infra/ServiceConnector.java", ], } @@ -393,10 +393,10 @@ filegroup { "android/content/pm/FileSystemControlParcel.aidl", "android/content/pm/IDataLoader.aidl", "android/content/pm/IDataLoaderManager.aidl", - "android/content/pm/InstallationFileParcel.aidl", - "android/content/pm/InstallationFileLocation.aidl", "android/content/pm/IDataLoaderStatusListener.aidl", "android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl", + "android/content/pm/InstallationFileLocation.aidl", + "android/content/pm/InstallationFileParcel.aidl", ], } @@ -404,9 +404,9 @@ filegroup { name: "incremental_manager_aidl", srcs: [ "android/os/incremental/IIncrementalService.aidl", + "android/os/incremental/IStorageHealthListener.aidl", "android/os/incremental/IStorageLoadingProgressListener.aidl", "android/os/incremental/IncrementalNewFileParams.aidl", - "android/os/incremental/IStorageHealthListener.aidl", "android/os/incremental/PerUidReadTimeouts.aidl", "android/os/incremental/StorageHealthCheckParams.aidl", ], @@ -422,15 +422,15 @@ filegroup { filegroup { name: "hwbinder-stubs-srcs", srcs: [ - "android/os/HidlSupport.java", + "android/os/DeadObjectException.java", + "android/os/DeadSystemException.java", "android/os/HidlMemory.java", + "android/os/HidlSupport.java", "android/os/HwBinder.java", "android/os/HwBlob.java", "android/os/HwParcel.java", "android/os/IHwBinder.java", "android/os/IHwInterface.java", - "android/os/DeadObjectException.java", - "android/os/DeadSystemException.java", "android/os/NativeHandle.java", "android/os/RemoteException.java", "android/util/AndroidException.java", @@ -444,12 +444,12 @@ filegroup { cc_defaults { name: "incremental_default", cflags: [ + "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", "-Wall", + "-Werror", "-Wextra", "-Wextra-semi", - "-Werror", "-Wzero-as-null-pointer-constant", - "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION", ], shared_libs: [ "libbinder", @@ -489,8 +489,8 @@ cc_library { ], defaults: ["incremental_default"], shared_libs: [ - "libincremental_aidl-cpp", "libdataloader_aidl-cpp", + "libincremental_aidl-cpp", ], } @@ -580,8 +580,8 @@ filegroup { filegroup { name: "framework-telephony-common-shared-srcs", srcs: [ - "android/os/RegistrantList.java", "android/os/Registrant.java", + "android/os/RegistrantList.java", "android/util/IndentingPrintWriter.java", "android/util/LocalLog.java", "android/util/TimeUtils.java", @@ -599,8 +599,8 @@ filegroup { name: "framework-ims-common-shared-srcs", srcs: [ ":modules-utils-preconditions-srcs", - "android/os/RegistrantList.java", "android/os/Registrant.java", + "android/os/RegistrantList.java", "com/android/internal/os/SomeArgs.java", ], } @@ -609,9 +609,9 @@ filegroup { filegroup { name: "framework-core-sources-for-fuzzers", srcs: [ - "android/os/IInterface.java", "android/os/Binder.java", "android/os/IBinder.java", + "android/os/IInterface.java", "android/os/Parcelable.java", ], } @@ -657,6 +657,17 @@ filegroup { } java_library { + name: "protolog-common-lib", + srcs: [ + ":protolog-common-src", + ], + host_supported: true, + static_libs: [ + "framework-annotations-lib", + ], +} + +java_library { name: "protolog-group", srcs: [ "com/android/internal/protolog/common/IProtoLogGroup.java", @@ -694,7 +705,9 @@ java_library { name: "protolog-groups", srcs: [ "com/android/internal/protolog/WmProtoLogGroups.java", - ":protolog-common-src", + ], + static_libs: [ + "protolog-common-lib", ], } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 01b2953362b5..6c6709bb2b47 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -369,6 +369,11 @@ interface IActivityTaskManager { in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor); /** + * Registers a callback to be invoked when the system server requests a back gesture. + */ + void registerBackGestureDelegate(in RemoteCallback monitor); + + /** * registers a callback to be invoked when a background activity launch is aborted. * * @param observer callback to be registered. diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java index c3c8c3d63bec..aae09e6f4c01 100644 --- a/core/java/android/hardware/display/DisplayTopology.java +++ b/core/java/android/hardware/display/DisplayTopology.java @@ -169,7 +169,23 @@ public final class DisplayTopology implements Parcelable { * @hide */ public void addDisplay(int displayId, float width, float height) { - addDisplay(displayId, width, height, /* shouldLog= */ true); + if (findDisplay(displayId, mRoot) != null) { + return; + } + if (mRoot == null) { + mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0); + mPrimaryDisplayId = displayId; + } else if (mRoot.mChildren.isEmpty()) { + // This is the 2nd display. Align the middles of the top and bottom edges. + float offset = mRoot.mWidth / 2 - width / 2; + TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset); + mRoot.mChildren.add(display); + } else { + TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first; + TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT, + /* offset= */ 0); + rightMostDisplay.mChildren.add(newDisplay); + } } /** @@ -216,7 +232,7 @@ public final class DisplayTopology implements Parcelable { while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node.mDisplayId != displayId) { - addDisplay(node.mDisplayId, node.mWidth, node.mHeight, /* shouldLog= */ false); + addDisplay(node.mDisplayId, node.mWidth, node.mHeight); } queue.addAll(node.mChildren); } @@ -227,10 +243,6 @@ public final class DisplayTopology implements Parcelable { } else { mPrimaryDisplayId = Display.INVALID_DISPLAY; } - Slog.i(TAG, "Primary display with ID " + displayId - + " removed, new primary display: " + mPrimaryDisplayId); - } else { - Slog.i(TAG, "Display with ID " + displayId + " removed"); } return true; } @@ -598,37 +610,6 @@ public final class DisplayTopology implements Parcelable { return out.toString(); } - private void addDisplay(int displayId, float width, float height, boolean shouldLog) { - if (findDisplay(displayId, mRoot) != null) { - return; - } - if (mRoot == null) { - mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0); - mPrimaryDisplayId = displayId; - if (shouldLog) { - Slog.i(TAG, "First display added: " + mRoot); - } - } else if (mRoot.mChildren.isEmpty()) { - // This is the 2nd display. Align the middles of the top and bottom edges. - float offset = mRoot.mWidth / 2 - width / 2; - TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset); - mRoot.mChildren.add(display); - if (shouldLog) { - Slog.i(TAG, "Second display added: " + display + ", parent ID: " - + mRoot.mDisplayId); - } - } else { - TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first; - TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT, - /* offset= */ 0); - rightMostDisplay.mChildren.add(newDisplay); - if (shouldLog) { - Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: " - + rightMostDisplay.mDisplayId); - } - } - } - /** * @param display The display from which the search should start. * @param xPos The x position of the right edge of that display. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b7e296228e45..e7bca1419418 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13317,10 +13317,18 @@ public final class Settings { public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package"; /** - * Inetger property which determines whether advanced protection is on or not. + * Integer property which determines whether advanced protection is on or not. * @hide */ public static final String ADVANCED_PROTECTION_MODE = "advanced_protection_mode"; + + /** + * Integer property which determines whether advanced protection USB data protection + * feature is on or not. + * + * @hide + */ + public static final String AAPM_USB_DATA_PROTECTION = "aapm_usb_data_protection"; } /** diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index c4347f05f4a3..c7ae3283c46c 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -28,6 +28,7 @@ import static android.view.SurfaceControlProto.NAME; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.DurationNanosLong; import android.annotation.FlaggedApi; import android.annotation.FloatRange; import android.annotation.IntDef; @@ -469,9 +470,9 @@ public final class SurfaceControl implements Parcelable { private final long mFrameVsyncId; private final @JankType int mJankType; - private final long mFrameIntervalNs; - private final long mScheduledAppFrameTimeNs; - private final long mActualAppFrameTimeNs; + private final @DurationNanosLong long mFrameIntervalNs; + private final @DurationNanosLong long mScheduledAppFrameTimeNs; + private final @DurationNanosLong long mActualAppFrameTimeNs; /** * @hide @@ -512,7 +513,7 @@ public final class SurfaceControl implements Parcelable { * @return the frame interval in ns * @hide */ - public long getFrameIntervalNanos() { + public @DurationNanosLong long getFrameIntervalNanos() { return mFrameIntervalNs; } @@ -525,7 +526,7 @@ public final class SurfaceControl implements Parcelable { * * @return scheduled app time in ns */ - public long getScheduledAppFrameTimeNanos() { + public @DurationNanosLong long getScheduledAppFrameTimeNanos() { return mScheduledAppFrameTimeNs; } @@ -534,7 +535,7 @@ public final class SurfaceControl implements Parcelable { * * @return the actual app time in ns */ - public long getActualAppFrameTimeNanos() { + public @DurationNanosLong long getActualAppFrameTimeNanos() { return mActualAppFrameTimeNs; } diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java index 5e8ce5ee557f..04e6c68859c9 100644 --- a/core/java/android/window/DesktopExperienceFlags.java +++ b/core/java/android/window/DesktopExperienceFlags.java @@ -64,6 +64,7 @@ public enum DesktopExperienceFlags { ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false), ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false), ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true), + ENABLE_MULTIDISPLAY_TRACKPAD_BACK_GESTURE(Flags::enableMultidisplayTrackpadBackGesture, false), ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false), ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false), ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS( diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java index 53c64bd6e664..4ddd73319840 100644 --- a/core/java/android/window/TaskSnapshot.java +++ b/core/java/android/window/TaskSnapshot.java @@ -37,6 +37,7 @@ import com.android.window.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.function.Consumer; /** * Represents a task snapshot. @@ -76,6 +77,7 @@ public class TaskSnapshot implements Parcelable { // Must be one of the named color spaces, otherwise, always use SRGB color space. private final ColorSpace mColorSpace; private int mInternalReferences; + private Consumer<HardwareBuffer> mSafeSnapshotReleaser; /** Keep in cache, doesn't need reference. */ public static final int REFERENCE_NONE = 0; @@ -365,8 +367,24 @@ public class TaskSnapshot implements Parcelable { mInternalReferences &= ~usage; if (Flags.releaseSnapshotAggressively() && mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) { - mSnapshot.close(); + if (mSafeSnapshotReleaser != null) { + mSafeSnapshotReleaser.accept(mSnapshot); + } else { + mSnapshot.close(); + } + } + } + + /** + * Register a safe release callback, instead of immediately closing the hardware buffer when + * no more reference, to let the system server decide when to close it. + * Only used in core. + */ + public synchronized void setSafeRelease(Consumer<HardwareBuffer> releaser) { + if (!Flags.safeReleaseSnapshotAggressively()) { + return; } + mSafeSnapshotReleaser = releaser; } public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() { diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index 8dd0457248a4..59dd32258d8c 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -516,6 +516,17 @@ flag { } flag { + name: "safe_release_snapshot_aggressively" + namespace: "windowing_frontend" + description: "Protect task snapshot memory from premature release, which can occur when a local variable holds a reference while the snapshot is removed from the cache." + bug: "238206323" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "scramble_snapshot_file_name" namespace: "windowing_frontend" description: "Scramble the file name of task snapshot." @@ -524,4 +535,22 @@ flag { metadata { purpose: PURPOSE_BUGFIX } +} + +flag { + name: "delegate_back_gesture_to_shell" + namespace: "windowing_frontend" + description: "Delegate back gesture event to back animation controller." + bug: "394599430" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "enable_multidisplay_trackpad_back_gesture" + namespace: "lse_desktop_experience" + description: "Adds support for trackpad back gestures on connected displays" + bug: "382774299" }
\ No newline at end of file diff --git a/data/etc/Android.bp b/data/etc/Android.bp index 8f85617acae3..98278f4529ff 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -184,6 +184,14 @@ prebuilt_etc { } prebuilt_etc { + name: "privapp_whitelist_com.android.statementservice", + system_ext_specific: true, + sub_dir: "permissions", + src: "com.android.statementservice.xml", + filename_from_src: true, +} + +prebuilt_etc { name: "privapp_whitelist_com.android.settings.intelligence", product_specific: true, sub_dir: "permissions", diff --git a/data/etc/com.android.statementservice.xml b/data/etc/com.android.statementservice.xml new file mode 100644 index 000000000000..e102af206395 --- /dev/null +++ b/data/etc/com.android.statementservice.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2018 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 + --> +<permissions> + <privapp-permissions package="com.android.statementservice"> + <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/> + <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/> + <permission name="android.permission.INTERACT_ACROSS_USERS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 9234902335c1..1dd0465f691e 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -623,12 +623,6 @@ applications that come with the platform <permission name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"/> </privapp-permissions> - <privapp-permissions package="com.android.statementservice"> - <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/> - <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - </privapp-permissions> - <privapp-permissions package="com.android.soundpicker"> <permission name="android.permission.INTERACT_ACROSS_USERS" /> </privapp-permissions> diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index bcb6c4f555f7..033c934056d6 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -26,9 +26,11 @@ package { java_library { name: "wm_shell_protolog-groups", srcs: [ - ":protolog-common-src", "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java", ], + static_libs: [ + "protolog-common-lib", + ], } filegroup { @@ -159,12 +161,12 @@ java_library { android_library { name: "WindowManager-Shell", srcs: [ - ":wm_shell_protolog_src", // TODO(b/168581922) protologtool do not support kotlin(*.kt) - "src/com/android/wm/shell/EventLogTags.logtags", ":wm_shell-aidls", ":wm_shell-shared-aidls", ":wm_shell-sources-kt", + ":wm_shell_protolog_src", + "src/com/android/wm/shell/EventLogTags.logtags", ], resource_dirs: [ "res", diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index e5a4cd034e72..a9224b02ad31 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -451,6 +451,6 @@ public class DesktopModeStatus { pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1)); pw.print(innerPrefix); pw.print("showAppHandle config override="); - pw.print(overridesShowAppHandle(context)); + pw.println(overridesShowAppHandle(context)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 81cf031994a2..746632f67725 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -286,6 +286,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont this::createExternalInterface, this); mShellCommandHandler.addDumpCallback(this::dump, this); mShellController.addConfigurationChangeListener(this); + registerBackGestureDelegate(); } public BackAnimation getBackAnimationImpl() { @@ -1144,6 +1145,32 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mBackAnimationAdapter = new BackAnimationAdapter(runner); } + private void registerBackGestureDelegate() { + if (!Flags.delegateBackGestureToShell()) { + return; + } + final RemoteCallback requestBackMonitor = new RemoteCallback( + new RemoteCallback.OnResultListener() { + @Override + public void onResult(@Nullable Bundle result) { + mShellExecutor.execute(() -> { + if (mBackGestureStarted) { + Log.w(TAG, "Back gesture is running, ignore request"); + return; + } + onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE); + setTriggerBack(true); + onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE); + }); + } + }); + try { + mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor); + } catch (RemoteException remoteException) { + Log.w(TAG, "Failed register back gesture request ", remoteException); + } + } + /** * Description of current BackAnimationController state. */ diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp index 9aacdcb9ca92..ed645826234d 100644 --- a/libs/androidfw/LocaleDataLookup.cpp +++ b/libs/androidfw/LocaleDataLookup.cpp @@ -5774,7 +5774,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) { case 0xD2120000u: // squ -> Latn case 0x73724D45u: // sr-ME -> Latn case 0x7372524Fu: // sr-RO -> Latn - case 0x73725255u: // sr-RU -> Latn case 0x73725452u: // sr-TR -> Latn case 0x82320000u: // sra -> Latn case 0x92320000u: // sre -> Latn @@ -7265,7 +7264,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) { return SCRIPT_CODES[73u]; case 0x8ACD0000u: // nwc -> Newa return SCRIPT_CODES[74u]; - case 0xB40C474Eu: // man-GN -> Nkoo case 0xBA0D0000u: // nqo -> Nkoo return SCRIPT_CODES[75u]; case 0xDCF90000u: // zhx -> Nshu diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig index c9ec31bab048..7221f1ddeb7f 100644 --- a/media/java/android/media/flags/media_better_together.aconfig +++ b/media/java/android/media/flags/media_better_together.aconfig @@ -259,3 +259,10 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "enable_output_switcher_personal_audio_sharing" + namespace: "cross_device_experiences" + description: "Enables personal audio sharing in the output switcher." + bug: "385672684" +}
\ No newline at end of file diff --git a/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm b/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm index 6c947c77ad3d..455fb83474b6 100644 --- a/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm +++ b/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm @@ -37,8 +37,8 @@ key 0 { key 1 { label: '1' base: '1' - shift: '!' - ralt: '\u0303' + shift: '\'' + ralt: '\u007e' } key 2 { @@ -80,7 +80,7 @@ key 7 { label: '7' base: '7' shift: '=' - ralt: '\u0300' + ralt: '`' } key 8 { @@ -374,6 +374,7 @@ key M { base: 'm' shift, capslock: 'M' shift+capslock: 'm' + ralt: '<' } key COMMA { @@ -387,6 +388,7 @@ key PERIOD { label: '.' base: '.' shift: ':' + ralt: '>' } key MINUS { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java index bcc737a351a9..d05aaaa6e389 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java @@ -52,8 +52,11 @@ import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Objects; +import java.util.stream.Stream; /** * This is a utility class for defining some utility methods and constants @@ -69,7 +72,8 @@ public class PackageUtil { //intent attribute strings related to uninstall public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName"; private static final String DOWNLOADS_AUTHORITY = "downloads"; - private static final String SPLIT_BASE_APK_END_WITH = "base.apk"; + private static final String SPLIT_BASE_APK_SUFFIX = "base.apk"; + private static final String SPLIT_APK_SUFFIX = ".apk"; /** * Utility method to get package information for a given {@link File} @@ -77,11 +81,20 @@ public class PackageUtil { @Nullable public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) { String filePath = sourceFile.getAbsolutePath(); - if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) { + if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) { File dir = sourceFile.getParentFile(); - if (dir.listFiles().length > 1) { - // split apks, use file directory to get archive info - filePath = dir.getPath(); + try (Stream<Path> list = Files.list(dir.toPath())) { + long count = list + .filter((name) -> name.endsWith(SPLIT_APK_SUFFIX)) + .limit(2) + .count(); + if (count > 1) { + // split apks, use file directory to get archive info + filePath = dir.getPath(); + } + } catch (Exception ignored) { + // No access to the parent directory, proceed to read app snippet + // from the base apk only } } try { @@ -240,9 +253,10 @@ public class PackageUtil { appInfo.publicSourceDir = archiveFilePath; if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) { - final File[] files = sourceFile.getParentFile().listFiles(); + final File[] files = sourceFile.getParentFile().listFiles( + (dir, name) -> name.endsWith(SPLIT_APK_SUFFIX)); final String[] splits = Arrays.stream(appInfo.splitNames) - .map(i -> findFilePath(files, i + ".apk")) + .map(i -> findFilePath(files, i + SPLIT_APK_SUFFIX)) .filter(Objects::nonNull) .toArray(String[]::new); @@ -283,7 +297,9 @@ public class PackageUtil { } private static String findFilePath(File[] files, String postfix) { - for (File file : files) { + final int length = files != null ? files.length : 0; + for (int i = 0; i < length; i++) { + File file = files[i]; final String path = file.getAbsolutePath(); if (path.endsWith(postfix)) { return path; diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt index e8477ef261a8..b84b903ac1cb 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt +++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt @@ -41,6 +41,8 @@ import android.util.Log import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet import java.io.ByteArrayOutputStream import java.io.File +import java.nio.file.Files +import java.nio.file.Path import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize @@ -48,6 +50,7 @@ object PackageUtil { private val LOG_TAG = InstallRepository::class.java.simpleName private const val DOWNLOADS_AUTHORITY = "downloads" private const val SPLIT_BASE_APK_SUFFIX = "base.apk" + private const val SPLIT_APK_SUFFIX = ".apk" const val localLogv = false const val ARGS_ABORT_REASON: String = "abort_reason" @@ -440,9 +443,20 @@ object PackageUtil { var filePath = sourceFile.absolutePath if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) { val dir = sourceFile.parentFile - if ((dir?.listFiles()?.size ?: 0) > 1) { - // split apks, use file directory to get archive info - filePath = dir.path + try { + Files.list(dir.toPath()).use { list -> + val count: Long = list + .filter { name: Path -> name.endsWith(SPLIT_APK_SUFFIX) } + .limit(2) + .count() + if (count > 1) { + // split apks, use file directory to get archive info + filePath = dir.path + } + } + } catch (ignored: Exception) { + // No access to the parent directory, proceed to read app snippet + // from the base apk only } } return try { diff --git a/packages/SettingsLib/ValuePreference/Android.bp b/packages/SettingsLib/ValuePreference/Android.bp new file mode 100644 index 000000000000..1cdcd2247eaf --- /dev/null +++ b/packages/SettingsLib/ValuePreference/Android.bp @@ -0,0 +1,31 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "SettingsLibValuePreference", + use_resource_processor: true, + defaults: [ + "SettingsLintDefaults", + ], + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.annotation_annotation", + "androidx.preference_preference", + "SettingsLibSettingsTheme", + ], + + sdk_version: "system_current", + min_sdk_version: "23", + apex_available: [ + "//apex_available:platform", + ], +} diff --git a/packages/SettingsLib/ValuePreference/AndroidManifest.xml b/packages/SettingsLib/ValuePreference/AndroidManifest.xml new file mode 100644 index 000000000000..217282be9c80 --- /dev/null +++ b/packages/SettingsLib/ValuePreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2025 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget.preference.value"> + + <uses-sdk android:minSdkVersion="21"/> + +</manifest> diff --git a/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml new file mode 100644 index 000000000000..4fd3ab10fb16 --- /dev/null +++ b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2025 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="72dp" + android:gravity="center_vertical" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:background="?android:attr/selectableItemBackground" + android:clipToPadding="false" + android:baselineAligned="false" + android:filterTouchesWhenObscured="false"> + + <include layout="@layout/settingslib_expressive_preference_icon_frame"/> + + <include layout="@layout/settingslib_expressive_value_preference_text_frame"/> + + <!-- Preference should place its actual preference widget here. --> + <LinearLayout + android:id="@android:id/widget_frame" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="end|center_vertical" + android:paddingStart="@dimen/settingslib_expressive_space_small1" + android:paddingEnd="0dp" + android:orientation="vertical"/> + +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml new file mode 100644 index 000000000000..a385ee521a74 --- /dev/null +++ b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2025 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. + --> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/settingslib_expressive_space_none" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingVertical="@dimen/settingslib_expressive_space_small1" + android:paddingStart="@dimen/settingslib_expressive_space_none" + android:paddingEnd="@dimen/settingslib_expressive_space_small1" + android:filterTouchesWhenObscured="false"> + + <TextView + android:id="@android:id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="start" + android:textAlignment="viewStart" + android:maxLines="2" + android:textAppearance="@style/TextAppearance.SettingsLib.ValuePreferenceTitle" + android:ellipsize="marquee"/> + + <TextView + android:id="@android:id/summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@android:id/title" + android:layout_alignStart="@android:id/title" + android:layout_gravity="start" + android:textAlignment="viewStart" + android:textAppearance="?android:attr/textAppearanceListItemSecondary" + android:textColor="?android:attr/textColorSecondary" + android:maxLines="10"/> +</RelativeLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/ValuePreference/res/values/styles.xml b/packages/SettingsLib/ValuePreference/res/values/styles.xml new file mode 100644 index 000000000000..284819498f64 --- /dev/null +++ b/packages/SettingsLib/ValuePreference/res/values/styles.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2025 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. + --> + +<resources xmlns:tools="http://schemas.android.com/tools"> + <style name="TextAppearance.SettingsLib.ValuePreferenceTitle" + parent="@style/TextAppearance.SettingsLib.DisplaySmall"> + <item name="android:textColor">@color/settingslib_text_color_primary</item> + </style> +</resources> diff --git a/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt b/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt new file mode 100644 index 000000000000..475638c8b406 --- /dev/null +++ b/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 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.settingslib.widget + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder +import com.android.settingslib.widget.preference.value.R + +/** The BulletPreference shows a text which describe a feature. */ +class ValuePreference +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, + defStyleRes: Int = 0 +) : Preference(context, attrs, defStyleAttr, defStyleRes) { + + init { + layoutResource = R.layout.settingslib_expressive_value_preference + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + holder.isDividerAllowedAbove = false + holder.isDividerAllowedBelow = false + } +} diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 1297aa3ff7d5..6a7e048b577a 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -288,7 +288,7 @@ <string name="bluetooth_profile_hid">Input device</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan">Internet access</string> - <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] --> + <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=80] --> <string name="bluetooth_profile_pbap">Allow access to contacts and call history</string> <!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] --> <string name="bluetooth_profile_pbap_summary">Info will be used for call announcements and more</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java index 155c7e6530aa..e46574cef917 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java @@ -37,9 +37,11 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.android.settingslib.R; +import com.android.settingslib.flags.Flags; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; public class LeAudioProfile implements LocalBluetoothProfile { @@ -59,6 +61,10 @@ public class LeAudioProfile implements LocalBluetoothProfile { // Order of this profile in device profiles list private static final int ORDINAL = 1; + // Cached callbacks being registered before service is connected. + private ConcurrentHashMap<BluetoothLeAudio.Callback, Executor> + mCachedCallbackExecutorMap = new ConcurrentHashMap<>(); + // These callbacks run on the main thread. private final class LeAudioServiceListener implements BluetoothProfile.ServiceListener { @@ -88,7 +94,19 @@ public class LeAudioProfile implements LocalBluetoothProfile { // Check current list of CachedDevices to see if any are hearing aid devices. mDeviceManager.updateHearingAidsDevices(); mProfileManager.callServiceConnectedListeners(); - mIsProfileReady = true; + if (!mIsProfileReady) { + mIsProfileReady = true; + if (Flags.adoptPrimaryGroupManagementApiV2()) { + if (DEBUG) { + Log.d( + TAG, + "onServiceConnected, register mCachedCallbackExecutorMap = " + + mCachedCallbackExecutorMap); + } + mCachedCallbackExecutorMap.forEach( + (callback, executor) -> registerCallback(executor, callback)); + } + } } public void onServiceDisconnected(int profile) { @@ -96,7 +114,12 @@ public class LeAudioProfile implements LocalBluetoothProfile { Log.d(TAG, "Bluetooth service disconnected"); } mProfileManager.callServiceDisconnectedListeners(); - mIsProfileReady = false; + if (mIsProfileReady) { + mIsProfileReady = false; + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.clear(); + } + } } } @@ -367,6 +390,9 @@ public class LeAudioProfile implements LocalBluetoothProfile { @NonNull BluetoothLeAudio.Callback callback) { if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot register callback."); + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.putIfAbsent(callback, executor); + } return; } mService.registerCallback(executor, callback); @@ -384,6 +410,9 @@ public class LeAudioProfile implements LocalBluetoothProfile { * callback is registered */ public void unregisterCallback(@NonNull BluetoothLeAudio.Callback callback) { + if (Flags.adoptPrimaryGroupManagementApiV2()) { + mCachedCallbackExecutorMap.remove(callback); + } if (mService == null) { Log.w(TAG, "Proxy not attached to service. Cannot unregister callback."); return; diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index d0f84627f8d8..463e94bfa0d6 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -464,6 +464,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED, new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.AAPM_USB_DATA_PROTECTION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_APP_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.FACE_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 7179cbdf93fb..e9b6c73cb800 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -642,6 +642,7 @@ public class SettingsBackupTest { private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS = newHashSet( + Settings.Secure.AAPM_USB_DATA_PROTECTION, Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O. Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS, diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp index 39b0302beff8..3fc9aabb7edb 100644 --- a/packages/StatementService/Android.bp +++ b/packages/StatementService/Android.bp @@ -32,7 +32,11 @@ android_app { }, target_sdk_version: "29", platform_apis: true, + system_ext_specific: true, privileged: true, + required: [ + "privapp_whitelist_com.android.statementservice", + ], certificate: "platform", static_libs: [ "StatementServiceParser", diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index c6bc1c70ad18..3cb30258fcb1 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -155,13 +155,3 @@ flag { purpose: PURPOSE_BUGFIX } } - -flag { - name: "hearing_devices_input_routing_ui_improvement" - namespace: "accessibility" - description: "UI improvement for hearing device input routing feature" - bug: "397314200" - metadata { - purpose: PURPOSE_BUGFIX - } -} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt index fdb07bdbe7f3..576ff61c4f94 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt @@ -83,7 +83,7 @@ constructor( ViewTransitionRegistry.instance } else { null - } + }, ) : ActivityTransitionAnimator.Controller { override val isLaunching: Boolean = true @@ -313,9 +313,17 @@ constructor( // visibility that is saved by `setShouldBlockVisibilityChanges()` for a later restoration. (ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true) - // Create a ghost of the view that will be moving and fading out. This allows to fade out - // the content before fading out the background. - ghostView = GhostView.addGhost(ghostedView, transitionContainer) + try { + // Create a ghost of the view that will be moving and fading out. This allows to fade + // out the content before fading out the background. + ghostView = GhostView.addGhost(ghostedView, transitionContainer) + } catch (e: Exception) { + // It is not 100% clear what conditions cause this exception to happen in practice, and + // we could never reproduce it, but it does show up extremely rarely. We already handle + // the scenario where ghostView is null, so we just avoid crashing and log the error. + // See b/315858472 for an investigation of the issue. + Log.e(TAG, "Failed to create ghostView", e) + } // [GhostView.addGhost], the result of which is our [ghostView], creates a [GhostView], and // adds it first to a [FrameLayout] container. It then adds _that_ container to an diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java index a58f7f72f08a..ae5cd0196e72 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java @@ -41,6 +41,7 @@ import android.app.NotificationChannel; import android.os.Handler; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; @@ -101,52 +102,53 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase { switchFlag("false"); // test flag disables logic with default values assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking())); assertNull(mAssistantFeedbackController.getFeedbackIcon( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking())); // test that the flag disables logic with values that otherwise would return a value assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking())); assertNull(mAssistantFeedbackController.getFeedbackIcon( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking())); } @Test public void testFeedback_noChange() { switchFlag("true"); assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking())); assertNull(mAssistantFeedbackController.getFeedbackIcon( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking())); } @Test public void testFeedback_changedImportance() { switchFlag("true"); - NotificationEntry entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED); - assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry)); - assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry)); - - entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_LOW, RANKING_UNCHANGED); - assertEquals(STATUS_SILENCED, mAssistantFeedbackController.getFeedbackStatus(entry)); - assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry)); - - entry = getEntry(IMPORTANCE_LOW, IMPORTANCE_MIN, RANKING_UNCHANGED); - assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry)); - assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry)); + NotificationListenerService.Ranking ranking = + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED).getRanking(); + assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking)); + assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking)); + + ranking = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_LOW, RANKING_UNCHANGED).getRanking(); + assertEquals(STATUS_SILENCED, mAssistantFeedbackController.getFeedbackStatus(ranking)); + assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking)); + + ranking = getEntry(IMPORTANCE_LOW, IMPORTANCE_MIN, RANKING_UNCHANGED).getRanking(); + assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking)); + assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking)); } @Test public void testFeedback_changedRanking() { switchFlag("true"); - NotificationEntry entry = - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED); - assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry)); - assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry)); - - entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_DEMOTED); - assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry)); - assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry)); + NotificationListenerService.Ranking ranking = + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED).getRanking(); + assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking)); + assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking)); + + ranking = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_DEMOTED).getRanking(); + assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking)); + assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking)); } @Test @@ -155,7 +157,7 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase { FeedbackIcon expected = new FeedbackIcon(com.android.internal.R.drawable.ic_feedback_uprank, com.android.internal.R.string.notification_feedback_indicator_promoted); assertEquals(expected, mAssistantFeedbackController.getFeedbackIcon( - getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED))); + getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking())); } private NotificationEntry getEntry(int oldImportance, int newImportance, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt index 101874807474..52f68bf4d729 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt @@ -25,7 +25,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON +import com.android.systemui.statusbar.notification.row.entryAdapterFactory import com.android.systemui.statusbar.notification.shared.NotificationBundleUi +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Rule @@ -36,13 +38,15 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @RunWithLooper class BundleEntryAdapterTest : SysuiTestCase() { + private val kosmos = testKosmos() private lateinit var underTest: BundleEntryAdapter @get:Rule val setFlagsRule = SetFlagsRule() + private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory @Before fun setUp() { - underTest = BundleEntryAdapter(BundleEntry("key")) + underTest = factory.create(BundleEntry("key")) as BundleEntryAdapter } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt index 7449064bc65b..02c6a484bd43 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt @@ -29,6 +29,9 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.res.R import com.android.systemui.statusbar.RankingBuilder +import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender +import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider import com.android.systemui.statusbar.notification.mockNotificationActivityStarter import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -41,9 +44,12 @@ import com.google.common.truth.Truth.assertThat import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.eq @SmallTest @RunWith(AndroidJUnit4::class) @@ -257,6 +263,109 @@ class NotificationEntryAdapterTest : SysuiTestCase() { @Test @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun getRanking() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + + underTest = factory.create(entry) as NotificationEntryAdapter + assertThat(underTest.ranking).isEqualTo(entry.ranking) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun endLifetimeExtension() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + val callback = + Mockito.mock(NotifLifetimeExtender.OnEndLifetimeExtensionCallback::class.java) + + underTest = factory.create(entry) as NotificationEntryAdapter + underTest.endLifetimeExtension(callback, Mockito.mock(NotifLifetimeExtender::class.java)) + verify(callback).onEndLifetimeExtension(any(), eq(entry)) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun onImportanceChanged() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + + underTest = factory.create(entry) as NotificationEntryAdapter + + underTest.onImportanceChanged() + verify(kosmos.mockVisualStabilityCoordinator) + .temporarilyAllowSectionChanges(eq(entry), anyLong()) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun markForUserTriggeredMovement() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + underTest = factory.create(entry) as NotificationEntryAdapter + + assertThat(underTest.isMarkedForUserTriggeredMovement) + .isEqualTo(entry.isMarkedForUserTriggeredMovement) + + underTest.markForUserTriggeredMovement() + assertThat(underTest.isMarkedForUserTriggeredMovement) + .isEqualTo(entry.isMarkedForUserTriggeredMovement) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun isHighPriority() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + underTest = factory.create(entry) as NotificationEntryAdapter + + underTest.isHighPriority + + verify(kosmos.mockHighPriorityProvider).isHighPriority(entry) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun isBlockable() { + val notification: Notification = + Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .addAction(Mockito.mock(Notification.Action::class.java)) + .build() + + val entry = NotificationEntryBuilder().setNotification(notification).build() + underTest = factory.create(entry) as NotificationEntryAdapter + + assertThat(underTest.isBlockable).isEqualTo(entry.isBlockable) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) fun canDragAndDrop() { val pi = mock(PendingIntent::class.java) Mockito.`when`(pi.isActivity).thenReturn(true) @@ -436,7 +545,6 @@ class NotificationEntryAdapterTest : SysuiTestCase() { underTest = factory.create(entry) as NotificationEntryAdapter - underTest.onEntryClicked(row) verify(kosmos.mockNotificationActivityStarter).onNotificationClicked(entry, row) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt index 7fe97d27f273..928b0014dc52 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt @@ -15,12 +15,15 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.log.logcatLogBuffer +import com.android.systemui.statusbar.notification.collection.EntryAdapter import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder @@ -30,6 +33,9 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager import com.android.systemui.statusbar.notification.row.NotificationGuts import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent +import com.android.systemui.statusbar.notification.row.entryAdapterFactory +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -45,12 +51,16 @@ import org.mockito.MockitoAnnotations.initMocks @RunWith(AndroidJUnit4::class) @RunWithLooper class GutsCoordinatorTest : SysuiTestCase() { + + private val kosmos = testKosmos() private lateinit var coordinator: GutsCoordinator private lateinit var notifLifetimeExtender: NotifLifetimeExtender private lateinit var notifGutsViewListener: NotifGutsViewListener private lateinit var entry1: NotificationEntry private lateinit var entry2: NotificationEntry + private lateinit var entryAdapter1: EntryAdapter + private lateinit var entryAdapter2: EntryAdapter @Mock private lateinit var notifGutsViewManager: NotifGutsViewManager @Mock private lateinit var pipeline: NotifPipeline @@ -73,12 +83,60 @@ class GutsCoordinatorTest : SysuiTestCase() { notifLifetimeExtender.setCallback(lifetimeExtenderCallback) entry1 = NotificationEntryBuilder().setId(1).build() entry2 = NotificationEntryBuilder().setId(2).build() + entryAdapter1 = kosmos.entryAdapterFactory.create(entry1) + entryAdapter2 = kosmos.entryAdapterFactory.create(entry2) whenever(notificationGuts.gutsContent).thenReturn(mock(GutsContent::class.java)) } @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) fun testSimpleLifetimeExtension() { assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() + notifGutsViewListener.onGutsClose(entryAdapter1) + verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun testDoubleOpenLifetimeExtension() { + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() + notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() + notifGutsViewListener.onGutsClose(entryAdapter1) + verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun testTwoEntryLifetimeExtension() { + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse() + notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse() + notifGutsViewListener.onGutsOpen(entryAdapter2, notificationGuts) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue() + notifGutsViewListener.onGutsClose(entryAdapter1) + verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue() + notifGutsViewListener.onGutsClose(entryAdapter2) + verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry2) + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse() + } + + @Test + @DisableFlags(NotificationBundleUi.FLAG_NAME) + fun testSimpleLifetimeExtension_entry() { + assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() notifGutsViewListener.onGutsOpen(entry1, notificationGuts) assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() notifGutsViewListener.onGutsClose(entry1) @@ -87,7 +145,8 @@ class GutsCoordinatorTest : SysuiTestCase() { } @Test - fun testDoubleOpenLifetimeExtension() { + @DisableFlags(NotificationBundleUi.FLAG_NAME) + fun testDoubleOpenLifetimeExtension_entry() { assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() notifGutsViewListener.onGutsOpen(entry1, notificationGuts) assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue() @@ -99,7 +158,8 @@ class GutsCoordinatorTest : SysuiTestCase() { } @Test - fun testTwoEntryLifetimeExtension() { + @DisableFlags(NotificationBundleUi.FLAG_NAME) + fun testTwoEntryLifetimeExtension_entry() { assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse() assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse() notifGutsViewListener.onGutsOpen(entry1, notificationGuts) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java index 0947cd5aaabb..e808deb1d5ad 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java @@ -44,6 +44,7 @@ import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.UserHandle; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.view.LayoutInflater; import android.view.View; @@ -132,7 +133,7 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextApplicationName() { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name); @@ -144,7 +145,7 @@ public class FeedbackInfoTest extends SysuiTestCase { final Drawable iconDrawable = mock(Drawable.class); when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon); @@ -153,9 +154,11 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPrompt_silenced() { - when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) + when(mAssistantFeedbackController.getFeedbackStatus( + any(NotificationListenerService.Ranking.class))) .thenReturn(STATUS_SILENCED); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically demoted to Silent by the system. " @@ -165,9 +168,11 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPrompt_promoted() { - when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) + when(mAssistantFeedbackController.getFeedbackStatus( + any(NotificationListenerService.Ranking.class))) .thenReturn(STATUS_PROMOTED); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked higher in your shade. " @@ -177,9 +182,11 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPrompt_alerted() { - when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) + when(mAssistantFeedbackController.getFeedbackStatus( + any(NotificationListenerService.Ranking.class))) .thenReturn(STATUS_ALERTED); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically promoted to Default by the system. " @@ -189,9 +196,11 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPrompt_demoted() { - when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class))) + when(mAssistantFeedbackController.getFeedbackStatus( + any(NotificationListenerService.Ranking.class))) .thenReturn(STATUS_DEMOTED); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); TextView prompt = mFeedbackInfo.findViewById(R.id.prompt); assertEquals("This notification was automatically ranked lower in your shade. " @@ -201,7 +210,8 @@ public class FeedbackInfoTest extends SysuiTestCase { @Test public void testPositiveFeedback() { - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View yes = mFeedbackInfo.findViewById(R.id.yes); @@ -218,7 +228,8 @@ public class FeedbackInfoTest extends SysuiTestCase { any(NotificationMenuRowPlugin.MenuItem.class))) .thenReturn(true); - mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow, + mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(), + mMockNotificationRow, mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager); final View no = mFeedbackInfo.findViewById(R.id.no); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt index 387c62d76083..324487b121bc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt @@ -67,15 +67,19 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter +import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider +import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.headsup.HeadsUpManager +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor import com.android.systemui.statusbar.notification.row.icon.AppIconProvider import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider import com.android.systemui.statusbar.notification.row.icon.appIconProvider import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.testKosmos @@ -263,7 +267,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong()) executor.runAllReady() verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>()) - verify(headsUpManager).setGutsShown(realRow.entry, true) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(true)) + } else { + verify(headsUpManager).setGutsShown(realRow.entry, true) + } assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong()) gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false) @@ -271,7 +279,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean()) verify(row, times(1)).setGutsView(any<MenuItem>()) executor.runAllReady() - verify(headsUpManager).setGutsShown(realRow.entry, false) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false)) + } else { + verify(headsUpManager).setGutsShown(realRow.entry, false) + } } @Test @@ -301,7 +313,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( verify(guts).closeControls(anyInt(), anyInt(), eq(false), eq(false)) verify(row, times(1)).setGutsView(any<MenuItem>()) executor.runAllReady() - verify(headsUpManager).setGutsShown(realRow.entry, false) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false)) + } else { + verify(headsUpManager).setGutsShown(realRow.entry, false) + } } @Test @@ -505,17 +521,22 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( @Throws(Exception::class) fun testInitializeNotificationInfoView_highPriority() { val notificationInfoView: NotificationInfo = mock() - val row = spy(helper.createRow()) + val row = createTestNotificationRow() val entry = row.entry NotificationEntryHelper.modifyRanking(entry) .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE) .setImportance(NotificationManager.IMPORTANCE_HIGH) .build() - whenever(row.canViewBeDismissed()).thenReturn(true) whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true) + whenever(kosmos.mockHighPriorityProvider.isHighPriority(entry)).thenReturn(true) val statusBarNotification = entry.sbn - gutsManager.initializeNotificationInfo(row, notificationInfoView) + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( @@ -527,15 +548,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( eq(channelEditorDialogController), any<PackageDemotionInteractor>(), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), any<UiEventLogger>(), /* isDeviceProvisioned = */ eq(false), /* isNonblockable = */ eq(false), - /* isDismissable = */ eq(true), + /* isDismissable = */ eq(false), /* wasShownHighPriority = */ eq(true), eq(assistantFeedbackController), eq(metricsLogger), @@ -547,17 +570,21 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( @Throws(Exception::class) fun testInitializeNotificationInfoView_PassesAlongProvisionedState() { val notificationInfoView: NotificationInfo = mock() - val row = spy(helper.createRow()) + val row = createTestNotificationRow() NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE) .build() - whenever(row.canViewBeDismissed()).thenReturn(true) val statusBarNotification = row.entry.sbn val entry = row.entry whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true) - gutsManager.initializeNotificationInfo(row, notificationInfoView) + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( @@ -569,15 +596,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( eq(channelEditorDialogController), any<PackageDemotionInteractor>(), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), any<UiEventLogger>(), /* isDeviceProvisioned = */ eq(true), /* isNonblockable = */ eq(false), - /* isDismissable = */ eq(true), + /* isDismissable = */ eq(false), /* wasShownHighPriority = */ eq(false), eq(assistantFeedbackController), eq(metricsLogger), @@ -589,15 +618,19 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( @Throws(Exception::class) fun testInitializeNotificationInfoView_withInitialAction() { val notificationInfoView: NotificationInfo = mock() - val row = spy(helper.createRow()) + val row = createTestNotificationRow() NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE) .build() - whenever(row.canViewBeDismissed()).thenReturn(true) val statusBarNotification = row.entry.sbn val entry = row.entry - gutsManager.initializeNotificationInfo(row, notificationInfoView) + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( @@ -609,15 +642,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( eq(channelEditorDialogController), any<PackageDemotionInteractor>(), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), any<UiEventLogger>(), /* isDeviceProvisioned = */ eq(false), /* isNonblockable = */ eq(false), - /* isDismissable = */ eq(true), + /* isDismissable = */ eq(false), /* wasShownHighPriority = */ eq(false), eq(assistantFeedbackController), eq(metricsLogger), @@ -639,6 +674,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase( NotificationEntryHelper.modifyRanking(row.entry) .setChannel(testNotificationChannel) .build() + row.entryAdapter = kosmos.entryAdapterFactory.create(row.entry) return row } catch (e: Exception) { fail() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt index 16663def16a4..b2521b0ce766 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt @@ -62,13 +62,16 @@ import com.android.systemui.kosmos.testCase import com.android.systemui.res.R import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.AssistantFeedbackController +import com.android.systemui.statusbar.notification.collection.EntryAdapter import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder +import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor import com.android.systemui.statusbar.notification.row.icon.AppIconProvider import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider import com.android.systemui.statusbar.notification.row.icon.mockAppIconProvider import com.android.systemui.statusbar.notification.row.icon.mockNotificationIconStyleProvider +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi import com.android.systemui.testKosmos import com.android.telecom.telecomManager import com.google.common.truth.Truth.assertThat @@ -100,6 +103,7 @@ class NotificationInfoTest : SysuiTestCase() { private lateinit var classifiedNotificationChannel: NotificationChannel private lateinit var sbn: StatusBarNotification private lateinit var entry: NotificationEntry + private lateinit var entryAdapter: EntryAdapter private val mockPackageManager = kosmos.mockPackageManager private val mockAppIconProvider = kosmos.mockAppIconProvider @@ -189,7 +193,12 @@ class NotificationInfoTest : SysuiTestCase() { null, 0, ) - entry = NotificationEntryBuilder().setSbn(sbn).build() + entry = + NotificationEntryBuilder() + .setSbn(sbn) + .updateRanking { it.setChannel(notificationChannel) } + .build() + entryAdapter = kosmos.entryAdapterFactory.create(entry) whenever(assistantFeedbackController.isFeedbackEnabled).thenReturn(false) whenever(assistantFeedbackController.getInlineDescriptionResource(any())) .thenReturn(R.string.notification_channel_summary_automatic) @@ -263,7 +272,7 @@ class NotificationInfoTest : SysuiTestCase() { .thenReturn(applicationInfo) whenever(mockPackageManager.getApplicationLabel(any())).thenReturn("Other") - val entry = NotificationEntryBuilder().setSbn(sbn).build() + val entry = NotificationEntryBuilder(entry).setSbn(sbn).build() bindNotification(entry = entry) val nameView = underTest.findViewById<TextView>(R.id.delegate_name) assertThat(nameView.visibility).isEqualTo(VISIBLE) @@ -304,6 +313,10 @@ class NotificationInfoTest : SysuiTestCase() { @Test fun testBindNotification_DefaultChannelDoesNotUseChannelName() { + entry = + NotificationEntryBuilder(entry) + .updateRanking { it.setChannel(defaultNotificationChannel) } + .build() bindNotification(notificationChannel = defaultNotificationChannel) val textView = underTest.findViewById<TextView>(R.id.channel_name) assertThat(textView.visibility).isEqualTo(GONE) @@ -320,6 +333,10 @@ class NotificationInfoTest : SysuiTestCase() { ) ) .thenReturn(10) + entry = + NotificationEntryBuilder(entry) + .updateRanking { it.setChannel(notificationChannel) } + .build() bindNotification(notificationChannel = defaultNotificationChannel) val textView = underTest.findViewById<TextView>(R.id.channel_name) assertThat(textView.visibility).isEqualTo(VISIBLE) @@ -747,7 +764,13 @@ class NotificationInfoTest : SysuiTestCase() { underTest.findViewById<View>(R.id.done).performClick() underTest.handleCloseControls(true, false) - verify(onUserInteractionCallback).onImportanceChanged(entry) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockVisualStabilityCoordinator) + .temporarilyAllowSectionChanges(eq(entry), any()) + } else { + verify(onUserInteractionCallback).onImportanceChanged(entry) + } + assertThat(underTest.shouldBeSavedOnClose()).isFalse() } @@ -866,7 +889,12 @@ class NotificationInfoTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI) @Throws(RemoteException::class) fun testBindNotification_SetsFeedbackLink_isReservedChannel() { - entry.setRanking(RankingBuilder(entry.ranking).setSummarization("something").build()) + entry.setRanking( + RankingBuilder(entry.ranking) + .setSummarization("something") + .setChannel(classifiedNotificationChannel) + .build() + ) val latch = CountDownLatch(1) bindNotification( notificationChannel = classifiedNotificationChannel, @@ -927,6 +955,7 @@ class NotificationInfoTest : SysuiTestCase() { pkg: String = TEST_PACKAGE_NAME, notificationChannel: NotificationChannel = this.notificationChannel, entry: NotificationEntry = this.entry, + entryAdapter: EntryAdapter = this.entryAdapter, onSettingsClick: NotificationInfo.OnSettingsClickListener? = null, onAppSettingsClick: NotificationInfo.OnAppSettingsClickListener? = null, onFeedbackClickListener: NotificationInfo.OnFeedbackClickListener? = null, @@ -948,8 +977,10 @@ class NotificationInfoTest : SysuiTestCase() { channelEditorDialogController, packageDemotionInteractor, pkg, - notificationChannel, + entry.ranking, + entry.sbn, entry, + entryAdapter, onSettingsClick, onAppSettingsClick, onFeedbackClickListener, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index e6b2c2541447..bda2ba79d6b5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -81,6 +81,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; @@ -746,7 +747,9 @@ public class NotificationTestHelper { mock(PeopleNotificationIdentifier.class), mock(NotificationIconStyleProvider.class), mock(VisualStabilityCoordinator.class), - mock(NotificationActionClickManager.class) + mock(NotificationActionClickManager.class), + mock(HighPriorityProvider.class), + mock(HeadsUpManager.class) ).create(entry); row.initialize( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java index 57b0f3f8d8c5..0eb60163766c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java @@ -40,6 +40,7 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.UserHandle; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.TestableLooper; import android.text.SpannableString; @@ -81,7 +82,6 @@ public class PartialConversationInfoTest extends SysuiTestCase { private TestableLooper mTestableLooper; private PartialConversationInfo mInfo; private NotificationChannel mNotificationChannel; - private NotificationChannel mDefaultNotificationChannel; private StatusBarNotification mSbn; private NotificationEntry mEntry; @@ -141,15 +141,14 @@ public class PartialConversationInfoTest extends SysuiTestCase { // Some test channels. mNotificationChannel = new NotificationChannel( TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW); - mDefaultNotificationChannel = new NotificationChannel( - NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME, - IMPORTANCE_LOW); Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) .setContentTitle(new SpannableString("title")) .build(); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, n, UserHandle.CURRENT, null, 0); - mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); + mEntry = new NotificationEntryBuilder().setSbn(mSbn).updateRanking(rankingBuilder -> { + rankingBuilder.setChannel(mNotificationChannel); + }).build(); } @Test @@ -160,8 +159,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, null, true, false); @@ -178,8 +177,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, null, true, false); @@ -194,8 +193,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, null, true, false); @@ -219,8 +218,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - entry, + mEntry.getRanking(), + mSbn, null, true, false); @@ -237,8 +236,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); @@ -260,8 +259,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); @@ -282,8 +281,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, null, true, false); @@ -298,8 +297,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); }, @@ -316,8 +315,8 @@ public class PartialConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mChannelEditorDialogController, TEST_PACKAGE_NAME, - mNotificationChannel, - mEntry, + mEntry.getRanking(), + mSbn, null, true, true); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java index 209dfb2d2ed6..9b86412fc051 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java @@ -31,6 +31,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.telecom.TelecomManager; import android.testing.TestableLooper; @@ -46,9 +47,16 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; +import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; +import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; @@ -74,6 +82,8 @@ public class PromotedNotificationInfoTest extends SysuiTestCase { private NotificationChannel mNotificationChannel; private StatusBarNotification mSbn; private NotificationEntry mEntry; + private NotificationListenerService.Ranking mRanking; + private EntryAdapter mEntryAdapter; private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); @Rule @@ -110,7 +120,20 @@ public class PromotedNotificationInfoTest extends SysuiTestCase { notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, notification, UserHandle.getUserHandleForUid(TEST_UID), null, 0); - mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); + mEntry = new NotificationEntryBuilder().setSbn(mSbn).updateRanking(rankingBuilder -> { + rankingBuilder.setChannel(mNotificationChannel); + }).build(); + mEntryAdapter = new EntryAdapterFactoryImpl( + mock(NotificationActivityStarter.class), + mock(MetricsLogger.class), + mock(PeopleNotificationIdentifier.class), + mock(NotificationIconStyleProvider.class), + mock(VisualStabilityCoordinator.class), + mock(NotificationActionClickManager.class), + mock(HighPriorityProvider.class), + mock(HeadsUpManager.class) + ).create(mEntry); + mRanking = mEntry.getRanking(); when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false); mTestableLooper = TestableLooper.get(this); @@ -143,8 +166,10 @@ public class PromotedNotificationInfoTest extends SysuiTestCase { mChannelEditorDialogController, mPackageDemotionInteractor, TEST_PACKAGE_NAME, - mNotificationChannel, + mRanking, + mSbn, mEntry, + mEntryAdapter, null, null, null, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 24b955152093..7f46613936ac 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -327,9 +327,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, setupDeviceListView(dialog, hearingDeviceItemList); setupPairNewDeviceButton(dialog); setupPresetSpinner(dialog, activeHearingDevice); - if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl() - && com.android.systemui.Flags - .hearingDevicesInputRoutingUiImprovement()) { + if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) { setupInputRoutingSpinner(dialog, activeHearingDevice); } if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index b16c416fb9df..dbaa90c10313 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -52,6 +52,7 @@ import android.os.PowerManager; import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; +import android.provider.Settings; import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; @@ -920,12 +921,21 @@ public class UdfpsController implements DozeReceiver, Dumpable { true /* isAod */); }; - if (mScreenOn) { + if (isScreenOffUnlockEnabled() || mScreenOn) { mAodInterruptRunnable.run(); mAodInterruptRunnable = null; } } + private boolean isScreenOffUnlockEnabled() { + return mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_enabled) + && Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, + 0, + mContext.getUserId()) != 0; + } + /** * Add a callback for fingerUp and fingerDown events */ diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java index d0c6a3e6a3ef..bf1f971c0f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java @@ -580,7 +580,23 @@ public class MediaSwitchingController synchronized (mMediaDevicesLock) { if (!mLocalMediaManager.isPreferenceRouteListingExist()) { attachRangeInfo(devices); - Collections.sort(devices, Comparator.naturalOrder()); + if (Flags.enableOutputSwitcherDeviceGrouping()) { + List<MediaDevice> selectedDevices = new ArrayList<>(); + Set<String> selectedDeviceIds = + getSelectedMediaDevice().stream() + .map(MediaDevice::getId) + .collect(Collectors.toSet()); + for (MediaDevice device : devices) { + if (selectedDeviceIds.contains(device.getId())) { + selectedDevices.add(device); + } + } + devices.removeAll(selectedDevices); + Collections.sort(devices, Comparator.naturalOrder()); + devices.addAll(0, selectedDevices); + } else { + Collections.sort(devices, Comparator.naturalOrder()); + } } if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) { // For the first time building list, to make sure the top device is the connected diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index 917869a66ca4..3e6f234a3663 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -30,8 +30,10 @@ import android.graphics.Rect; import android.graphics.RenderEffect; import android.graphics.Shader; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Looper; import android.util.AttributeSet; +import android.util.Log; import android.view.MotionEvent; import android.view.View; @@ -52,6 +54,9 @@ import java.util.concurrent.Executor; * need to be careful to synchronize when necessary. */ public class ScrimView extends View { + private static final String TAG = "ScrimView"; + private static final boolean isDebugLoggable = Build.isDebuggable() || Log.isLoggable(TAG, + Log.DEBUG); private final Object mColorLock = new Object(); @@ -381,12 +386,21 @@ public class ScrimView extends View { */ public void setBlurRadius(float blurRadius) { if (blurRadius > 0) { + debugLog("Apply blur RenderEffect to ScrimView " + mScrimName + " for radius " + + blurRadius); setRenderEffect(RenderEffect.createBlurEffect( blurRadius, blurRadius, Shader.TileMode.CLAMP)); } else { + debugLog("Resetting blur RenderEffect to ScrimView " + mScrimName); setRenderEffect(null); } } + + private void debugLog(String logMsg) { + if (isDebugLoggable) { + Log.d(TAG, logMsg); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index b90624245cc5..fcfce1281722 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -989,8 +989,10 @@ public final class NotificationPanelViewController implements mBlurConfig.getMaxBlurRadiusPx(), Shader.TileMode.CLAMP); } + debugLog("Applying blur RenderEffect to shade."); mView.setRenderEffect(mBlurRenderEffect); } else { + debugLog("Resetting blur RenderEffect on shade."); mView.setRenderEffect(null); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java index 11ec2edb36f6..e9a6ad26f5e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java @@ -108,13 +108,12 @@ public class AssistantFeedbackController { /** * Get the feedback status according to assistant's adjustments * - * @param entry Notification Entry to show feedback for + * @param ranking Ranking of the notification show feedback for */ - public int getFeedbackStatus(NotificationEntry entry) { + public int getFeedbackStatus(Ranking ranking) { if (!isFeedbackEnabled()) { return STATUS_UNCHANGED; } - Ranking ranking = entry.getRanking(); int oldImportance = ranking.getChannel().getImportance(); int newImportance = ranking.getImportance(); if (oldImportance < NotificationManager.IMPORTANCE_DEFAULT @@ -138,11 +137,11 @@ public class AssistantFeedbackController { * Get the feedback indicator image and content description resources according to assistant's * changes on this notification's rank or importance. * - * @param entry Notification Entry to show feedback for + * @param ranking Ranking of the notification to show feedback for */ @Nullable - public FeedbackIcon getFeedbackIcon(NotificationEntry entry) { - int feedbackStatus = getFeedbackStatus(entry); + public FeedbackIcon getFeedbackIcon(Ranking ranking) { + int feedbackStatus = getFeedbackStatus(ranking); return mIcons.get(feedbackStatus); } @@ -150,10 +149,10 @@ public class AssistantFeedbackController { * Get the inline settings description resource according to assistant's changes on this * notification's rank or importance. * - * @param entry Notification Entry to show feedback for + * @param ranking Ranking of the notification to show feedback for */ - public int getInlineDescriptionResource(NotificationEntry entry) { - int feedbackStatus = getFeedbackStatus(entry); + public int getInlineDescriptionResource(Ranking ranking) { + int feedbackStatus = getFeedbackStatus(ranking); switch (feedbackStatus) { case STATUS_ALERTED: return com.android.systemui.res.R.string.notification_channel_summary_automatic_alerted; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt index be17ae56c315..6a3f8f166c34 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt @@ -19,14 +19,20 @@ package com.android.systemui.statusbar.notification.collection import android.app.Notification import android.content.Context import android.os.Build +import android.service.notification.NotificationListenerService import android.service.notification.StatusBarNotification import android.util.Log +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.icon.IconPack import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import kotlinx.coroutines.flow.StateFlow -class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter { +class BundleEntryAdapter( + private val highPriorityProvider: HighPriorityProvider, + val entry: BundleEntry, +) : EntryAdapter { /** TODO (b/394483200): convert to PipelineEntry.ROOT_ENTRY when pipeline is migrated? */ override fun getParent(): GroupEntry { return GroupEntry.ROOT_ENTRY @@ -94,6 +100,41 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter { return null } + override fun getRanking(): NotificationListenerService.Ranking? { + return null + } + + override fun endLifetimeExtension( + callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback?, + extender: NotifLifetimeExtender, + ) { + Log.wtf(TAG, "endLifetimeExtension() called") + } + + override fun onImportanceChanged() { + Log.wtf(TAG, "onImportanceChanged() called") + } + + override fun markForUserTriggeredMovement() { + Log.wtf(TAG, "markForUserTriggeredMovement() called") + } + + override fun isMarkedForUserTriggeredMovement(): Boolean { + return false + } + + override fun isHighPriority(): Boolean { + return highPriorityProvider.isHighPriority(entry) + } + + override fun setInlineControlsShown(currentlyVisible: Boolean) { + // nothing to do, yet + } + + override fun isBlockable(): Boolean { + return false + } + override fun canDragAndDrop(): Boolean { return false } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java index 3757ebfb9986..16d9c787d435 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java @@ -17,11 +17,13 @@ package com.android.systemui.statusbar.notification.collection; import android.content.Context; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -123,6 +125,40 @@ public interface EntryAdapter { @Nullable StatusBarNotification getSbn(); + /** + * Returns the ranking that backs this row, if present + */ + @Nullable + NotificationListenerService.Ranking getRanking(); + + void endLifetimeExtension( + @Nullable NotifLifetimeExtender.OnEndLifetimeExtensionCallback callback, + @NonNull NotifLifetimeExtender extender); + + + void onImportanceChanged(); + + /** + * Use when a change has been made to the underlying object that will both rerank the object + * in the shade and change something about its appearance to delay the appearance change until + * the ranking reordering is likely to have settled. + */ + void markForUserTriggeredMovement(); + + /** + * Determines whether a row is considered 'high priority'. + * + * Notifications that are high priority are visible on the lock screen/status bar and in the top + * section in the shade. + */ + boolean isHighPriority(); + + boolean isMarkedForUserTriggeredMovement(); + + void setInlineControlsShown(boolean currentlyVisible); + + boolean isBlockable(); + boolean canDragAndDrop(); boolean isBubble(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt index a5169865c3c9..2fce19c2becb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.collection import com.android.internal.logging.MetricsLogger import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.row.NotificationActionClickManager import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider @@ -34,6 +36,8 @@ constructor( private val iconStyleProvider: NotificationIconStyleProvider, private val visualStabilityCoordinator: VisualStabilityCoordinator, private val notificationActionClickManager: NotificationActionClickManager, + private val highPriorityProvider: HighPriorityProvider, + private val headsUpManager: HeadsUpManager, ) : EntryAdapterFactory { override fun create(entry: PipelineEntry): EntryAdapter { return if (entry is NotificationEntry) { @@ -44,10 +48,12 @@ constructor( iconStyleProvider, visualStabilityCoordinator, notificationActionClickManager, + highPriorityProvider, + headsUpManager, entry, ) } else { - BundleEntryAdapter((entry as BundleEntry)) + BundleEntryAdapter(highPriorityProvider, (entry as BundleEntry)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index b5ab0920a470..c94289c3befa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -1030,7 +1030,7 @@ public final class NotificationEntry extends ListEntry { } /** - * Mark this entry for movement triggered by a user action (ex: changing the priorirty of a + * Mark this entry for movement triggered by a user action (ex: changing the priority of a * conversation). This can then be used for custom animations. */ public void markForUserTriggeredMovement(boolean marked) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt index a23c5a3ea9f2..339a999e1535 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt @@ -17,10 +17,15 @@ package com.android.systemui.statusbar.notification.collection import android.content.Context +import android.os.SystemClock +import android.service.notification.NotificationListenerService import android.service.notification.StatusBarNotification import com.android.internal.logging.MetricsLogger import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator +import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager import com.android.systemui.statusbar.notification.icon.IconPack import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -35,6 +40,8 @@ class NotificationEntryAdapter( private val iconStyleProvider: NotificationIconStyleProvider, private val visualStabilityCoordinator: VisualStabilityCoordinator, private val notificationActionClickManager: NotificationActionClickManager, + private val highPriorityProvider: HighPriorityProvider, + private val headsUpManager: HeadsUpManager, private val entry: NotificationEntry, ) : EntryAdapter { @@ -110,6 +117,41 @@ class NotificationEntryAdapter( return entry.sbn } + override fun getRanking(): NotificationListenerService.Ranking? { + return entry.ranking + } + + override fun endLifetimeExtension( + callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback?, + extender: NotifLifetimeExtender, + ) { + callback?.onEndLifetimeExtension(extender, entry) + } + + override fun onImportanceChanged() { + visualStabilityCoordinator.temporarilyAllowSectionChanges(entry, SystemClock.uptimeMillis()) + } + + override fun markForUserTriggeredMovement() { + entry.markForUserTriggeredMovement(true) + } + + override fun isMarkedForUserTriggeredMovement(): Boolean { + return entry.isMarkedForUserTriggeredMovement + } + + override fun isHighPriority(): Boolean { + return highPriorityProvider.isHighPriority(entry) + } + + override fun setInlineControlsShown(currentlyVisible: Boolean) { + headsUpManager.setGutsShown(entry, currentlyVisible) + } + + override fun isBlockable(): Boolean { + return entry.isBlockable + } + override fun canDragAndDrop(): Boolean { val canBubble: Boolean = entry.canBubble() val notif = entry.sbn.notification diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt index 9045aac6ea6f..754f89191684 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt @@ -18,9 +18,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.util.ArraySet import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager -import com.android.systemui.statusbar.notification.collection.PipelineEntry +import com.android.systemui.statusbar.notification.collection.EntryAdapter import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback @@ -28,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager import com.android.systemui.statusbar.notification.row.NotificationGuts import com.android.systemui.statusbar.notification.row.NotificationGutsManager +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi import com.android.systemui.util.asIndenting import com.android.systemui.util.printCollection import com.android.systemui.util.println @@ -38,23 +40,25 @@ import javax.inject.Inject private const val TAG = "GutsCoordinator" /** - * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. - * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing. + * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. Specifically, + * this just adds the lifetime extension necessary to keep guts from disappearing. */ @CoordinatorScope -class GutsCoordinator @Inject constructor( +class GutsCoordinator +@Inject +constructor( private val notifGutsViewManager: NotifGutsViewManager, private val logger: GutsCoordinatorLogger, - dumpManager: DumpManager + dumpManager: DumpManager, ) : Coordinator, Dumpable { - /** Keys of any Notifications for which we've been told the guts are open */ + /** Keys of any Notifications for which we've been told the guts are open */ private val notifsWithOpenGuts = ArraySet<String>() - /** Keys of any Notifications we've extended the lifetime for, based on guts */ + /** Keys of any Notifications we've extended the lifetime for, based on guts */ private val notifsExtendingLifetime = ArraySet<String>() - /** Callback for ending lifetime extension */ + /** Callback for ending lifetime extension */ private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null init { @@ -66,55 +70,77 @@ class GutsCoordinator @Inject constructor( pipeline.addNotificationLifetimeExtender(mLifetimeExtender) } - override fun dump(pw: PrintWriter, args: Array<String>) = pw.asIndenting().run { - withIncreasedIndent { - printCollection("notifsWithOpenGuts", notifsWithOpenGuts) - printCollection("notifsExtendingLifetime", notifsExtendingLifetime) - println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback) + override fun dump(pw: PrintWriter, args: Array<String>) = + pw.asIndenting().run { + withIncreasedIndent { + printCollection("notifsWithOpenGuts", notifsWithOpenGuts) + printCollection("notifsExtendingLifetime", notifsExtendingLifetime) + println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback) + } } - } - private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender { - override fun getName(): String { - return TAG - } + private val mLifetimeExtender: NotifLifetimeExtender = + object : NotifLifetimeExtender { + override fun getName(): String { + return TAG + } - override fun setCallback(callback: OnEndLifetimeExtensionCallback) { - onEndLifetimeExtensionCallback = callback - } + override fun setCallback(callback: OnEndLifetimeExtensionCallback) { + onEndLifetimeExtensionCallback = callback + } - override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { - val isShowingGuts = isCurrentlyShowingGuts(entry) - if (isShowingGuts) { - notifsExtendingLifetime.add(entry.key) + override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean { + val isShowingGuts = isCurrentlyShowingGuts(entry) + if (isShowingGuts) { + notifsExtendingLifetime.add(entry.key) + } + return isShowingGuts } - return isShowingGuts - } - override fun cancelLifetimeExtension(entry: NotificationEntry) { - notifsExtendingLifetime.remove(entry.key) + override fun cancelLifetimeExtension(entry: NotificationEntry) { + notifsExtendingLifetime.remove(entry.key) + } } - } - private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener { - override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) { - logger.logGutsOpened(entry.key, guts) - if (guts.isLeavebehind) { - // leave-behind guts should not extend the lifetime of the notification + private val mGutsListener: NotifGutsViewListener = + object : NotifGutsViewListener { + override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) { + NotificationBundleUi.assertInLegacyMode() + logger.logGutsOpened(entry.key, guts) + if (guts.isLeavebehind) { + // leave-behind guts should not extend the lifetime of the notification + closeGutsAndEndLifetimeExtension(entry) + } else { + notifsWithOpenGuts.add(entry.key) + } + } + + override fun onGutsClose(entry: NotificationEntry) { + NotificationBundleUi.assertInLegacyMode() + logger.logGutsClosed(entry.key) closeGutsAndEndLifetimeExtension(entry) - } else { - notifsWithOpenGuts.add(entry.key) } - } - override fun onGutsClose(entry: NotificationEntry) { - logger.logGutsClosed(entry.key) - closeGutsAndEndLifetimeExtension(entry) + override fun onGutsOpen(entryAdapter: EntryAdapter, guts: NotificationGuts) { + NotificationBundleUi.isUnexpectedlyInLegacyMode() + logger.logGutsOpened(entryAdapter.key, guts) + if (guts.isLeavebehind) { + // leave-behind guts should not extend the lifetime of the notification + closeGutsAndEndLifetimeExtension(entryAdapter) + } else { + notifsWithOpenGuts.add(entryAdapter.key) + } + } + + override fun onGutsClose(entryAdapter: EntryAdapter) { + NotificationBundleUi.isUnexpectedlyInLegacyMode() + logger.logGutsClosed(entryAdapter.key) + closeGutsAndEndLifetimeExtension(entryAdapter) + } } - } private fun isCurrentlyShowingGuts(entry: PipelineEntry) = - notifsWithOpenGuts.contains(entry.key) + notifsWithOpenGuts.contains(entry.key) private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) { notifsWithOpenGuts.remove(entry.key) @@ -122,4 +148,11 @@ class GutsCoordinator @Inject constructor( onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry) } } + + private fun closeGutsAndEndLifetimeExtension(entryAdapter: EntryAdapter) { + notifsWithOpenGuts.remove(entryAdapter.key) + if (notifsExtendingLifetime.remove(entryAdapter.key)) { + entryAdapter.endLifetimeExtension(onEndLifetimeExtensionCallback, mLifetimeExtender) + } + } } 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 a987c544bb50..341cc09aafc1 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 @@ -20,9 +20,9 @@ import android.content.Context import com.android.systemui.res.R import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.notification.AssistantFeedbackController -import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.collection.render.NotifRowController @@ -79,6 +79,6 @@ internal constructor( (mAutoExpandFirstNotification && entry == entryToExpand) ) // Show/hide the feedback icon - controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry)) + controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry.ranking)) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 03b4076ba6fb..ca3d2e2a2a35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi; import javax.inject.Inject; @@ -83,6 +84,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback @Override public void onImportanceChanged(NotificationEntry entry) { + NotificationBundleUi.assertInLegacyMode(); mVisualStabilityCoordinator.temporarilyAllowSectionChanges( entry, UseElapsedRealtimeForCreationTime.getCurrentTime()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt index 129f6b1750e6..9e49083e4d04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt @@ -15,16 +15,21 @@ */ package com.android.systemui.statusbar.notification.collection.render +import com.android.systemui.statusbar.notification.collection.EntryAdapter import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.NotificationGuts -/** - * Interface for listening to guts open and close events. - */ +/** Interface for listening to guts open and close events. */ interface NotifGutsViewListener { /** A notification's guts are being opened */ fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) /** A notification's guts are being closed */ fun onGutsClose(entry: NotificationEntry) + + /** A notification's guts are being opened */ + fun onGutsOpen(entryAdapter: EntryAdapter, guts: NotificationGuts) + + /** A notification's guts are being closed */ + fun onGutsClose(entryAdapter: EntryAdapter) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt index 777392df67cc..fdbd75bd33ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt @@ -226,7 +226,7 @@ private val PromotedNotificationContentModel.layoutResource: Int? private class AODPromotedNotificationViewUpdater(root: View) { private val alertedIcon: ImageView? = root.findViewById(R.id.alerted_icon) private val alternateExpandTarget: View? = root.findViewById(R.id.alternate_expand_target) - private val appNameDivider: View? = root.findViewById(R.id.app_name_divider) + private val appNameDivider: TextView? = root.findViewById(R.id.app_name_divider) private val appNameText: TextView? = root.findViewById(R.id.app_name_text) private val bigPicture: BigPictureNotificationImageView? = root.findViewById(R.id.big_picture) private val bigText: ImageFloatingTextView? = root.findViewById(R.id.big_text) @@ -241,9 +241,9 @@ private class AODPromotedNotificationViewUpdater(root: View) { ) private val expandButton: NotificationExpandButton? = root.findViewById(R.id.expand_button) private val headerText: TextView? = root.findViewById(R.id.header_text) - private val headerTextDivider: View? = root.findViewById(R.id.header_text_divider) + private val headerTextDivider: TextView? = root.findViewById(R.id.header_text_divider) private val headerTextSecondary: TextView? = root.findViewById(R.id.header_text_secondary) - private val headerTextSecondaryDivider: View? = + private val headerTextSecondaryDivider: TextView? = root.findViewById(R.id.header_text_secondary_divider) private val icon: NotificationRowIconView? = root.findViewById(R.id.icon) private val leftIcon: ImageView? = root.findViewById(R.id.left_icon) @@ -256,9 +256,9 @@ private class AODPromotedNotificationViewUpdater(root: View) { private val rightIcon: ImageView? = root.findViewById(R.id.right_icon) private val text: ImageFloatingTextView? = root.findViewById(R.id.text) private val time: DateTimeView? = root.findViewById(R.id.time) - private val timeDivider: View? = root.findViewById(R.id.time_divider) + private val timeDivider: TextView? = root.findViewById(R.id.time_divider) private val title: TextView? = root.findViewById(R.id.title) - private val verificationDivider: View? = root.findViewById(R.id.verification_divider) + private val verificationDivider: TextView? = root.findViewById(R.id.verification_divider) private val verificationIcon: ImageView? = root.findViewById(R.id.verification_icon) private val verificationText: TextView? = root.findViewById(R.id.verification_text) @@ -283,6 +283,12 @@ private class AODPromotedNotificationViewUpdater(root: View) { ?.mutate() ?.setColorFilter(SecondaryText.colorInt, PorterDuff.Mode.SRC_IN) + setTextViewColor(appNameDivider, SecondaryText) + setTextViewColor(headerTextDivider, SecondaryText) + setTextViewColor(headerTextSecondaryDivider, SecondaryText) + setTextViewColor(timeDivider, SecondaryText) + setTextViewColor(verificationDivider, SecondaryText) + if (Flags.notificationsRedesignTemplates()) { (mainColumn?.layoutParams as? MarginLayoutParams)?.let { mainColumnMargins -> mainColumnMargins.topMargin = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java index 40897dae4c44..d80d5e101294 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java @@ -45,7 +45,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.Compile; public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent { @@ -58,7 +57,6 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC private PackageManager mPm; private String mAppName; private String mPkg; - private NotificationEntry mEntry; private IStatusBarService mStatusBarService; private AssistantFeedbackController mFeedbackController; @@ -73,16 +71,15 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC public void bindGuts( final PackageManager pm, final StatusBarNotification sbn, - final NotificationEntry entry, + final NotificationListenerService.Ranking ranking, final ExpandableNotificationRow row, final AssistantFeedbackController controller, final IStatusBarService statusBarService, final NotificationGutsManager notificationGutsManager) { mPkg = sbn.getPackageName(); mPm = pm; - mEntry = entry; mExpandableNotificationRow = row; - mRanking = entry.getRanking(); + mRanking = ranking; mFeedbackController = controller; mAppName = mPkg; mStatusBarService = statusBarService; @@ -143,7 +140,7 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC @SuppressLint("DefaultLocale") private String getPrompt() { StringBuilder sb = new StringBuilder(); - int status = mFeedbackController.getFeedbackStatus(mEntry); + int status = mFeedbackController.getFeedbackStatus(mRanking); if (DEBUG) { sb.append(String.format( "[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%f\n\n", diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 57ceafcd15c6..f17ae571d3ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -1392,7 +1392,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mPromotedNotificationContentExtractor.extractContent(mEntry, recoveredBuilder, mBindParams.redactionType, imageModelProvider); mLogger.logAsyncTaskProgress(logKey, "extracted promoted notification content: " - + promotedContent); + + (promotedContent != null ? promotedContent.toRedactedString() : null)); result.mPromotedContent = promotedContent; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java index 3c7d9ef8e71d..2cf3b14bb8c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java @@ -49,6 +49,7 @@ import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.transition.ChangeBounds; @@ -72,7 +73,9 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.notification.NmSummarizationUiFlag; import com.android.systemui.statusbar.notification.NotificationChannelHelper; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.wmshell.BubblesManager; @@ -104,6 +107,8 @@ public class NotificationConversationInfo extends LinearLayout implements private ShortcutInfo mShortcutInfo; private NotificationEntry mEntry; private StatusBarNotification mSbn; + private EntryAdapter mEntryAdapter; + private NotificationListenerService.Ranking mRanking; @Nullable private Notification.BubbleMetadata mBubbleMetadata; private Context mUserContext; private boolean mIsDeviceProvisioned; @@ -200,9 +205,10 @@ public class NotificationConversationInfo extends LinearLayout implements INotificationManager iNotificationManager, OnUserInteractionCallback onUserInteractionCallback, String pkg, - NotificationChannel notificationChannel, NotificationEntry entry, - Notification.BubbleMetadata bubbleMetadata, + EntryAdapter entryAdapter, + NotificationListenerService.Ranking ranking, + StatusBarNotification sbn, OnSettingsClickListener onSettingsClick, NotificationInfo.OnFeedbackClickListener onFeedbackClickListener, ConversationIconFactory conversationIconFactory, @@ -218,25 +224,27 @@ public class NotificationConversationInfo extends LinearLayout implements mOnUserInteractionCallback = onUserInteractionCallback; mPackageName = pkg; mEntry = entry; - mSbn = entry.getSbn(); + mSbn = sbn; + mRanking = ranking; + mEntryAdapter = entryAdapter; mPm = pm; mUm = um; mAppName = mPackageName; mOnSettingsClickListener = onSettingsClick; - mNotificationChannel = notificationChannel; + mNotificationChannel = ranking.getChannel(); mAppUid = mSbn.getUid(); mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; mOnConversationSettingsClickListener = onConversationSettingsClickListener; mIconFactory = conversationIconFactory; mUserContext = userContext; - mBubbleMetadata = bubbleMetadata; + mBubbleMetadata = sbn.getNotification().getBubbleMetadata(); mBubblesManagerOptional = bubblesManagerOptional; mShadeController = shadeController; mMainHandler = mainHandler; mBgHandler = bgHandler; mShortcutManager = shortcutManager; - mShortcutInfo = entry.getRanking().getConversationShortcutInfo(); + mShortcutInfo = ranking.getConversationShortcutInfo(); mFeedbackClickListener = onFeedbackClickListener; if (mShortcutInfo == null) { throw new IllegalArgumentException("Does not have required information"); @@ -308,11 +316,11 @@ public class NotificationConversationInfo extends LinearLayout implements private void bindFeedback() { View feedbackButton = findViewById(R.id.feedback); if (!NmSummarizationUiFlag.isEnabled() - || TextUtils.isEmpty(mEntry.getRanking().getSummarization())) { + || TextUtils.isEmpty(mRanking.getSummarization())) { feedbackButton.setVisibility(GONE); } else { Intent intent = NotificationInfo.getAssistantFeedbackIntent( - mINotificationManager, mPm, mEntry); + mINotificationManager, mPm, mSbn.getKey(), mRanking); if (intent == null) { feedbackButton.setVisibility(GONE); } else { @@ -551,10 +559,17 @@ public class NotificationConversationInfo extends LinearLayout implements mBgHandler.post( new UpdateChannelRunnable(mINotificationManager, mPackageName, mAppUid, mSelectedAction, mNotificationChannel)); - mEntry.markForUserTriggeredMovement(true); - mMainHandler.postDelayed( - () -> mOnUserInteractionCallback.onImportanceChanged(mEntry), - StackStateAnimator.ANIMATION_DURATION_STANDARD); + if (NotificationBundleUi.isEnabled()) { + mEntryAdapter.markForUserTriggeredMovement(); + mMainHandler.postDelayed( + () -> mEntryAdapter.onImportanceChanged(), + StackStateAnimator.ANIMATION_DURATION_STANDARD); + } else { + mEntry.markForUserTriggeredMovement(true); + mMainHandler.postDelayed( + () -> mOnUserInteractionCallback.onImportanceChanged(mEntry), + StackStateAnimator.ANIMATION_DURATION_STANDARD); + } } private boolean willBypassDnd() { @@ -658,8 +673,13 @@ public class NotificationConversationInfo extends LinearLayout implements BUBBLE_PREFERENCE_SELECTED); } if (mBubblesManagerOptional.isPresent()) { - post(() -> mBubblesManagerOptional.get() - .onUserSetImportantConversation(mEntry)); + if (NotificationBundleUi.isEnabled()) { + post(() -> mBubblesManagerOptional.get() + .onUserSetImportantConversation(mEntryAdapter)); + } else { + post(() -> mBubblesManagerOptional.get() + .onUserSetImportantConversation(mEntry)); + } } } mChannelToUpdate.setImportance(Math.max( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index f4e01bf718d9..6c7c7a79348f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -33,6 +33,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.IconDrawableFactory; @@ -66,7 +67,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener; import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager; @@ -289,10 +289,21 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta @VisibleForTesting protected boolean bindGuts(final ExpandableNotificationRow row, NotificationMenuRowPlugin.MenuItem item) { - NotificationEntry entry = row.getEntry(); + + StatusBarNotification sbn = NotificationBundleUi.isEnabled() + ? row.getEntryAdapter().getSbn() + : row.getEntryLegacy().getSbn(); + NotificationListenerService.Ranking ranking = NotificationBundleUi.isEnabled() + ? row.getEntryAdapter().getRanking() + : row.getEntryLegacy().getRanking(); + + if (sbn == null || ranking == null) { + // only valid for notification rows + return false; + } row.setGutsView(item); - row.setTag(entry.getSbn().getPackageName()); + row.setTag(sbn.getPackageName()); row.getGuts().setClosedListener((NotificationGuts g) -> { row.onGutsClosed(); if (!g.willBeRemoved() && !row.isRemoved()) { @@ -304,28 +315,37 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mGutsMenuItem = null; } if (mGutsListener != null) { - mGutsListener.onGutsClose(entry); + if (NotificationBundleUi.isEnabled()) { + mGutsListener.onGutsClose(row.getEntryAdapter()); + row.updateBubbleButton(); + } else { + mGutsListener.onGutsClose(row.getEntryLegacy()); + } + } + if(NotificationBundleUi.isEnabled()) { + row.getEntryAdapter().setInlineControlsShown(false); + } else { + mHeadsUpManager.setGutsShown(row.getEntryLegacy(), false); } - mHeadsUpManager.setGutsShown(row.getEntry(), false); }); View gutsView = item.getGutsView(); try { if (gutsView instanceof NotificationSnooze) { - initializeSnoozeView(row, (NotificationSnooze) gutsView); + initializeSnoozeView(row, sbn, ranking, (NotificationSnooze) gutsView); } else if (gutsView instanceof NotificationInfo) { - initializeNotificationInfo(row, (NotificationInfo) gutsView); + initializeNotificationInfo(row, sbn, ranking, (NotificationInfo) gutsView); } else if (gutsView instanceof NotificationConversationInfo) { initializeConversationNotificationInfo( - row, (NotificationConversationInfo) gutsView); + row, sbn, ranking, (NotificationConversationInfo) gutsView); } else if (gutsView instanceof PartialConversationInfo) { - initializePartialConversationNotificationInfo(row, + initializePartialConversationNotificationInfo(row, sbn, ranking, (PartialConversationInfo) gutsView); } else if (gutsView instanceof FeedbackInfo) { - initializeFeedbackInfo(row, (FeedbackInfo) gutsView); + initializeFeedbackInfo(row, sbn, ranking, (FeedbackInfo) gutsView); } else if (gutsView instanceof PromotedPermissionGutsContent) { - initializeDemoteView(row, (PromotedPermissionGutsContent) gutsView); + initializeDemoteView(row, sbn, (PromotedPermissionGutsContent) gutsView); } return true; } catch (Exception e) { @@ -342,13 +362,14 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta */ private void initializeSnoozeView( final ExpandableNotificationRow row, + final StatusBarNotification sbn, + final NotificationListenerService.Ranking ranking, NotificationSnooze notificationSnoozeView) { NotificationGuts guts = row.getGuts(); - StatusBarNotification sbn = row.getEntry().getSbn(); notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper()); notificationSnoozeView.setStatusBarNotification(sbn); - notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria()); + notificationSnoozeView.setSnoozeOptions(ranking.getSnoozeCriteria()); guts.setHeightChangedListener((NotificationGuts g) -> { mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */); }); @@ -362,8 +383,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta */ private void initializeDemoteView( final ExpandableNotificationRow row, + StatusBarNotification sbn, PromotedPermissionGutsContent demoteGuts) { - StatusBarNotification sbn = row.getEntry().getSbn(); demoteGuts.setStatusBarNotification(sbn); demoteGuts.setOnDemoteAction(new View.OnClickListener() { @Override @@ -387,16 +408,17 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta */ private void initializeFeedbackInfo( final ExpandableNotificationRow row, + final StatusBarNotification sbn, + final NotificationListenerService.Ranking ranking, FeedbackInfo feedbackInfo) { - if (mAssistantFeedbackController.getFeedbackIcon(row.getEntry()) == null) { + if (mAssistantFeedbackController.getFeedbackIcon(ranking) == null) { return; } - StatusBarNotification sbn = row.getEntry().getSbn(); UserHandle userHandle = sbn.getUser(); PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext, userHandle.getIdentifier()); - feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController, + feedbackInfo.bindGuts(pmUser, sbn, ranking, row, mAssistantFeedbackController, mStatusBarService, this); } @@ -408,9 +430,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta @VisibleForTesting void initializeNotificationInfo( final ExpandableNotificationRow row, + final StatusBarNotification sbn, + final NotificationListenerService.Ranking ranking, NotificationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); - StatusBarNotification sbn = row.getEntry().getSbn(); String packageName = sbn.getPackageName(); // Settings link is only valid for notifications that specify a non-system user NotificationInfo.OnSettingsClickListener onSettingsClick = null; @@ -449,18 +472,22 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mChannelEditorDialogController, mPackageDemotionInteractor, packageName, - row.getEntry().getChannel(), - row.getEntry(), + ranking, + sbn, + NotificationBundleUi.isEnabled() ? null : row.getEntryLegacy(), + NotificationBundleUi.isEnabled() ? row.getEntryAdapter() : null, onSettingsClick, onAppSettingsClick, onNasFeedbackClick, mUiEventLogger, mDeviceProvisionedController.isDeviceProvisioned(), NotificationBundleUi.isEnabled() - ? !row.getEntry().isBlockable() + ? !row.getEntryAdapter().isBlockable() : row.getIsNonblockable(), row.canViewBeDismissed(), - mHighPriorityProvider.isHighPriority(row.getEntry()), + NotificationBundleUi.isEnabled() + ? row.getEntryAdapter().isHighPriority() + : mHighPriorityProvider.isHighPriority(row.getEntryLegacy()), mAssistantFeedbackController, mMetricsLogger, row.getCloseButtonOnClickListener(row)); @@ -474,9 +501,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta @VisibleForTesting void initializePartialConversationNotificationInfo( final ExpandableNotificationRow row, + final StatusBarNotification sbn, + final NotificationListenerService.Ranking ranking, PartialConversationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); - StatusBarNotification sbn = row.getEntry().getSbn(); String packageName = sbn.getPackageName(); // Settings link is only valid for notifications that specify a non-system user NotificationInfo.OnSettingsClickListener onSettingsClick = null; @@ -499,12 +527,12 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mNotificationManager, mChannelEditorDialogController, packageName, - row.getEntry().getChannel(), - row.getEntry(), + ranking, + sbn, onSettingsClick, mDeviceProvisionedController.isDeviceProvisioned(), NotificationBundleUi.isEnabled() - ? !row.getEntry().isBlockable() + ? !row.getEntryAdapter().isBlockable() : row.getIsNonblockable()); } @@ -516,10 +544,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta @VisibleForTesting void initializeConversationNotificationInfo( final ExpandableNotificationRow row, + final StatusBarNotification sbn, + final NotificationListenerService.Ranking ranking, NotificationConversationInfo notificationInfoView) throws Exception { NotificationGuts guts = row.getGuts(); - NotificationEntry entry = row.getEntry(); - StatusBarNotification sbn = entry.getSbn(); String packageName = sbn.getPackageName(); // Settings link is only valid for notifications that specify a non-system user NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null; @@ -567,9 +595,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta mNotificationManager, mOnUserInteractionCallback, packageName, - entry.getChannel(), - entry, - entry.getBubbleMetadata(), + NotificationBundleUi.isEnabled() ? null : row.getEntryLegacy(), + NotificationBundleUi.isEnabled() ? row.getEntryAdapter() : null, + ranking, + sbn, onSettingsClick, onNasFeedbackClick, iconFactoryLoader, @@ -746,13 +775,21 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta row::onGutsOpened); if (mGutsListener != null) { - mGutsListener.onGutsOpen(row.getEntry(), guts); + if(NotificationBundleUi.isEnabled()) { + mGutsListener.onGutsOpen(row.getEntryAdapter(), guts); + } else { + mGutsListener.onGutsOpen(row.getEntryLegacy(), guts); + } } row.closeRemoteInput(); mListContainer.onHeightChanged(row, true /* needsAnimation */); mGutsMenuItem = menuItem; - mHeadsUpManager.setGutsShown(row.getEntry(), true); + if(NotificationBundleUi.isEnabled()) { + row.getEntryAdapter().setInlineControlsShown(true); + } else { + mHeadsUpManager.setGutsShown(row.getEntryLegacy(), true); + } } }; guts.post(mOpenRunnable); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index b6f4ffce8e00..571006bc21e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -50,6 +50,7 @@ import android.metrics.LogMaker; import android.os.Handler; import android.os.RemoteException; import android.service.notification.NotificationAssistantService; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.text.Html; import android.text.TextUtils; @@ -73,10 +74,12 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.Dependency; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor; import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi; import java.lang.annotation.Retention; import java.util.List; @@ -124,6 +127,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private boolean mIsDismissable; private NotificationEntry mEntry; private StatusBarNotification mSbn; + private NotificationListenerService.Ranking mRanking; + private EntryAdapter mEntryAdapter; private boolean mIsDeviceProvisioned; private boolean mIsSystemRegisteredCall; @@ -198,8 +203,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G ChannelEditorDialogController channelEditorDialogController, PackageDemotionInteractor packageDemotionInteractor, String pkg, - NotificationChannel notificationChannel, + NotificationListenerService.Ranking ranking, + StatusBarNotification sbn, NotificationEntry entry, + EntryAdapter entryAdapter, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, OnFeedbackClickListener onFeedbackClickListener, @@ -220,14 +227,16 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mChannelEditorDialogController = channelEditorDialogController; mAssistantFeedbackController = assistantFeedbackController; mPackageName = pkg; + mSbn = sbn; + mRanking = ranking; mEntry = entry; - mSbn = entry.getSbn(); + mEntryAdapter = entryAdapter; mPm = pm; mAppSettingsClickListener = onAppSettingsClick; mFeedbackClickListener = onFeedbackClickListener; mAppName = mPackageName; mOnSettingsClickListener = onSettingsClick; - mSingleNotificationChannel = notificationChannel; + mSingleNotificationChannel = ranking.getChannel(); mStartingChannelImportance = mSingleNotificationChannel.getImportance(); mWasShownHighPriority = wasShownHighPriority; mIsNonblockable = isNonblockable; @@ -301,7 +310,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G View automatic = findViewById(R.id.automatic); if (mShowAutomaticSetting) { mAutomaticDescriptionView.setText(Html.fromHtml(mContext.getText( - mAssistantFeedbackController.getInlineDescriptionResource(mEntry)).toString())); + mAssistantFeedbackController.getInlineDescriptionResource(mRanking)) + .toString())); automatic.setVisibility(VISIBLE); automatic.setOnClickListener(mOnAutomatic); } else { @@ -381,7 +391,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindFeedback() { View feedbackButton = findViewById(R.id.feedback); - Intent intent = getAssistantFeedbackIntent(mINotificationManager, mPm, mEntry); + Intent intent = getAssistantFeedbackIntent( + mINotificationManager, mPm, mSbn.getKey(), mRanking); if (!android.app.Flags.notificationClassificationUi() || intent == null) { feedbackButton.setVisibility(GONE); } else { @@ -395,7 +406,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } public static Intent getAssistantFeedbackIntent(INotificationManager inm, PackageManager pm, - NotificationEntry entry) { + String key, NotificationListenerService.Ranking ranking) { try { ComponentName assistant = inm.getAllowedNotificationAssistant(); if (assistant == null) { @@ -414,9 +425,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo; intent.setClassName(activityInfo.packageName, activityInfo.name); - intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_KEY, entry.getKey()); + intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_KEY, key); intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_ADJUSTMENT, - entry.getRanking().getSummarization() != null ? KEY_SUMMARIZATION : KEY_TYPE); + ranking.getSummarization() != null ? KEY_SUMMARIZATION : KEY_TYPE); return intent; } catch (Exception e) { Slog.d(TAG, "no assistant?", e); @@ -526,7 +537,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid, mSingleNotificationChannel, mStartingChannelImportance, newImportance, mIsAutomaticChosen)); - mOnUserInteractionCallback.onImportanceChanged(mEntry); + if (NotificationBundleUi.isEnabled()) { + mEntryAdapter.onImportanceChanged(); + } else { + mOnUserInteractionCallback.onImportanceChanged(mEntry); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index 4f1b90544403..efc90c91092a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -710,7 +710,7 @@ constructor( .also { logger.logAsyncTaskProgress( entry.logKey, - "extracted promoted notification content: $it", + "extracted promoted notification content: ${it?.toRedactedString()}", ) } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java index 60e98a5c317a..3b795796020c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java @@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.RemoteException; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; @@ -35,6 +36,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.res.R; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; /** @@ -79,18 +81,18 @@ public class PartialConversationInfo extends LinearLayout implements INotificationManager iNotificationManager, ChannelEditorDialogController channelEditorDialogController, String pkg, - NotificationChannel notificationChannel, - NotificationEntry entry, + NotificationListenerService.Ranking ranking, + StatusBarNotification sbn, NotificationInfo.OnSettingsClickListener onSettingsClick, boolean isDeviceProvisioned, boolean isNonBlockable) { mINotificationManager = iNotificationManager; mPackageName = pkg; - mSbn = entry.getSbn(); + mSbn = sbn; mPm = pm; mAppName = mPackageName; mOnSettingsClickListener = onSettingsClick; - mNotificationChannel = notificationChannel; + mNotificationChannel = ranking.getChannel(); mAppUid = mSbn.getUid(); mDelegatePkg = mSbn.getOpPkg(); mIsDeviceProvisioned = isDeviceProvisioned; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java index 769f0b5a4fa4..2cb208932943 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java @@ -21,6 +21,7 @@ import android.app.NotificationChannel; import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; +import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.AttributeSet; import android.util.Log; @@ -30,6 +31,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AssistantFeedbackController; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor; import com.android.systemui.statusbar.notification.row.icon.AppIconProvider; @@ -60,8 +62,10 @@ public class PromotedNotificationInfo extends NotificationInfo { ChannelEditorDialogController channelEditorDialogController, PackageDemotionInteractor packageDemotionInteractor, String pkg, - NotificationChannel notificationChannel, + NotificationListenerService.Ranking ranking, + StatusBarNotification sbn, NotificationEntry entry, + EntryAdapter entryAdapter, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, OnFeedbackClickListener feedbackClickListener, @@ -73,17 +77,17 @@ public class PromotedNotificationInfo extends NotificationInfo { AssistantFeedbackController assistantFeedbackController, MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException { super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider, - onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor, - pkg, notificationChannel, - entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger, - isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority, - assistantFeedbackController, metricsLogger, onCloseClick); + onUserInteractionCallback, channelEditorDialogController, + packageDemotionInteractor,pkg, ranking, sbn, + entry, entryAdapter, onSettingsClick, onAppSettingsClick, feedbackClickListener, + uiEventLogger, isDeviceProvisioned, isDismissable, isNonblockable, + wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick); mNotificationManager = iNotificationManager; mPackageDemotionInteractor = packageDemotionInteractor; - bindDemote(entry.getSbn(), pkg); + bindDemote(sbn, pkg); } protected void bindDemote(StatusBarNotification sbn, String packageName) { 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 503256accff0..afa988dd8e89 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 @@ -6076,7 +6076,9 @@ public class NotificationStackScrollLayout if (mBlurRadius > 0) { mBlurEffect = RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, Shader.TileMode.CLAMP); + spewLog("Setting up blur RenderEffect for NotificationStackScrollLayout"); } else { + spewLog("Clearing the blur RenderEffect setup for NotificationStackScrollLayout"); mBlurEffect = null; } } @@ -6252,6 +6254,7 @@ public class NotificationStackScrollLayout @Override protected void dispatchDraw(@NonNull Canvas canvas) { if (mBlurEffect != null) { + spewLog("Applying blur RenderEffect to NotificationStackScrollLayout"); // reuse the cached RenderNode to blur mBlurNode.setPosition(0, 0, canvas.getWidth(), canvas.getHeight()); mBlurNode.setRenderEffect(mBlurEffect); @@ -7025,4 +7028,10 @@ public class NotificationStackScrollLayout SceneContainerFlag.assertInLegacyMode(); mMaxTopPadding = maxTopPadding; } + + private void spewLog(String logMsg) { + if (SPEW) { + Log.v(TAG, logMsg); + } + } } 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 612c19fc6696..66c9b17ef235 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 @@ -420,10 +420,15 @@ public class NotificationStackScrollLayoutController implements Dumpable { return; } if (view instanceof ExpandableNotificationRow row) { - mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) - .setType(MetricsEvent.TYPE_ACTION) - ); + StatusBarNotification sbn = NotificationBundleUi.isEnabled() + ? row.getEntryAdapter().getSbn() + : row.getEntryLegacy().getSbn(); + if (sbn != null) { + mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); + } } mNotificationGutsManager.openGuts(view, x, y, item); } @@ -440,9 +445,14 @@ public class NotificationStackScrollLayoutController implements Dumpable { @Override public void onMenuShown(View row) { if (row instanceof ExpandableNotificationRow notificationRow) { - mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) - .setType(MetricsEvent.TYPE_ACTION)); + StatusBarNotification sbn = NotificationBundleUi.isEnabled() + ? notificationRow.getEntryAdapter().getSbn() + : notificationRow.getEntryLegacy().getSbn(); + if (sbn != null) { + mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); + } mSwipeHelper.onMenuShown(row); mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, @@ -1355,11 +1365,15 @@ public class NotificationStackScrollLayoutController implements Dumpable { */ public void setBlurRadius(float blurRadius) { if (blurRadius > 0.0f) { + debugLog( + "Setting blur RenderEffect for NotificationStackScrollLayoutController with " + + "radius " + blurRadius); mView.setRenderEffect(RenderEffect.createBlurEffect( blurRadius, blurRadius, Shader.TileMode.CLAMP)); } else { + debugLog("Resetting blur RenderEffect for NotificationStackScrollLayoutController"); mView.setRenderEffect(null); } } @@ -2175,4 +2189,10 @@ public class NotificationStackScrollLayoutController implements Dumpable { && !mSwipeHelper.isSwiping(); } } + + private void debugLog(String msg) { + if (DEBUG) { + Log.d(TAG, msg); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 8a447f7395d4..60f1777e80bc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -64,6 +64,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationChannelHelper; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -618,6 +619,28 @@ public class BubblesManager { } /** + * When a notification is set as important, make it a bubble + * + * @param entryAdapter the important notification. + */ + public void onUserSetImportantConversation(EntryAdapter entryAdapter) { + if (entryAdapter.getSbn() != null + && entryAdapter.getSbn().getNotification().getBubbleMetadata() == null) { + // No bubble metadata, nothing to do. + return; + } + try { + int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; + mBarService.onNotificationBubbleChanged(entryAdapter.getKey(), true, flags); + } catch (RemoteException e) { + Log.e(TAG, e.getMessage()); + } + mShadeController.collapseShade(true); + // NotificationGutsManager will refresh the ENR when the guts close and update the + // bubble button if needed + } + + /** * Called when a user has indicated that an active notification should be shown as a bubble. * <p> * This method will collapse the shade, create the bubble without a flyout or dot, and suppress diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java index 798aa428e73e..eb72acc0dade 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java @@ -109,6 +109,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; @SmallTest @RunWith(ParameterizedAndroidJunit4.class) @@ -151,8 +152,10 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { private MediaDevice mMediaDevice1; @Mock private MediaDevice mMediaDevice2; - @Mock - private NearbyDevice mNearbyDevice1; + @Mock private MediaDevice mMediaDevice3; + @Mock private MediaDevice mMediaDevice4; + @Mock private MediaDevice mMediaDevice5; + @Mock private NearbyDevice mNearbyDevice1; @Mock private NearbyDevice mNearbyDevice2; @Mock @@ -1550,6 +1553,89 @@ public class MediaSwitchingControllerTest extends SysuiTestCase { assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice2); } + @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) + @Test + public void selectedDevicesAddedInSameOrderWhenRlpDoesNotExist() { + setUpSelectedDevicesAndOrdering(); + + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + + List<MediaDevice> devices = + mMediaSwitchingController.getMediaItemList().stream() + .filter(item -> item.getMediaDevice().isPresent()) + .map(item -> item.getMediaDevice().orElse(null)) + .collect(Collectors.toList()); + assertThat(devices) + .containsExactly( + mMediaDevice4, + mMediaDevice3, + mMediaDevice5, + mMediaDevice1, + mMediaDevice2) + .inOrder(); + } + + @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) + @Test + public void selectedDevicesAddedInSortedOrderWhenRlpDoesNotExist() { + setUpSelectedDevicesAndOrdering(); + + mMediaSwitchingController.onDeviceListUpdate(mMediaDevices); + + List<MediaDevice> devices = + mMediaSwitchingController.getMediaItemList().stream() + .filter(item -> item.getMediaDevice().isPresent()) + .map(item -> item.getMediaDevice().orElse(null)) + .collect(Collectors.toList()); + + assertThat(devices) + .containsExactly( + mMediaDevice5, + mMediaDevice4, + mMediaDevice3, + mMediaDevice1, + mMediaDevice2) + .inOrder(); + } + + private void setUpSelectedDevicesAndOrdering() { + when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID); + when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID); + when(mMediaDevice3.getId()).thenReturn(TEST_DEVICE_3_ID); + when(mMediaDevice4.getId()).thenReturn(TEST_DEVICE_4_ID); + when(mMediaDevice5.getId()).thenReturn(TEST_DEVICE_5_ID); + mMediaDevices.clear(); + Collections.addAll( + mMediaDevices, + mMediaDevice2, + mMediaDevice1, + mMediaDevice4, + mMediaDevice3, + mMediaDevice5); + List<MediaDevice> selectedMediaDevices = new ArrayList<>(); + Collections.addAll(selectedMediaDevices, mMediaDevice3, mMediaDevice4, mMediaDevice5); + doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice(); + // Sort the media devices in the order they appear in the deviceOrder list + List<MediaDevice> deviceOrder = new ArrayList<>(); + Collections.addAll( + deviceOrder, + mMediaDevice1, + mMediaDevice2, + mMediaDevice3, + mMediaDevice4, + mMediaDevice5); + for (int i = 0; i < deviceOrder.size(); i++) { + for (int j = i + 1; j < deviceOrder.size(); j++) { + when(deviceOrder.get(i).compareTo(deviceOrder.get(j))).thenReturn(-1); + when(deviceOrder.get(j).compareTo(deviceOrder.get(i))).thenReturn(1); + } + } + when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false); + mMediaSwitchingController.start(mCb); + reset(mCb); + mMediaSwitchingController.getMediaItemList().clear(); + } + @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING) @Test public void selectedDevicesAddedInReverseOrder() { 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 bc7ab9d4fe3c..eae23e70027b 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 @@ -78,6 +78,8 @@ import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter; import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; @@ -1023,6 +1025,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mock(NotificationIconStyleProvider.class), mock(VisualStabilityCoordinator.class), mock(NotificationActionClickManager.class), + mock(HighPriorityProvider.class), + mock(HeadsUpManager.class), entry); row.setEntryAdapter(entryAdapter); } else { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java index 3061842c386c..2c800bd87ef5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java @@ -89,8 +89,16 @@ import com.android.systemui.res.R; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SbnBuilder; +import com.android.systemui.statusbar.notification.NotificationActivityStarter; +import com.android.systemui.statusbar.notification.collection.EntryAdapter; +import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; +import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider; import com.android.systemui.wmshell.BubblesManager; import com.android.systemui.wmshell.BubblesTestActivity; @@ -127,6 +135,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { private NotificationChannel mConversationChannel; private StatusBarNotification mSbn; private NotificationEntry mEntry; + private EntryAdapter mEntryAdapter; private StatusBarNotification mBubbleSbn; private NotificationEntry mBubbleEntry; @Mock @@ -228,7 +237,21 @@ public class NotificationConversationInfoTest extends SysuiTestCase { notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, notification, UserHandle.CURRENT, null, 0); - mEntry = new NotificationEntryBuilder().setSbn(mSbn).setShortcutInfo(mShortcutInfo).build(); + mEntry = new NotificationEntryBuilder().setSbn(mSbn).setShortcutInfo(mShortcutInfo) + .updateRanking(rankingBuilder -> { + rankingBuilder.setChannel(mNotificationChannel); + }) + .build(); + mEntryAdapter = new EntryAdapterFactoryImpl( + mock(NotificationActivityStarter.class), + mock(MetricsLogger.class), + mock(PeopleNotificationIdentifier.class), + mock(NotificationIconStyleProvider.class), + mock(VisualStabilityCoordinator.class), + mock(NotificationActionClickManager.class), + mock(HighPriorityProvider.class), + mock(HeadsUpManager.class) + ).create(mEntry); PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, BubblesTestActivity.class), @@ -264,9 +287,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - mBubbleMetadata, + mEntryAdapter, + mEntry.getRanking(), + mSbn, null, null, mIconFactory, @@ -367,9 +391,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, - entry, - mBubbleMetadata, + mEntry, + mEntryAdapter, + mEntry.getRanking(), + mSbn, null, null, mIconFactory, @@ -404,9 +429,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - mBubbleMetadata, + mEntryAdapter, + mEntry.getRanking(), + mSbn, null, (View v, Intent intent) -> { latch.countDown(); @@ -444,9 +470,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - mBubbleMetadata, + mEntryAdapter, + mEntry.getRanking(), + mSbn, (View v, NotificationChannel c, int appUid) -> { assertEquals(mConversationChannel, c); latch.countDown(); @@ -483,9 +510,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - mBubbleMetadata, + mEntryAdapter, + mEntry.getRanking(), + mSbn, (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); @@ -553,6 +581,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(IMPORTANCE_HIGH); mConversationChannel.setImportantConversation(false); mConversationChannel.setAllowBubbles(false); + mSbn.getNotification().setBubbleMetadata(null); mNotificationInfo.bindNotification( mShortcutManager, mMockPackageManager, @@ -561,9 +590,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - null, + mEntryAdapter, + mEntry.getRanking(), + mSbn, null, null, mIconFactory, @@ -583,6 +613,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mConversationChannel.setImportance(IMPORTANCE_HIGH); mConversationChannel.setImportantConversation(false); mConversationChannel.setAllowBubbles(false); + mSbn.getNotification().setBubbleMetadata(null); mNotificationInfo.bindNotification( mShortcutManager, mMockPackageManager, @@ -591,9 +622,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase { mMockINotificationManager, mOnUserInteractionCallback, TEST_PACKAGE_NAME, - mNotificationChannel, mEntry, - null, + mEntryAdapter, + mEntry.getRanking(), + mSbn, null, null, mIconFactory, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt index 10de86644015..49ebc8c83ea7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt @@ -63,13 +63,16 @@ import com.android.systemui.statusbar.NotificationEntryHelper import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.AssistantFeedbackController import com.android.systemui.statusbar.notification.NotificationActivityStarter +import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider +import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor import com.android.systemui.statusbar.notification.row.icon.appIconProvider import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.notificationLockscreenUserManager import com.android.systemui.statusbar.policy.deviceProvisionedController @@ -233,14 +236,22 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong()) executor.runAllReady() verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>()) - verify(headsUpManager).setGutsShown(realRow!!.entry, true) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(true)) + } else { + verify(headsUpManager).setGutsShown(realRow!!.entry, true) + } assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong()) gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false) verify(guts) .closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>()) verify(row, times(1)).setGutsView(any()) executor.runAllReady() - verify(headsUpManager).setGutsShown(realRow.entry, false) + if (NotificationBundleUi.isEnabled) { + verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false)) + } else { + verify(headsUpManager).setGutsShown(realRow!!.entry, false) + } } @Test @@ -385,16 +396,23 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { @Throws(Exception::class) fun testInitializeNotificationInfoView_highPriority() { val notificationInfoView = mock<NotificationInfo>() - val row = spy(helper.createRow()) - val entry = row.entry + val row = createTestNotificationRow() + val entry = row!!.entry NotificationEntryHelper.modifyRanking(entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .setImportance(NotificationManager.IMPORTANCE_HIGH) .build() - whenever(row.canViewBeDismissed()).thenReturn(true) + whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true) + whenever(kosmos.mockHighPriorityProvider.isHighPriority(entry)).thenReturn(true) val statusBarNotification = entry.sbn - gutsManager.initializeNotificationInfo(row, notificationInfoView) + + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( any<PackageManager>(), @@ -405,15 +423,17 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { eq(channelEditorDialogController), eq(packageDemotionInteractor), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), any<UiEventLogger>(), eq(true), eq(false), - eq(true), + eq(false), eq(true), eq(assistantFeedbackController), any<MetricsLogger>(), @@ -425,14 +445,19 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { @Throws(Exception::class) fun testInitializeNotificationInfoView_PassesAlongProvisionedState() { val notificationInfoView = mock<NotificationInfo>() - val row = spy(helper.createRow()) + val row = createTestNotificationRow() NotificationEntryHelper.modifyRanking(row.entry) .setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE) .build() - whenever(row.canViewBeDismissed()).thenReturn(true) val statusBarNotification = row.entry.sbn val entry = row.entry - gutsManager.initializeNotificationInfo(row, notificationInfoView) + + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( any<PackageManager>(), @@ -443,15 +468,17 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { eq(channelEditorDialogController), eq(packageDemotionInteractor), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), any<UiEventLogger>(), eq(true), eq(false), - eq(true), /* wasShownHighPriority */ + eq(false), /* wasShownHighPriority */ eq(false), eq(assistantFeedbackController), any<MetricsLogger>(), @@ -470,7 +497,15 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { whenever(row.canViewBeDismissed()).thenReturn(true) val statusBarNotification = row.entry.sbn val entry = row.entry - gutsManager.initializeNotificationInfo(row, notificationInfoView) + val entryAdapter = kosmos.entryAdapterFactory.create(entry) + row.entryAdapter = entryAdapter + + gutsManager.initializeNotificationInfo( + row, + statusBarNotification, + entry.ranking, + notificationInfoView, + ) verify(notificationInfoView) .bindNotification( any<PackageManager>(), @@ -481,8 +516,10 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { eq(channelEditorDialogController), eq(packageDemotionInteractor), eq(statusBarNotification.packageName), - any<NotificationChannel>(), - eq(entry), + eq(entry.ranking), + eq(statusBarNotification), + if (NotificationBundleUi.isEnabled) eq(null) else eq(entry), + if (NotificationBundleUi.isEnabled) eq(entryAdapter) else eq(null), any<NotificationInfo.OnSettingsClickListener>(), any<NotificationInfo.OnAppSettingsClickListener>(), any<NotificationInfo.OnFeedbackClickListener>(), @@ -497,7 +534,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { ) } - private fun createTestNotificationRow(): ExpandableNotificationRow? { + private fun createTestNotificationRow(): ExpandableNotificationRow { val nb = Notification.Builder(mContext, testNotificationChannel.id) .setContentTitle("foo") @@ -505,16 +542,10 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() { .setColor(Color.RED) .setFlag(Notification.FLAG_CAN_COLORIZE, true) .setSmallIcon(R.drawable.sym_def_app_icon) - return try { - val row = helper.createRow(nb.build()) - NotificationEntryHelper.modifyRanking(row.entry) - .setChannel(testNotificationChannel) - .build() - row - } catch (_: Exception) { - Assert.fail() - null - } + val row = helper.createRow(nb.build()) + NotificationEntryHelper.modifyRanking(row.entry).setChannel(testNotificationChannel).build() + row.entryAdapter = kosmos.entryAdapterFactory.create(row.entry) + return row } private fun setIsLockscreenOrShadeVisible(isVisible: Boolean) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt index 1a5c61a04c9f..aba4942b44e4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.stack.data.repository.headsUp import com.android.systemui.statusbar.notification.visibilityLocationProvider import com.android.systemui.statusbar.policy.keyguardStateController import com.android.systemui.util.kotlin.JavaAdapter +import org.mockito.kotlin.mock var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by Kosmos.Fixture { @@ -54,3 +55,5 @@ var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by visualStabilityCoordinatorLogger, ) } + +var Kosmos.mockVisualStabilityCoordinator: VisualStabilityCoordinator by Kosmos.Fixture { mock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt new file mode 100644 index 000000000000..aa30828d4375 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2025 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.statusbar.notification.collection.provider + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.mockHighPriorityProvider: HighPriorityProvider by Kosmos.Fixture { mock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt index 067e420b89c3..4b699702d54f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.notification.row import com.android.internal.logging.metricsLogger import com.android.systemui.kosmos.Kosmos import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl -import com.android.systemui.statusbar.notification.collection.coordinator.visualStabilityCoordinator +import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator +import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider +import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager import com.android.systemui.statusbar.notification.mockNotificationActivityStarter import com.android.systemui.statusbar.notification.people.peopleNotificationIdentifier import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider @@ -31,7 +33,9 @@ val Kosmos.entryAdapterFactory by metricsLogger, peopleNotificationIdentifier, notificationIconStyleProvider, - visualStabilityCoordinator, + mockVisualStabilityCoordinator, mockNotificationActionClickManager, + mockHighPriorityProvider, + mockHeadsUpManager, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt index 6a674ca29ca4..b6acf477b660 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl @@ -376,6 +377,8 @@ class ExpandableNotificationRowBuilder( Mockito.mock(NotificationIconStyleProvider::class.java), Mockito.mock(VisualStabilityCoordinator::class.java), Mockito.mock(NotificationActionClickManager::class.java), + Mockito.mock(HighPriorityProvider::class.java), + Mockito.mock(HeadsUpManager::class.java), ) .create(entry) diff --git a/ravenwood/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md index 615e7671bea1..9f72611953c2 100644 --- a/ravenwood/tools/hoststubgen/README.md +++ b/ravenwood/tools/hoststubgen/README.md @@ -17,62 +17,3 @@ AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classe - More Android specific build files and code are stored in `frameworks/base/Ravenwood.bp` `frameworks/base/ravenwood`. - -## Directories and files - -- `src/` - - HostStubGen tool source code. - -- `annotations-src/` See `Android.bp`. -- `helper-framework-buildtime-src/` See `Android.bp`. -- `helper-framework-runtime-src/` See `Android.bp`. -- `helper-runtime-src/` See `Android.bp`. - -- `test-tiny-framework/` See `README.md` in it. - -- `scripts` - - `dump-jar.sh` - - A script to dump the content of `*.class` and `*.jar` files. - - - `run-all-tests.sh` - - Run all tests. Many tests may fail, but at least this should run til the end. - (It should print `run-all-tests.sh finished` at the end) - -## Build and run - -### Building `HostStubGen` binary - -``` -m hoststubgen -``` - -### Run the tests - -- Run all relevant tests and test scripts. All of it is expected to pass, and it'll print - "Ready to submit" at the end. - - However, because some of the script it executes depend on internal file paths to Soong's - intermediate directory, some of it might fail when something changes in the build system. - - We need proper build system integration to fix them. -``` -$ ./scripts/run-all-tests.sh -``` - -- See also `README.md` in `test-*` directories. - -## TODOs, etc - - - Make sure the parent's visibility is not smaller than the member's. - -- @HostSideTestNativeSubstitutionClass should automatically add class-keep to the substitute class. - (or at least check it.) - - - The `HostStubGenTest-framework-test-host-test-lib` jar somehow contain all ASM classes? Figure out where the dependency is coming from. - -- At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`. - - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood` - - `test-framework` should be deleted. diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt index f8bb526d0a86..760999f5e129 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt @@ -60,7 +60,7 @@ class ClassWidePolicyPropagatingFilter( } return p.withReason(policy.reason) - .wrapReason("class-wide in $className") + .wrapReason("class-wide in $className", policy.statsLabelOverride) } // If the class's policy is remove, then remove it. if (policy.policy == FilterPolicy.Remove) { diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt index 7358a0bfb3e6..e082bbb0a119 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt @@ -41,7 +41,7 @@ enum class StatsLabel(val statValue: Int, val label: String) { data class FilterPolicyWithReason ( val policy: FilterPolicy, val reason: String = "", - private val statsLabelOverride: StatsLabel? = null + val statsLabelOverride: StatsLabel? = null ) { /** * Return a new [FilterPolicy] with an updated reason, while keeping the original reason @@ -51,7 +51,7 @@ data class FilterPolicyWithReason ( return FilterPolicyWithReason( policy, "$reason [inner-reason: ${this.reason}]", - statsLabelOverride = statsLabelOverride, + statsLabelOverride = statsLabelOverride ?: this.statsLabelOverride, ) } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt index 97fc35302528..cdcea4c15820 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt @@ -528,7 +528,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class AIDL)" + "$FILTER_REASON (special-class AIDL)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) aidlPolicy = p @@ -541,7 +542,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class feature flags)" + "$FILTER_REASON (special-class feature flags)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) featureFlagsPolicy = p @@ -554,7 +556,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class sysprops)" + "$FILTER_REASON (special-class sysprops)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) syspropsPolicy = p @@ -567,7 +570,8 @@ class TextFileFilterPolicyParser { ) } val p = policy.withReason( - "$FILTER_REASON (special-class R file)" + "$FILTER_REASON (special-class R file)", + StatsLabel.SupportedButBoring, ) processor.onSpecialClassPolicy(classType, p) rFilePolicy = p diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md deleted file mode 100644 index 344b4e953b23..000000000000 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# HostStubGen: tiny-framework test - -This directory contains a small classes that "simulates" framework.jar, and tests against it. - -This test is agnostic to Android, and it doesn't use any android framework code or knowledge. - -## How to run - -- With `atest`. This is the proper way to run it, but `atest` has known problems that may - affect the result. If you see weird problems, try the next `run-ravenwood-test` command. - -``` -$ atest hoststubgen-test-tiny-test -``` - -- `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without - using the build system. This is useful for debugging the tool. - -``` -$ ./run-test-manually.sh -```
\ No newline at end of file diff --git a/services/Android.bp b/services/Android.bp index efd35ce8f1a3..8657bfc79316 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -181,7 +181,7 @@ art_profile_java_defaults { conditions_default: { dex_preopt: { app_image: true, - profile: "art-profile", + profile: ":art-profile-combined", }, }, }, @@ -391,9 +391,14 @@ platform_compat_config { src: ":services", } -filegroup { - name: "art-profile", - srcs: ["art-profile"], +genrule { + name: "art-profile-combined", + srcs: [ + "art-profile", + "art-profile-extra", + ], + out: ["art-profile-combined"], + cmd: "cat $(location art-profile) $(location art-profile-extra) > $(location art-profile-combined)", } // API stub diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java index edcf5748a8fc..8cf94b464a1a 100644 --- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java @@ -160,24 +160,16 @@ public class HearingDevicePhoneCallNotificationController { mHearingDevice = null; } if (state == TelephonyManager.CALL_STATE_OFFHOOK) { - if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) { - AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice(); - if (commDevice == null) { - return; - } - mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice)); - if (mHearingDevice != null) { - showNotificationIfNeeded(); - } else { - addOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedExecutor, - mCommDeviceChangedListener); - } + AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice(); + if (commDevice == null) { + return; + } + mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice)); + if (mHearingDevice != null) { + showNotificationIfNeeded(); } else { - mHearingDevice = getSupportedInputHearingDeviceInfo( - mAudioManager.getAvailableCommunicationDevices()); - if (mHearingDevice != null) { - showNotificationIfNeeded(); - } + addOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedExecutor, + mCommDeviceChangedListener); } } } diff --git a/services/art-profile-extra b/services/art-profile-extra new file mode 100644 index 000000000000..54362411e5ea --- /dev/null +++ b/services/art-profile-extra @@ -0,0 +1 @@ +HSPLcom/android/server/am/ActivityManagerService$LocalService;->checkContentProviderAccess(Ljava/lang/String;I)Ljava/lang/String; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 5395d2a914ec..c15915ba39a4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -790,7 +790,7 @@ public final class ActiveServices { "SHORT_FGS_TIMEOUT"); this.mServiceFGAnrTimer = new ServiceAnrTimer(service, ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, - "SERVICE_FOREGROUND_TIMEOUT"); + "SERVICE_FOREGROUND_TIMEOUT", new AnrTimer.Args().extend(true)); } void systemServicesReady() { @@ -7702,6 +7702,11 @@ public final class ActiveServices { super(Objects.requireNonNull(am).mHandler, msg, label); } + ServiceAnrTimer(ActivityManagerService am, int msg, String label, + @NonNull AnrTimer.Args args) { + super(Objects.requireNonNull(am).mHandler, msg, label, args); + } + @Override public int getPid(@NonNull ServiceRecord service) { return (service.app != null) ? service.app.getPid() : 0; diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java index b4df1f76dccb..569a426b80d5 100644 --- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java +++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java @@ -111,13 +111,14 @@ class DisplayTopologyCoordinator { * @param info The display info */ void onDisplayAdded(DisplayInfo info) { - if (!isDisplayAllowedInTopology(info)) { + if (!isDisplayAllowedInTopology(info, /* shouldLog= */ true)) { return; } synchronized (mSyncRoot) { addDisplayIdMappingLocked(info); mDensities.put(info.displayId, info.logicalDensityDpi); mTopology.addDisplay(info.displayId, getWidth(info), getHeight(info)); + Slog.i(TAG, "Display " + info.displayId + " added, new topology: " + mTopology); restoreTopologyLocked(); sendTopologyUpdateLocked(); } @@ -128,7 +129,7 @@ class DisplayTopologyCoordinator { * @param info The new display info */ void onDisplayChanged(DisplayInfo info) { - if (!isDisplayAllowedInTopology(info)) { + if (!isDisplayAllowedInTopology(info, /* shouldLog= */ false)) { return; } synchronized (mSyncRoot) { @@ -149,6 +150,7 @@ class DisplayTopologyCoordinator { synchronized (mSyncRoot) { mDensities.delete(displayId); if (mTopology.removeDisplay(displayId)) { + Slog.i(TAG, "Display " + displayId + " removed, new topology: " + mTopology); removeDisplayIdMappingLocked(displayId); restoreTopologyLocked(); sendTopologyUpdateLocked(); @@ -249,22 +251,28 @@ class DisplayTopologyCoordinator { return pxToDp(info.logicalHeight, info.logicalDensityDpi); } - private boolean isDisplayAllowedInTopology(DisplayInfo info) { + private boolean isDisplayAllowedInTopology(DisplayInfo info, boolean shouldLog) { if (info.type != Display.TYPE_INTERNAL && info.type != Display.TYPE_EXTERNAL && info.type != Display.TYPE_OVERLAY) { - Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " - + "type is not INTERNAL, EXTERNAL or OVERLAY"); + if (shouldLog) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "type is not INTERNAL, EXTERNAL or OVERLAY"); + } return false; } if (info.type == Display.TYPE_INTERNAL && info.displayId != Display.DEFAULT_DISPLAY) { - Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " - + "it is a non-default internal display"); + if (shouldLog) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "it is a non-default internal display"); + } return false; } if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY) && !mIsExtendedDisplayAllowed.getAsBoolean()) { - Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " - + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed"); + if (shouldLog) { + Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because " + + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed"); + } return false; } return true; diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 60b7fca99e7b..228e6f1c4ddb 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -130,6 +130,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter { private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE = "fixed_content_mode"; + /** + * When this flag is set, disables support for moving and resizing the overlay window. + * As the window is made non-touchable, this also makes it possible to directly interact with + * the content underneath. + */ + private static final String OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION = + "disable_window_interaction"; + // Gravity flags to decide where the overlay should be shown. private static final String GRAVITY_TOP_LEFT = "gravity_top_left"; private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right"; @@ -571,9 +579,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { @Override public void run() { OverlayMode mode = mModes.get(mActiveMode); - OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), - mName, mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, - mFlags.mSecure, OverlayDisplayHandle.this); + OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), mName, + mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, mFlags.mSecure, + mFlags.mDisableWindowInteraction, OverlayDisplayHandle.this); window.show(); synchronized (getSyncRoot()) { @@ -655,6 +663,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { /** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */ final boolean mFixedContentMode; + /** See {@link #OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION}. */ + final boolean mDisableWindowInteraction; + final int mGravity; OverlayFlags( @@ -662,11 +673,13 @@ final class OverlayDisplayAdapter extends DisplayAdapter { boolean ownContentOnly, boolean shouldShowSystemDecorations, boolean fixedContentMode, + boolean disableWindowInteraction, int gravity) { mSecure = secure; mOwnContentOnly = ownContentOnly; mShouldShowSystemDecorations = shouldShowSystemDecorations; mFixedContentMode = fixedContentMode; + mDisableWindowInteraction = disableWindowInteraction; mGravity = gravity; } @@ -677,6 +690,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { false /* ownContentOnly */, false /* shouldShowSystemDecorations */, false /* fixedContentMode */, + false /* disableWindowInteraction */, Gravity.NO_GRAVITY); } @@ -684,6 +698,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { boolean ownContentOnly = false; boolean shouldShowSystemDecorations = false; boolean fixedContentMode = false; + boolean disableWindowInteraction = false; int gravity = Gravity.NO_GRAVITY; for (String flag: flagString.split(FLAG_SPLITTER)) { if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) { @@ -694,12 +709,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter { shouldShowSystemDecorations = true; } else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) { fixedContentMode = true; + } else if (OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION.equals(flag)) { + disableWindowInteraction = true; } else { gravity = parseOverlayGravity(flag); } } return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, - fixedContentMode, gravity); + fixedContentMode, disableWindowInteraction, gravity); } @Override @@ -709,6 +726,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter { .append(", ownContentOnly=").append(mOwnContentOnly) .append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations) .append(", fixedContentMode=").append(mFixedContentMode) + .append(", disableWindowInteraction=").append(mDisableWindowInteraction) .append(", gravity").append(Gravity.toString(mGravity)) .append("}") .toString(); diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index 3fd58e8641c3..523bbfa7d69a 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -69,6 +69,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { private int mDensityDpi; private final int mGravity; private final boolean mSecure; + private final boolean mDisableWindowInteraction; private final Listener mListener; private String mTitle; @@ -96,15 +97,15 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { private float mLiveTranslationY; private float mLiveScale = 1.0f; - public OverlayDisplayWindow(Context context, String name, - int width, int height, int densityDpi, int gravity, boolean secure, - Listener listener) { + OverlayDisplayWindow(Context context, String name, int width, int height, int densityDpi, + int gravity, boolean secure, boolean disableWindowInteraction, Listener listener) { // Workaround device freeze (b/38372997) ThreadedRenderer.disableVsync(); mContext = context; mName = name; mGravity = gravity; mSecure = secure; + mDisableWindowInteraction = disableWindowInteraction; mListener = listener; mDisplayManager = (DisplayManager)context.getSystemService( @@ -226,8 +227,10 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { if (mSecure) { mWindowParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; } - if (DISABLE_MOVE_AND_RESIZE) { + if (DISABLE_MOVE_AND_RESIZE || mDisableWindowInteraction) { mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; + mWindowParams.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; } mWindowParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED; diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 52ddb800fa40..695bf612ccc3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -82,4 +82,11 @@ public interface NotificationManagerInternal { byte[] getBackupPayload(int user, BackupRestoreEventLogger logger); void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger); + + /** + * Notifies NotificationManager that the system decorations should be removed from the display. + * + * @param displayId display ID + */ + void onDisplayRemoveSystemDecorations(int displayId); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 06fc9b083086..6ce1746ed3f6 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8214,6 +8214,18 @@ public class NotificationManagerService extends SystemService { // This can also throw IllegalStateException if called too late. mZenModeHelper.setDeviceEffectsApplier(applier); } + + @Override + public void onDisplayRemoveSystemDecorations(int displayId) { + synchronized (mToastQueue) { + for (int i = mToastQueue.size() - 1; i >= 0; i--) { + final ToastRecord toast = mToastQueue.get(i); + if (toast.displayId == displayId) { + cancelToastLocked(i); + } + } + } + } }; private static boolean isBigPictureWithBitmapOrIcon(Notification n) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e9f522d08328..8cf0481b1dc3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -4633,6 +4633,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { @SuppressLint("MissingPermission") private void injectBackGesture(long downtime) { + if (mActivityTaskManagerInternal.requestBackGesture()) { + return; + } // Create and inject down event KeyEvent downEvent = new KeyEvent(downtime, downtime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 3eac4b54cd2b..bef505867a14 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -130,12 +130,12 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; import com.android.server.LockGuard; +import com.android.server.PackageWatchdog; import com.android.server.ServiceThread; import com.android.server.SystemService; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.BatteryStatsService; -import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; @@ -4113,7 +4113,7 @@ public final class PowerManagerService extends SystemService } } if (mHandler == null || !mSystemReady) { - if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) { + if (PackageWatchdog.isRecoveryTriggeredReboot()) { // If we're stuck in a really low-level reboot loop, and a // rescue party is trying to prompt the user for a factory data // reset, we must GET TO DA CHOPPA! diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java index d209ea90f3ca..4ae1b4e119fa 100644 --- a/services/core/java/com/android/server/power/ShutdownThread.java +++ b/services/core/java/com/android/server/power/ShutdownThread.java @@ -59,7 +59,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.crashrecovery.CrashRecoveryHelper; +import com.android.server.PackageWatchdog; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; @@ -339,7 +339,7 @@ public final class ShutdownThread extends Thread { com.android.internal.R.string.reboot_to_update_reboot)); } } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) { - if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) { + if (PackageWatchdog.isRecoveryTriggeredReboot()) { // We're not actually doing a factory reset yet; we're rebooting // to ask the user if they'd like to reset, so give them a less // scary dialog message. diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 5ee9b7d09fdd..46c497d04f9e 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -176,7 +176,9 @@ public class ThermalManagerService extends SystemService { try { final HeadroomCallbackData data; synchronized (mTemperatureWatcher.mSamples) { - Slog.d(TAG, "Updating skin threshold: " + threshold); + if (DEBUG) { + Slog.d(TAG, "Updating skin threshold: " + threshold); + } mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true); data = mTemperatureWatcher.getHeadroomCallbackDataLocked(); } @@ -454,7 +456,9 @@ public class ThermalManagerService extends SystemService { && temperature.getType() == Temperature.TYPE_SKIN) { final HeadroomCallbackData data; synchronized (mTemperatureWatcher.mSamples) { - Slog.d(TAG, "Updating new temperature: " + temperature); + if (DEBUG) { + Slog.d(TAG, "Updating new temperature: " + temperature); + } mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(), temperature); mTemperatureWatcher.mCachedHeadrooms.clear(); @@ -1878,6 +1882,7 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS; + @GuardedBy("mSamples") private final Handler mHandler = BackgroundThread.getHandler(); /** @@ -1900,6 +1905,9 @@ public class ThermalManagerService extends SystemService { @GuardedBy("mSamples") private long mLastForecastCallTimeMillis = 0; + private final Runnable mGetAndUpdateTemperatureSamplesRunnable = + this::getAndUpdateTemperatureSamples; + void getAndUpdateThresholds() { List<TemperatureThreshold> thresholds = mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN); @@ -1930,7 +1938,9 @@ public class ThermalManagerService extends SystemService { return; } if (override) { - Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold); + if (DEBUG) { + Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold); + } mCachedHeadrooms.clear(); Arrays.fill(mHeadroomThresholds, Float.NaN); } @@ -1962,7 +1972,7 @@ public class ThermalManagerService extends SystemService { < mInactivityThresholdMillis) { // Trigger this again after a second as long as forecast has been called more // recently than the inactivity timeout - mHandler.postDelayed(this::getAndUpdateTemperatureSamples, 1000); + mHandler.postDelayed(mGetAndUpdateTemperatureSamplesRunnable, 1000); } else { // Otherwise, we've been idle for at least 10 seconds, so we should // shut down @@ -1974,6 +1984,9 @@ public class ThermalManagerService extends SystemService { long now = SystemClock.elapsedRealtime(); final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true, Temperature.TYPE_SKIN); + if (DEBUG) { + Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures); + } for (Temperature temperature : temperatures) { updateTemperatureSampleLocked(now, temperature); } @@ -2080,10 +2093,16 @@ public class ThermalManagerService extends SystemService { } synchronized (mSamples) { mLastForecastCallTimeMillis = SystemClock.elapsedRealtime(); - if (mSamples.isEmpty()) { + if (!mHandler.hasCallbacks(mGetAndUpdateTemperatureSamplesRunnable)) { + if (DEBUG) { + Slog.d(TAG, "No temperature update callback, scheduling one"); + } getAndUpdateTemperatureSamples(); + } else { + if (DEBUG) { + Slog.d(TAG, "Temperature update callback already exists"); + } } - // If somehow things take much longer than expected or there are no temperatures // to sample, return early if (mSamples.isEmpty()) { @@ -2103,8 +2122,11 @@ public class ThermalManagerService extends SystemService { Binder.getCallingUid(), FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS, headroom, forecastSeconds); - Slog.d(TAG, "Headroom forecast in " + forecastSeconds + "s served from cache: " - + headroom); + if (DEBUG) { + Slog.d(TAG, + "Headroom forecast in " + forecastSeconds + "s served from cache: " + + headroom); + } return headroom; } @@ -2133,7 +2155,10 @@ public class ThermalManagerService extends SystemService { Binder.getCallingUid(), FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS, headroom, 0); - Slog.d(TAG, "Headroom forecast in 0s served from cache: " + headroom); + if (DEBUG) { + Slog.d(TAG, + "Headroom forecast in 0s served from cache: " + headroom); + } return headroom; } // Don't try to forecast, just use the latest one we have @@ -2182,7 +2207,9 @@ public class ThermalManagerService extends SystemService { getForecast(DEFAULT_FORECAST_SECONDS), DEFAULT_FORECAST_SECONDS, Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length)); - Slog.d(TAG, "New headroom callback data: " + data); + if (DEBUG) { + Slog.d(TAG, "New headroom callback data: " + data); + } return data; } diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java index 8c3b7c606f04..3ece07c84080 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -17,6 +17,7 @@ package com.android.server.security.advancedprotection; import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; +import static android.provider.Settings.Secure.AAPM_USB_DATA_PROTECTION; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import android.Manifest; @@ -69,6 +70,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** @hide */ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub { @@ -129,7 +131,10 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub Slog.e(TAG, "Failed to initialize DisallowCellular2g", e); } } - if (android.security.Flags.aapmFeatureUsbDataProtection()) { + if (android.security.Flags.aapmFeatureUsbDataProtection() + // Usb data protection is enabled by default + && mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) + == AdvancedProtectionStore.ON) { try { mHooks.add(new UsbDataAdvancedProtectionHook(mContext, enabled)); } catch (Exception e) { @@ -183,7 +188,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub // Without permission check private boolean isAdvancedProtectionEnabledInternal() { - return mStore.retrieve(); + return mStore.retrieveAdvancedProtectionModeEnabled(); } @Override @@ -217,7 +222,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub try { synchronized (mCallbacks) { if (enabled != isAdvancedProtectionEnabledInternal()) { - mStore.store(enabled); + mStore.storeAdvancedProtectionModeEnabled(enabled); sendModeChanged(enabled); logAdvancedProtectionEnabled(enabled); } @@ -227,6 +232,34 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub } } + public void setUsbDataProtectionEnabled(boolean enabled) { + int value = enabled ? AdvancedProtectionStore.ON + : AdvancedProtectionStore.OFF; + setAdvancedProtectionSubSettingInt(AAPM_USB_DATA_PROTECTION, value); + } + + private void setAdvancedProtectionSubSettingInt(String key, int value) { + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mCallbacks) { + mStore.storeInt(key, value); + Slog.i(TAG, "Advanced protection: subsetting" + key + " is " + value); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + public boolean isUsbDataProtectionEnabled() { + final long identity = Binder.clearCallingIdentity(); + try { + return mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON) + == AdvancedProtectionStore.ON; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + @Override @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type, @@ -419,8 +452,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub @VisibleForTesting static class AdvancedProtectionStore { private final Context mContext; - private static final int APM_ON = 1; - private static final int APM_OFF = 0; + static final int ON = 1; + static final int OFF = 0; private final UserManagerInternal mUserManager; AdvancedProtectionStore(@NonNull Context context) { @@ -428,15 +461,26 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub mUserManager = LocalServices.getService(UserManagerInternal.class); } - void store(boolean enabled) { + void storeAdvancedProtectionModeEnabled(boolean enabled) { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, enabled ? ON : OFF, + mUserManager.getMainUserId()); + } + + boolean retrieveAdvancedProtectionModeEnabled() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + ADVANCED_PROTECTION_MODE, OFF, mUserManager.getMainUserId()) == ON; + } + + void storeInt(String key, int value) { Settings.Secure.putIntForUser(mContext.getContentResolver(), - ADVANCED_PROTECTION_MODE, enabled ? APM_ON : APM_OFF, + key, value, mUserManager.getMainUserId()); } - boolean retrieve() { + int retrieveInt(String key, int defaultValue) { return Settings.Secure.getIntForUser(mContext.getContentResolver(), - ADVANCED_PROTECTION_MODE, APM_OFF, mUserManager.getMainUserId()) == APM_ON; + key, defaultValue, mUserManager.getMainUserId()); } } diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java index 42505ad2de3f..ae17a459010b 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java @@ -45,6 +45,10 @@ class AdvancedProtectionShellCommand extends ShellCommand { return setProtectionEnabled(); case "is-protection-enabled": return isProtectionEnabled(pw); + case "set-usb-data-protection-enabled": + return setUsbDataProtectedEnabled(); + case "is-usb-data-protection-enabled": + return isUsbDataProtectedEnabled(pw); } } catch (RemoteException e) { pw.println("Remote exception: " + e); @@ -64,6 +68,10 @@ class AdvancedProtectionShellCommand extends ShellCommand { pw.println(" Print this help text."); pw.println(" set-protection-enabled [true|false]"); pw.println(" is-protection-enabled"); + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + pw.println(" set-usb-data-protection-enabled [true|false]"); + pw.println(" is-usb-data-protection-enabled"); + } } @SuppressLint("AndroidFrameworkRequiresPermission") @@ -79,4 +87,22 @@ class AdvancedProtectionShellCommand extends ShellCommand { pw.println(protectionMode); return 0; } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int setUsbDataProtectedEnabled() throws RemoteException { + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + String protectionMode = getNextArgRequired(); + mService.setUsbDataProtectionEnabled(Boolean.parseBoolean(protectionMode)); + } + return 0; + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private int isUsbDataProtectedEnabled(@NonNull PrintWriter pw) throws RemoteException { + if(android.security.Flags.aapmFeatureUsbDataProtection()) { + boolean protectionMode = mService.isUsbDataProtectionEnabled(); + pw.println(protectionMode); + } + return 0; + } } diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index 70fc6bace868..ee173a2a6a35 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -131,6 +131,10 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, mCache = cache; } + void setSnapshotReleaser(Consumer<HardwareBuffer> releaser) { + mCache.setSafeSnapshotReleaser(releaser); + } + void setSnapshotEnabled(boolean enabled) { mSnapshotEnabled = enabled; } diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java index ed07afd2eab5..a0e537231071 100644 --- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java +++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java @@ -31,6 +31,7 @@ class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> { void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) { final int hasCode = System.identityHashCode(ar); snapshot.addReference(TaskSnapshot.REFERENCE_CACHE); + snapshot.setSafeRelease(mSafeSnapshotReleaser); synchronized (mLock) { final CacheEntry entry = mRunningCache.get(hasCode); if (entry != null) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 21b730e13585..7123a7c1160f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -803,4 +803,10 @@ public abstract class ActivityTaskManagerInternal { /** Returns whether assist data is allowed. */ public abstract boolean isAssistDataAllowed(); + + /** + * Delegate back gesture request from shell. + * Returns true if the back gesture request was successful, false otherwise. + */ + public abstract boolean requestBackGesture(); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 46d24b0d3201..a0c38dd82037 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1892,6 +1892,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void registerBackGestureDelegate(RemoteCallback requestObserver) { + mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, + "registerBackGestureDelegate()"); + final long origId = Binder.clearCallingIdentity(); + try { + mBackNavigationController.registerBackGestureDelegate(requestObserver); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + /** * Public API to check if the client is allowed to start an activity on specified display. * @@ -7571,6 +7583,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean isAssistDataAllowed() { return ActivityTaskManagerService.this.isAssistDataAllowed(); } + + @Override + public boolean requestBackGesture() { + return mBackNavigationController.requestBackGesture(); + } } /** Cache the return value for {@link #isPip2ExperimentEnabled()} */ diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index dfe323c43abb..819e4abaa9d3 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -85,6 +85,7 @@ class BackNavigationController { private boolean mShowWallpaper; private Runnable mPendingAnimation; private final NavigationMonitor mNavigationMonitor = new NavigationMonitor(); + private RemoteCallback mGestureRequest; private AnimationHandler mAnimationHandler; @@ -113,6 +114,35 @@ class BackNavigationController { mNavigationMonitor.onEmbeddedWindowGestureTransferred(host); } + void registerBackGestureDelegate(@NonNull RemoteCallback requestObserver) { + if (!sPredictBackEnable) { + return; + } + synchronized (mWindowManagerService.mGlobalLock) { + mGestureRequest = requestObserver; + try { + requestObserver.getInterface().asBinder().linkToDeath(() -> { + synchronized (mWindowManagerService.mGlobalLock) { + mGestureRequest = null; + } + }, 0 /* flags */); + } catch (RemoteException r) { + Slog.e(TAG, "Failed to link to death"); + mGestureRequest = null; + } + } + } + + boolean requestBackGesture() { + synchronized (mWindowManagerService.mGlobalLock) { + if (mGestureRequest == null) { + return false; + } + mGestureRequest.sendResult(null); + return true; + } + } + /** * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming * back gesture animation. diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 313b77ed4b20..f9eb0574d87c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -137,6 +137,7 @@ import com.android.internal.view.AppearanceRegion; import com.android.internal.widget.PointerLocationView; import com.android.server.LocalServices; import com.android.server.UiThread; +import com.android.server.notification.NotificationManagerInternal; import com.android.server.policy.WindowManagerPolicy.ScreenOnListener; import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs; import com.android.server.statusbar.StatusBarManagerInternal; @@ -1925,6 +1926,11 @@ public class DisplayPolicy { if (wpMgr != null) { wpMgr.onDisplayRemoveSystemDecorations(displayId); } + final NotificationManagerInternal notificationManager = + LocalServices.getService(NotificationManagerInternal.class); + if (notificationManager != null) { + notificationManager.onDisplayRemoveSystemDecorations(displayId); + } }); } diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java index 9812a88bfa5a..685bd3b355ed 100644 --- a/services/core/java/com/android/server/wm/SnapshotCache.java +++ b/services/core/java/com/android/server/wm/SnapshotCache.java @@ -16,12 +16,14 @@ package com.android.server.wm; import android.annotation.Nullable; +import android.hardware.HardwareBuffer; import android.util.ArrayMap; import android.window.TaskSnapshot; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; +import java.util.function.Consumer; /** * Base class for an app snapshot cache @@ -38,12 +40,18 @@ abstract class SnapshotCache<TYPE extends WindowContainer> { @GuardedBy("mLock") protected final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>(); + protected Consumer<HardwareBuffer> mSafeSnapshotReleaser; + SnapshotCache(String name) { mName = name; } abstract void putSnapshot(TYPE window, TaskSnapshot snapshot); + void setSafeSnapshotReleaser(Consumer<HardwareBuffer> safeSnapshotReleaser) { + mSafeSnapshotReleaser = safeSnapshotReleaser; + } + void clearRunningCache() { synchronized (mLock) { mRunningCache.clear(); diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java index 3a7222ae6d51..48f0959214c1 100644 --- a/services/core/java/com/android/server/wm/SnapshotController.java +++ b/services/core/java/com/android/server/wm/SnapshotController.java @@ -26,13 +26,16 @@ import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import android.hardware.HardwareBuffer; import android.os.Trace; import android.util.ArrayMap; import android.view.WindowManager; import android.window.TaskSnapshot; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.function.Consumer; /** * Integrates common functionality from TaskSnapshotController and ActivitySnapshotController. @@ -41,11 +44,30 @@ class SnapshotController { private final SnapshotPersistQueue mSnapshotPersistQueue; final TaskSnapshotController mTaskSnapshotController; final ActivitySnapshotController mActivitySnapshotController; + private final WindowManagerService mService; + private final ArrayList<WeakReference<HardwareBuffer>> mObsoleteSnapshots = new ArrayList<>(); SnapshotController(WindowManagerService wms) { + mService = wms; mSnapshotPersistQueue = new SnapshotPersistQueue(); mTaskSnapshotController = new TaskSnapshotController(wms, mSnapshotPersistQueue); mActivitySnapshotController = new ActivitySnapshotController(wms, mSnapshotPersistQueue); + final Consumer<HardwareBuffer> releaser = hb -> { + mService.mH.post(() -> { + synchronized (mService.mGlobalLock) { + if (hb.isClosed()) { + return; + } + if (mService.mAtmService.getTransitionController().inTransition()) { + mObsoleteSnapshots.add(new WeakReference<>(hb)); + } else { + hb.close(); + } + } + }); + }; + mTaskSnapshotController.setSnapshotReleaser(releaser); + mActivitySnapshotController.setSnapshotReleaser(releaser); } void systemReady() { @@ -168,6 +190,7 @@ class SnapshotController { final boolean isTransitionClose = isTransitionClose(type); if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM || (changeInfos.isEmpty())) { + closeObsoleteSnapshots(); return; } Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SnapshotController_analysis"); @@ -195,9 +218,22 @@ class SnapshotController { } } } + closeObsoleteSnapshots(); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + private void closeObsoleteSnapshots() { + if (mObsoleteSnapshots.isEmpty()) { + return; + } + for (int i = mObsoleteSnapshots.size() - 1; i >= 0; --i) { + final HardwareBuffer hb = mObsoleteSnapshots.remove(i).get(); + if (hb != null && !hb.isClosed()) { + hb.close(); + } + } + } + private static boolean isTransitionOpen(int type) { return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT || type == TRANSIT_PREPARE_BACK_NAVIGATION; diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java index cc957bd9ee42..1988f2630b6e 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java @@ -36,6 +36,7 @@ class TaskSnapshotCache extends SnapshotCache<Task> { void putSnapshot(Task task, TaskSnapshot snapshot) { synchronized (mLock) { snapshot.addReference(TaskSnapshot.REFERENCE_CACHE); + snapshot.setSafeRelease(mSafeSnapshotReleaser); final CacheEntry entry = mRunningCache.get(task.mTaskId); if (entry != null) { mAppIdMap.remove(entry.topApp); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java index 33529c3f4375..44870eb5dd49 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java @@ -37,7 +37,6 @@ import android.content.Context; import android.media.AudioDeviceInfo; import android.media.AudioDevicePort; import android.media.AudioManager; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; @@ -164,7 +163,6 @@ public class HearingDevicePhoneCallNotificationControllerTest { } @Test - @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE) public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() { final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor = ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class); @@ -185,7 +183,6 @@ public class HearingDevicePhoneCallNotificationControllerTest { @Test - @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE) public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() { final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor = ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class); @@ -209,7 +206,6 @@ public class HearingDevicePhoneCallNotificationControllerTest { } @Test - @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE) public void onCallStateChanged_offHookMultiple_addListenerOnlyOneTime() { AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS, AudioManager.DEVICE_OUT_BLUETOOTH_A2DP); diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 952d8fa47a34..09acfddacf03 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -51,6 +51,7 @@ import android.os.IThermalStatusListener; import android.os.PowerManager; import android.os.RemoteException; import android.os.Temperature; +import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; @@ -78,6 +79,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server @@ -117,7 +119,8 @@ public class ThermalManagerServiceTest { */ private class ThermalHalFake extends ThermalHalWrapper { private static final int INIT_STATUS = Temperature.THROTTLING_NONE; - private List<Temperature> mTemperatureList = new ArrayList<>(); + private final List<Temperature> mTemperatureList = new ArrayList<>(); + private AtomicInteger mGetCurrentTemperaturesCalled = new AtomicInteger(); private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); @@ -173,6 +176,7 @@ public class ThermalManagerServiceTest { mTemperatureList.add(mUsbPort); mCoolingDeviceList.add(mCpu); mCoolingDeviceList.add(mGpu); + mGetCurrentTemperaturesCalled.set(0); } void enableForecastSkinTemperature() { @@ -188,14 +192,24 @@ public class ThermalManagerServiceTest { mForecastSkinTemperaturesError = true; } + void updateTemperatureList(Temperature... temperatures) { + synchronized (mTemperatureList) { + mTemperatureList.clear(); + mTemperatureList.addAll(Arrays.asList(temperatures)); + } + } + @Override protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) { List<Temperature> ret = new ArrayList<>(); - for (Temperature temperature : mTemperatureList) { - if (shouldFilter && type != temperature.getType()) { - continue; + synchronized (mTemperatureList) { + mGetCurrentTemperaturesCalled.incrementAndGet(); + for (Temperature temperature : mTemperatureList) { + if (shouldFilter && type != temperature.getType()) { + continue; + } + ret.add(temperature); } - ret.add(temperature); } return ret; } @@ -407,7 +421,7 @@ public class ThermalManagerServiceTest { Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC); resetListenerMock(); int status = Temperature.THROTTLING_SEVERE; - mFakeHal.mTemperatureList = new ArrayList<>(); + mFakeHal.updateTemperatureList(); // Should not notify on non-skin type Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status); @@ -537,6 +551,42 @@ public class ThermalManagerServiceTest { } @Test + @DisableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) + public void testGetThermalHeadroom_handlerUpdateTemperatures() + throws RemoteException, InterruptedException { + // test that handler will at least enqueue one message to periodically read temperatures + // even if there is sample seeded from HAL temperature callback + String temperatureName = "skin1"; + Temperature temperature = new Temperature(100, Temperature.TYPE_SKIN, temperatureName, + Temperature.THROTTLING_NONE); + mFakeHal.mCallback.onTemperatureChanged(temperature); + float headroom = mService.mService.getThermalHeadroom(0); + // the callback temperature 100C (headroom > 1.0f) sample should have been appended by the + // immediately scheduled fake HAL current temperatures read (mSkin1, mSkin2), and because + // there are less samples for prediction, the latest temperature mSkin1 is used to calculate + // headroom (mSkin2 has no threshold), which is 0.6f (28C vs threshold 40C). + assertEquals(0.6f, headroom, 0.01f); + // one called by service onActivityManagerReady, one called by handler on headroom call + assertEquals(2, mFakeHal.mGetCurrentTemperaturesCalled.get()); + // periodic read should update the samples history, so the headroom should increase 0.1f + // as current temperature goes up by 3C every 1100ms. + for (int i = 1; i < 5; i++) { + Temperature newTemperature = new Temperature(mFakeHal.mSkin1.getValue() + 3 * i, + Temperature.TYPE_SKIN, + temperatureName, + Temperature.THROTTLING_NONE); + mFakeHal.updateTemperatureList(newTemperature); + // wait for handler to update temperature + Thread.sleep(1100); + // assert that only one callback was scheduled to query HAL when making multiple + // headroom calls + assertEquals(2 + i, mFakeHal.mGetCurrentTemperaturesCalled.get()); + headroom = mService.mService.getThermalHeadroom(0); + assertEquals(0.6f + 0.1f * i, headroom, 0.01f); + } + } + + @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java index 339bac4f768b..d6b3fecb487c 100644 --- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java @@ -45,6 +45,8 @@ import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.List; +import java.util.Map; +import java.util.HashMap; import java.util.concurrent.atomic.AtomicBoolean; @SuppressLint("VisibleForTests") @@ -66,17 +68,28 @@ public class AdvancedProtectionServiceTest { mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { + private Map<String, Integer> mStoredValues = new HashMap<>(); private boolean mEnabled = false; @Override - boolean retrieve() { + boolean retrieveAdvancedProtectionModeEnabled() { return mEnabled; } @Override - void store(boolean enabled) { + void storeAdvancedProtectionModeEnabled(boolean enabled) { this.mEnabled = enabled; } + + @Override + void storeInt(String key, int value) { + mStoredValues.put(key, value); + } + + @Override + int retrieveInt(String key, int defaultValue) { + return mStoredValues.getOrDefault(key, defaultValue); + } }; mLooper = new TestLooper(); @@ -316,6 +329,18 @@ public class AdvancedProtectionServiceTest { } @Test + public void testUsbDataProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.isUsbDataProtectionEnabled()); + } + + @Test + public void testSetUsbDataProtection_withoutPermission() { + mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE); + assertThrows(SecurityException.class, () -> mService.setUsbDataProtectionEnabled(true)); + } + + @Test public void testRegisterCallback_withoutPermission() { mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback( 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 902171d614d9..8c9b9bd03b9f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -18657,4 +18657,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mGroupHelper).onNotificationRemoved(eq(n), any(), eq(false)); } + + @Test + public void onDisplayRemoveSystemDecorations_cancelToasts() throws RemoteException { + final String testPackage = "testPackageName"; + final INotificationManager service = ((INotificationManager) mService.mService); + final IBinder firstExternal = new Binder(); + final IBinder secondExternal = new Binder(); + final IBinder firstBuiltin = new Binder(); + service.enqueueTextToast(testPackage, + firstExternal, "First external", TOAST_DURATION, + /* isUiContext= */ true, /* displayId= */ 10, /* callback= */ null); + service.enqueueTextToast(testPackage, + secondExternal, "Second external", TOAST_DURATION, + /* isUiContext= */ true, /* displayId= */ 10, /* callback= */ null); + service.enqueueTextToast(testPackage, + firstBuiltin, "First built-in", TOAST_DURATION, /* isUiContext= */ true, + /* displayId= */ DEFAULT_DISPLAY, /* callback= */ null); + + mInternalService.onDisplayRemoveSystemDecorations(10); + + verify(mStatusBar).showToast(anyInt(), eq(testPackage), eq(firstExternal), + any(String.class), any(IBinder.class), anyInt(), any(), eq(10)); + verify(mStatusBar).hideToast(eq(testPackage), eq(firstExternal)); + // The second toast has not been shown but invokes hide() anyway as + // NotificationManagerService does not remembered if it invoked show(). + verify(mStatusBar, never()).showToast(anyInt(), eq(testPackage), eq(secondExternal), + any(String.class), any(IBinder.class), anyInt(), any(), eq(10)); + verify(mStatusBar).hideToast(eq(testPackage), eq(secondExternal)); + // The toast on the default display is shown as other notifications are cancelled. + verify(mStatusBar).showToast(anyInt(), eq(testPackage), eq(firstBuiltin), any(String.class), + any(IBinder.class), anyInt(), any(), eq(DEFAULT_DISPLAY)); + verify(mStatusBar, never()).hideToast(eq(testPackage), eq(firstBuiltin)); + } } diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java index 0495e967c0e3..5ab00361d3c0 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -491,6 +491,11 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { @Test public void testKeyGestureBack() { + mPhoneWindowManager.overrideDelegateBackGestureRemote(true); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK); + mPhoneWindowManager.assertBackEventInjected(); + + mPhoneWindowManager.overrideDelegateBackGestureRemote(false); sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK); mPhoneWindowManager.assertBackEventInjected(); } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 7b6d361c55d4..7059c41898f3 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -202,6 +202,7 @@ class TestPhoneWindowManager { private boolean mIsTalkBackEnabled; private boolean mIsTalkBackShortcutGestureEnabled; + private boolean mDelegateBackGestureRemote; private boolean mIsVoiceAccessEnabled; private Intent mBrowserIntent; @@ -580,6 +581,12 @@ class TestPhoneWindowManager { setPhoneCallIsInProgress(); } + void overrideDelegateBackGestureRemote(boolean isDelegating) { + mDelegateBackGestureRemote = isDelegating; + doReturn(mDelegateBackGestureRemote).when(mActivityTaskManagerInternal) + .requestBackGesture(); + } + void prepareBrightnessDecrease(float currentBrightness) { doReturn(0.0f).when(mPowerManager).getBrightnessConstraint( DEFAULT_DISPLAY, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM); @@ -661,13 +668,21 @@ class TestPhoneWindowManager { } void assertBackEventInjected() { - ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class); - verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt()); - List<InputEvent> inputEvents = intentCaptor.getAllValues(); - Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(0)).getKeyCode()); - Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(1)).getKeyCode()); - // Reset verifier for next call. - Mockito.clearInvocations(mContext); + if (mDelegateBackGestureRemote) { + Mockito.verify(mActivityTaskManagerInternal).requestBackGesture(); + ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class); + verify(mInputManager, never()).injectInputEvent(intentCaptor.capture(), anyInt()); + } else { + ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class); + verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt()); + List<InputEvent> inputEvents = intentCaptor.getAllValues(); + Assert.assertEquals(KeyEvent.KEYCODE_BACK, + ((KeyEvent) inputEvents.get(0)).getKeyCode()); + Assert.assertEquals(KeyEvent.KEYCODE_BACK, + ((KeyEvent) inputEvents.get(1)).getKeyCode()); + // Reset verifier for next call. + Mockito.clearInvocations(mContext); + } } void overrideSearchKeyBehavior(int behavior) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 521d8364a31f..449ca867b987 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -52,6 +52,8 @@ import android.view.Surface; import androidx.test.filters.SmallTest; import com.android.server.LocalServices; +import com.android.server.UiThread; +import com.android.server.notification.NotificationManagerInternal; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry; import com.android.window.flags.Flags; @@ -59,6 +61,7 @@ import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import java.util.HashMap; import java.util.Map; @@ -387,6 +390,30 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { } @Test + @EnableFlags(com.android.server.display.feature.flags.Flags + .FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT) + public void testSetShouldShowSystemDecorsNotifyNotificationManager() { + final NotificationManagerInternal notificationManager = Mockito.mock( + NotificationManagerInternal.class); + LocalServices.addService(NotificationManagerInternal.class, notificationManager); + try { + // First show the decoration because setting false is noop if the decoration has already + // been hidden. + mDisplayWindowSettings.setShouldShowSystemDecorsLocked( + mSecondaryDisplay, /* shouldShow= */ true); + + mDisplayWindowSettings.setShouldShowSystemDecorsLocked( + mSecondaryDisplay, /* shouldShow= */ false); + + waitHandlerIdle(UiThread.getHandler()); + Mockito.verify(notificationManager).onDisplayRemoveSystemDecorations( + mSecondaryDisplay.mDisplayId); + } finally { + LocalServices.removeServiceForTest(NotificationManagerInternal.class); + } + } + + @Test public void testPrimaryDisplayImePolicy() { assertEquals(DISPLAY_IME_POLICY_LOCAL, mDisplayWindowSettings.getImePolicyLocked(mPrimaryDisplay)); diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp index 8fbc3e8a78db..a103d03af3c3 100644 --- a/tools/protologtool/Android.bp +++ b/tools/protologtool/Android.bp @@ -11,13 +11,13 @@ java_library_host { name: "protologtool-lib", srcs: [ "src/com/android/protolog/tool/**/*.kt", - ":protolog-common-src", ], static_libs: [ "javaparser", - "platformprotos", "jsonlib", "perfetto_trace-full", + "platformprotos", + "protolog-common-lib", ], } @@ -39,10 +39,10 @@ java_test_host { unit_test: true, }, static_libs: [ - "protologtool-lib", "junit", "mockito", "objenesis", + "protologtool-lib", "truth", ], } diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt index 47724b7a9e1d..03c3a15562a9 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt @@ -24,5 +24,5 @@ interface ProtoLogCallProcessor { logCallVisitor: ProtoLogCallVisitor?, otherCallVisitor: MethodCallVisitor?, fileName: String - ): CompilationUnit + ): Collection<CodeProcessingException> } diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt index 1f6db5fe2d37..44db2ba45845 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt @@ -74,7 +74,7 @@ class ProtoLogCallProcessorImpl( } fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String): - CompilationUnit { + Collection<CodeProcessingException> { return process(code, logCallVisitor, null, fileName) } @@ -83,7 +83,7 @@ class ProtoLogCallProcessorImpl( logCallVisitor: ProtoLogCallVisitor?, otherCallVisitor: MethodCallVisitor?, fileName: String - ): CompilationUnit { + ): Collection<CodeProcessingException> { CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName) CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName) @@ -93,50 +93,63 @@ class ProtoLogCallProcessorImpl( protoLogGroupClassName) val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName) + val errors = mutableListOf<CodeProcessingException>() code.findAll(MethodCallExpr::class.java) .filter { call -> isProtoCall(call, isLogClassImported, staticLogImports) }.forEach { call -> val context = ParsingContext(fileName, call) - val logMethods = LogLevel.entries.map { it.shortCode } - if (logMethods.contains(call.name.id)) { - // Process a log call - if (call.arguments.size < 2) { - throw InvalidProtoLogCallException("Method signature does not match " + - "any ProtoLog method: $call", context) - } + try { + val logMethods = LogLevel.entries.map { it.shortCode } + if (logMethods.contains(call.name.id)) { + // Process a log call + if (call.arguments.size < 2) { + errors.add(InvalidProtoLogCallException( + "Method signature does not match " + + "any ProtoLog method: $call", context + )) + return@forEach + } - val messageString = CodeUtils.concatMultilineString(call.getArgument(1), - context) - val groupNameArg = call.getArgument(0) - val groupName = - getLogGroupName(groupNameArg, isGroupClassImported, - staticGroupImports, fileName) - if (groupName !in groupMap) { - throw InvalidProtoLogCallException("Unknown group argument " + - "- not a ProtoLogGroup enum member: $call", context) - } + val messageString = CodeUtils.concatMultilineString( + call.getArgument(1), + context + ) + val groupNameArg = call.getArgument(0) + val groupName = + getLogGroupName( + groupNameArg, isGroupClassImported, + staticGroupImports, fileName + ) + if (groupName !in groupMap) { + errors.add(InvalidProtoLogCallException( + "Unknown group argument " + + "- not a ProtoLogGroup enum member: $call", context + )) + return@forEach + } - try { logCallVisitor?.processCall( call, messageString, getLevelForMethodName( call.name.toString(), call, context ), groupMap.getValue(groupName), context.lineNumber ) - } catch (e: Throwable) { - throw InvalidProtoLogCallException("Error processing log call: $call", - context, e) + } else if (call.name.id == "init") { + // No processing + } else { + // Process non-log message calls + otherCallVisitor?.processCall(call) } - } else if (call.name.id == "init") { - // No processing - } else { - // Process non-log message calls - otherCallVisitor?.processCall(call) + } catch (e: Throwable) { + errors.add(InvalidProtoLogCallException( + "Error processing log call: $call", + context, e + )) } } - return code + return errors } private fun getLevelForMethodName( diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt index d8a2954545bb..e9f2e81b69db 100644 --- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt +++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt @@ -130,14 +130,16 @@ object ProtoLogTool { val outSrc = try { val code = tryParse(text, path) if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) { - transformer.processClass(text, path, packagePath(file, code), code) + val (processedText, errors) = transformer.processClass(text, path, packagePath(file, code), code) + errors.forEach { injector.reportProcessingError(it) } + processedText } else { text } } catch (ex: ParsingException) { // If we cannot parse this file, skip it (and log why). Compilation will // fail in a subsequent build step. - injector.reportParseError(ex) + injector.reportProcessingError(ex) text } path to outSrc @@ -405,7 +407,7 @@ object ProtoLogTool { } catch (ex: ParsingException) { // If we cannot parse this file, skip it (and log why). Compilation will // fail in a subsequent build step. - injector.reportParseError(ex) + injector.reportProcessingError(ex) null } } else { @@ -466,6 +468,14 @@ object ProtoLogTool { try { val command = CommandOptions(args) invoke(command) + + if (injector.processingErrors.isNotEmpty()) { + injector.processingErrors.forEachIndexed { index, it -> + println("CodeProcessingException " + + "(${index + 1}/${injector.processingErrors.size}): \n${it.message}\n") + } + exitProcess(1) + } } catch (ex: InvalidCommandException) { println("InvalidCommandException: \n${ex.message}\n") showHelpAndExit() @@ -489,12 +499,14 @@ object ProtoLogTool { } var injector = object : Injector { + override val processingErrors: MutableList<CodeProcessingException> + get() = mutableListOf() override fun fileOutputStream(file: String) = FileOutputStream(file) override fun readText(file: File) = file.readText() override fun readLogGroups(jarPath: String, className: String) = ProtoLogGroupReader().loadFromJar(jarPath, className) - override fun reportParseError(ex: ParsingException) { - println("\n${ex.message}\n") + override fun reportProcessingError(ex: CodeProcessingException) { + processingErrors.add(ex) } } @@ -502,7 +514,8 @@ object ProtoLogTool { fun fileOutputStream(file: String): OutputStream fun readText(file: File): String fun readLogGroups(jarPath: String, className: String): Map<String, LogGroup> - fun reportParseError(ex: ParsingException) + fun reportProcessingError(ex: CodeProcessingException) + val processingErrors: Collection<CodeProcessingException> } } diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt index 76df0674df65..e97b0dd9a04b 100644 --- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt +++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt @@ -66,13 +66,13 @@ class SourceTransformer( packagePath: String, compilationUnit: CompilationUnit = StaticJavaParser.parse(code) - ): String { + ): Pair<String, Collection<CodeProcessingException>> { this.path = path this.packagePath = packagePath processedCode = code.split('\n').toMutableList() offsets = IntArray(processedCode.size) - protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path) - return processedCode.joinToString("\n") + val processingErrors = protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path) + return Pair(processedCode.joinToString("\n"), processingErrors) } private val protoLogImplClassNode = diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt index 0cbbd483fe59..71bab3ebf2db 100644 --- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt +++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt @@ -17,6 +17,7 @@ package com.android.protolog.tool import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH +import com.android.protolog.tool.ProtoLogTool.injector import com.google.common.truth.Truth import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -102,6 +103,42 @@ class EndToEndTest { """.trimIndent()) } + @Test + fun e2e_transform_withErrors() { + val srcs = mapOf( + "frameworks/base/org/example/Example.java" to """ + package org.example; + import com.android.internal.protolog.ProtoLog; + import static com.android.internal.protolog.ProtoLogGroup.GROUP; + + class Example { + void method() { + String argString = "hello"; + int argInt = 123; + ProtoLog.d(GROUP, "Invalid format: %s %d %9 %", argString, argInt); + } + } + """.trimIndent()) + val output = run( + srcs = srcs, + logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"), + commandOptions = CommandOptions(arrayOf("transform-protolog-calls", + "--protolog-class", "com.android.internal.protolog.ProtoLog", + "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup", + "--loggroups-jar", "not_required.jar", + "--viewer-config-file-path", "not_required.pb", + "--output-srcjar", "out.srcjar", + "frameworks/base/org/example/Example.java")) + ) + val outSrcJar = assertLoadSrcJar(output, "out.srcjar") + // No change to source code on failure to process + Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"]) + .contains(srcs["frameworks/base/org/example/Example.java"]) + + Truth.assertThat(injector.processingErrors).hasSize(1) + Truth.assertThat(injector.processingErrors.first().message).contains("Invalid format") + } + private fun assertLoadSrcJar( outputs: Map<String, ByteArray>, path: String @@ -172,7 +209,11 @@ class EndToEndTest { override fun readLogGroups(jarPath: String, className: String) = mapOf( logGroup.name to logGroup) - override fun reportParseError(ex: ParsingException) = throw AssertionError(ex) + override fun reportProcessingError(ex: CodeProcessingException) { + processingErrors.add(ex) + } + + override val processingErrors: MutableList<CodeProcessingException> = mutableListOf() } ProtoLogTool.invoke(commandOptions) diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt index 732824ae841c..8f765aecb431 100644 --- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt +++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt @@ -21,7 +21,6 @@ import com.android.internal.protolog.common.LogLevel import com.github.javaparser.StaticJavaParser import com.github.javaparser.ast.expr.MethodCallExpr import org.junit.Assert.assertEquals -import org.junit.Assert.assertThrows import org.junit.Test import com.google.common.truth.Truth @@ -124,7 +123,7 @@ class ProtoLogCallProcessorImplTest { checkCalls() } - @Test(expected = InvalidProtoLogCallException::class) + @Test fun process_groupNotImported() { val code = """ package org.example2; @@ -138,7 +137,10 @@ class ProtoLogCallProcessorImplTest { } """ groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager") - visitor.process(StaticJavaParser.parse(code), processor, "") + val errors = visitor.process(StaticJavaParser.parse(code), processor, "") + + Truth.assertThat(errors).hasSize(1) + Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java) } @Test @@ -159,7 +161,7 @@ class ProtoLogCallProcessorImplTest { assertEquals(0, calls.size) } - @Test(expected = InvalidProtoLogCallException::class) + @Test fun process_unknownGroup() { val code = """ package org.example; @@ -170,10 +172,13 @@ class ProtoLogCallProcessorImplTest { } } """ - visitor.process(StaticJavaParser.parse(code), processor, "") + val errors = visitor.process(StaticJavaParser.parse(code), processor, "") + + Truth.assertThat(errors).hasSize(1) + Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java) } - @Test(expected = InvalidProtoLogCallException::class) + @Test fun process_staticGroup() { val code = """ package org.example; @@ -184,10 +189,13 @@ class ProtoLogCallProcessorImplTest { } } """ - visitor.process(StaticJavaParser.parse(code), processor, "") + val errors = visitor.process(StaticJavaParser.parse(code), processor, "") + + Truth.assertThat(errors).hasSize(1) + Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java) } - @Test(expected = InvalidProtoLogCallException::class) + @Test fun process_badGroup() { val code = """ package org.example; @@ -198,10 +206,13 @@ class ProtoLogCallProcessorImplTest { } } """ - visitor.process(StaticJavaParser.parse(code), processor, "") + val errors = visitor.process(StaticJavaParser.parse(code), processor, "") + + Truth.assertThat(errors).hasSize(1) + Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java) } - @Test(expected = InvalidProtoLogCallException::class) + @Test fun process_invalidSignature() { val code = """ package org.example; @@ -212,7 +223,10 @@ class ProtoLogCallProcessorImplTest { } } """ - visitor.process(StaticJavaParser.parse(code), processor, "") + val errors = visitor.process(StaticJavaParser.parse(code), processor, "") + + Truth.assertThat(errors).hasSize(1) + Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java) } @Test @@ -257,9 +271,10 @@ class ProtoLogCallProcessorImplTest { } } - val exception = assertThrows(InvalidProtoLogCallException::class.java) { - visitor.process(StaticJavaParser.parse(code), processor, "MyTestFile.java") - } + val errors = visitor.process(StaticJavaParser.parse(code), processor, "MyTestFile.java") + Truth.assertThat(errors).hasSize(1) + + val exception = errors.first() Truth.assertThat(exception).hasMessageThat() .contains("Code processing error in MyTestFile.java:6") Truth.assertThat(exception.cause).hasMessageThat() diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt index 674a907d68d9..271fc6064678 100644 --- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt +++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt @@ -160,10 +160,10 @@ class SourceTransformerTest { visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f", LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { @@ -201,10 +201,10 @@ class SourceTransformerTest { visitor.processCall(calls[2], "test %d %f", LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { @@ -238,10 +238,10 @@ class SourceTransformerTest { "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { @@ -275,10 +275,10 @@ class SourceTransformerTest { visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test", LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { @@ -309,10 +309,10 @@ class SourceTransformerTest { visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f", LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"), 789) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { @@ -346,10 +346,10 @@ class SourceTransformerTest { "test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"), 123) - invocation.arguments[0] as CompilationUnit + listOf<CodeProcessingException>() } - val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code) + val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code) code = StaticJavaParser.parse(out) val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter { |