diff options
163 files changed, 3434 insertions, 1675 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 2bd5aee0cd24..bb6538739c49 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -299,6 +299,7 @@ java_defaults { static_libs: [ // License notices from art module "art-notices-for-framework-stubs-jar", + "framework-res-package-jar", // Export package of framework-res ], errorprone: { javacflags: [ @@ -311,6 +312,15 @@ java_defaults { compile_dex: true, } +java_defaults { + name: "android_stubs_dists_default", + dist: { + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android.jar", + }, +} + java_library_static { name: "android_monolith_stubs_current", srcs: [ ":api-stubs-docs" ], @@ -346,7 +356,21 @@ java_library_static { name: "android_system_monolith_stubs_current", srcs: [ ":system-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/system", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_system.jar", + }, + ], } java_library_static { @@ -378,14 +402,34 @@ java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], static_libs: [ "private-stub-annotations-jar" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], + dist: { + dir: "apistubs/android/test", + }, + dists: [ + { + // Legacy dist path + targets: ["sdk", "win_sdk"], + tag: ".jar", + dest: "android_test.jar", + }, + ], } java_library_static { name: "android_module_lib_stubs_current", srcs: [ ":module-lib-api-stubs-docs-non-updatable" ], - defaults: ["android_defaults_stubs_current"], + defaults: [ + "android_defaults_stubs_current", + "android_stubs_dists_default", + ], libs: ["sdk_system_29_android"], + dist: { + dir: "apistubs/android/module-lib", + }, } java_library_static { diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml index c7929af6077f..fbe589248338 100644 --- a/apct-tests/perftests/multiuser/AndroidTest.xml +++ b/apct-tests/perftests/multiuser/AndroidTest.xml @@ -27,6 +27,9 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="push-file" key="trace_config_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config_detailed.textproto" value="/sdcard/sample.textproto" /> </target_preparer> <!-- Needed for pulling the collected trace config on to the host --> diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index 7cd8a628c9fc..17545a469cb8 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -46,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.net { diff --git a/api/system-current.txt b/api/system-current.txt index ea16238316ac..3ab164554da6 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -12171,6 +12171,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/api/test-current.txt b/api/test-current.txt index a1d1fa781096..342bd81ac69b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1305,6 +1305,8 @@ package android.hardware.display { method public android.graphics.Point getStableDisplaySize(); method public boolean isMinimalPostProcessingRequested(int); method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); + field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 + field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400 } } @@ -4618,6 +4620,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 6b335ee1b923..8b2c2da9396d 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -485,6 +485,9 @@ message Atom { NetworkTetheringReported network_tethering_reported = 303 [(module) = "network_tethering"]; ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; + UIInteractionFrameInfoReported ui_interaction_frame_info_reported = + 305 [(module) = "framework"]; + UIActionLatencyReported ui_action_latency_reported = 306 [(module) = "framework"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. @@ -5054,6 +5057,54 @@ message BlobOpened{ optional Result result = 4; } +/** + * Event to track Jank for various system interactions. + * + * Logged from: + * frameworks/base/core/java/android/os/aot/FrameTracker.java + */ +message UIInteractionFrameInfoReported { + enum InteractionType { + UNKNOWN = 0; + NOTIFICATION_SHADE_SWIPE = 1; + } + + optional InteractionType interaction_type = 1; + + // Number of frames rendered during the interaction. + optional int64 total_frames = 2; + + // Number of frames that were skipped in rendering during the interaction. + optional int64 missed_frames = 3; + + // Maximum time it took to render a single frame during the interaction. + optional int64 max_frame_time_nanos = 4; +} + +/** + * Event to track various latencies in SystemUI. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/util/LatencyTracker.java + */ +message UIActionLatencyReported { + enum ActionType { + UNKNOWN = 0; + ACTION_EXPAND_PANEL = 1; + ACTION_TOGGLE_RECENTS = 2; + ACTION_FINGERPRINT_WAKE_AND_UNLOCK = 3; + ACTION_CHECK_CREDENTIAL = 4; + ACTION_CHECK_CREDENTIAL_UNLOCKED = 5; + ACTION_TURN_ON_SCREEN = 6; + ACTION_ROTATE_SCREEN = 7; + ACTION_FACE_WAKE_AND_UNLOCK = 8; + } + + optional ActionType action = 1; + + optional int64 latency_millis = 2; +} + ////////////////////////////////////////////////////////////////////// // Pulled atoms below this line // ////////////////////////////////////////////////////////////////////// diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index 98a23f2b0075..3cb6293f0706 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -105,7 +105,8 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd public ActivityView( @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay) { - this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, false); + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + false /* disableSurfaceViewBackgroundLayer */); } /** @hide */ @@ -113,12 +114,22 @@ public class ActivityView extends ViewGroup implements android.window.TaskEmbedd @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, boolean singleTaskInstance, boolean usePublicVirtualDisplay, boolean disableSurfaceViewBackgroundLayer) { + this(context, attrs, defStyle, singleTaskInstance, usePublicVirtualDisplay, + disableSurfaceViewBackgroundLayer, false /* useTrustedDisplay */); + } + + // TODO(b/162901735): Refactor ActivityView with Builder + /** @hide */ + public ActivityView( + @NonNull Context context, @NonNull AttributeSet attrs, int defStyle, + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean disableSurfaceViewBackgroundLayer, boolean useTrustedDisplay) { super(context, attrs, defStyle); if (useTaskOrganizer()) { mTaskEmbedder = new TaskOrganizerTaskEmbedder(context, this); } else { mTaskEmbedder = new VirtualDisplayTaskEmbedder(context, this, singleTaskInstance, - usePublicVirtualDisplay); + usePublicVirtualDisplay, useTrustedDisplay); } mSurfaceView = new SurfaceView(context, null, 0, 0, disableSurfaceViewBackgroundLayer); // Since ActivityView#getAlpha has been overridden, we should use parent class's alpha diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index bc418061e1d1..b0437ac7284e 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -101,14 +101,11 @@ public final class ApkAssets { public @interface FormatType {} @GuardedBy("this") - private final long mNativePtr; + private long mNativePtr; // final, except cleared in finalizer. @Nullable @GuardedBy("this") - private final StringBlock mStringBlock; - - @GuardedBy("this") - private boolean mOpen = true; + private final StringBlock mStringBlock; // null or closed if mNativePtr = 0. @PropertyFlags private final int mFlags; @@ -380,12 +377,16 @@ public final class ApkAssets { /** @hide */ @Nullable public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException { - return nativeGetOverlayableInfo(mNativePtr, overlayableName); + synchronized (this) { + return nativeGetOverlayableInfo(mNativePtr, overlayableName); + } } /** @hide */ public boolean definesOverlayable() throws IOException { - return nativeDefinesOverlayable(mNativePtr); + synchronized (this) { + return nativeDefinesOverlayable(mNativePtr); + } } /** @@ -412,12 +413,12 @@ public final class ApkAssets { */ public void close() { synchronized (this) { - if (mOpen) { - mOpen = false; + if (mNativePtr != 0) { if (mStringBlock != null) { mStringBlock.close(); } nativeDestroy(mNativePtr); + mNativePtr = 0; } } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index c1ba2094d3cf..392f56212058 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -310,6 +310,7 @@ public final class DisplayManager { * @hide */ // TODO (b/114338689): Remove the flag and use IWindowManager#setShouldShowSystemDecors + @TestApi public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9; /** @@ -320,6 +321,7 @@ public final class DisplayManager { * @see #VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS * @hide */ + @TestApi public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10; /** @hide */ diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java index 50cc764dd536..58c8efa3a972 100644 --- a/core/java/android/os/Trace.java +++ b/core/java/android/os/Trace.java @@ -102,6 +102,8 @@ public final class Trace { /** @hide */ public static final long TRACE_TAG_RRO = 1L << 26; /** @hide */ + public static final long TRACE_TAG_SYSPROP = 1L << 27; + /** @hide */ public static final long TRACE_TAG_APEX_MANAGER = 1L << 18; private static final long TRACE_TAG_NOT_READY = 1L << 63; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 73b33c20d296..6903a995bf7b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9037,6 +9037,13 @@ public final class Settings { "accessibility_magnification_capability"; /** + * Whether the Adaptive connectivity option is enabled. + * + * @hide + */ + public static final String ADAPTIVE_CONNECTIVITY_ENABLED = "adaptive_connectivity_enabled"; + + /** * Keys we no longer back up under the current schema, but want to continue to * process when restoring historical backup datasets. * diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7f45c044408a..403ac3ab29c0 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -629,7 +629,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); } - if (!mState.equals(state, true /* excludingCaptionInsets */, + if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */, true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState); updateRequestedState(); @@ -1138,15 +1138,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (invokeCallback) { control.cancel(); } + boolean stateChanged = false; for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); if (runningAnimation.runner == control) { mRunningAnimations.remove(i); ArraySet<Integer> types = toInternalType(control.getTypes()); for (int j = types.size() - 1; j >= 0; j--) { - if (getSourceConsumer(types.valueAt(j)).notifyAnimationFinished()) { - mHost.notifyInsetsChanged(); - } + stateChanged |= getSourceConsumer(types.valueAt(j)).notifyAnimationFinished(); } if (invokeCallback && runningAnimation.startDispatched) { dispatchAnimationEnd(runningAnimation.runner.getAnimation()); @@ -1154,6 +1153,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation break; } } + if (stateChanged) { + mHost.notifyInsetsChanged(); + updateRequestedState(); + } } private void applyLocalVisibilityOverride() { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 6b0b509932a8..593b37af26ad 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -60,6 +60,8 @@ import java.util.StringJoiner; */ public class InsetsState implements Parcelable { + public static final InsetsState EMPTY = new InsetsState(); + /** * Internal representation of inset source types. This is different from the public API in * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 28644858377a..7cc347d25458 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -294,7 +294,7 @@ public final class InputMethodInfo implements Parcelable { */ public InputMethodInfo(String packageName, String className, CharSequence label, String settingsActivity) { - this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */, + this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, false /* isVrOnly */); @@ -344,7 +344,7 @@ public final class InputMethodInfo implements Parcelable { mIsVrOnly = isVrOnly; } - private static ResolveInfo buildDummyResolveInfo(String packageName, String className, + private static ResolveInfo buildFakeResolveInfo(String packageName, String className, CharSequence label) { ResolveInfo ri = new ResolveInfo(); ServiceInfo si = new ServiceInfo(); diff --git a/core/java/android/window/VirtualDisplayTaskEmbedder.java b/core/java/android/window/VirtualDisplayTaskEmbedder.java index 9ccb4c172158..9013da36007e 100644 --- a/core/java/android/window/VirtualDisplayTaskEmbedder.java +++ b/core/java/android/window/VirtualDisplayTaskEmbedder.java @@ -19,6 +19,7 @@ package android.window; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; import static android.view.Display.INVALID_DISPLAY; import android.app.ActivityManager; @@ -63,6 +64,7 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { private int mDisplayDensityDpi; private final boolean mSingleTaskInstance; private final boolean mUsePublicVirtualDisplay; + private final boolean mUseTrustedDisplay; private VirtualDisplay mVirtualDisplay; private Insets mForwardedInsets; private DisplayMetrics mTmpDisplayMetrics; @@ -77,10 +79,12 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { * only applicable if virtual displays are used */ public VirtualDisplayTaskEmbedder(Context context, VirtualDisplayTaskEmbedder.Host host, - boolean singleTaskInstance, boolean usePublicVirtualDisplay) { + boolean singleTaskInstance, boolean usePublicVirtualDisplay, + boolean useTrustedDisplay) { super(context, host); mSingleTaskInstance = singleTaskInstance; mUsePublicVirtualDisplay = usePublicVirtualDisplay; + mUseTrustedDisplay = useTrustedDisplay; } /** @@ -103,6 +107,9 @@ public class VirtualDisplayTaskEmbedder extends TaskEmbedder { if (mUsePublicVirtualDisplay) { virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_PUBLIC; } + if (mUseTrustedDisplay) { + virtualDisplayFlags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; + } mVirtualDisplay = displayManager.createVirtualDisplay( DISPLAY_NAME + "@" + System.identityHashCode(this), mHost.getWidth(), diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 053b06f3d407..2f8c45770eb5 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -347,7 +347,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { super(context); mLayoutInflater = LayoutInflater.from(context); mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } /** diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto index 541e018d079b..3d12d072eb80 100644 --- a/core/proto/android/providers/settings/secure.proto +++ b/core/proto/android/providers/settings/secure.proto @@ -181,6 +181,7 @@ message SecureSettingsProto { optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ]; repeated SettingProto completed_categories = 15; optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto adaptive_connectivity_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ]; message Controls { option (android.msg_privacy).dest = DEST_EXPLICIT; @@ -614,5 +615,5 @@ message SecureSettingsProto { // Please insert fields in alphabetical order and group them into messages // if possible (to avoid reaching the method limit). - // Next tag = 84; + // Next tag = 85; } diff --git a/core/res/Android.bp b/core/res/Android.bp index b365de4f4630..f94a2b08e6c3 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -46,6 +46,13 @@ android_app { }, } +java_genrule { + name: "framework-res-package-jar", + srcs: [":framework-res{.export-package.apk}"], + out: ["framework-res-package.jar"], + cmd: "cp $(in) $(out)", +} + // This logic can be removed once robolectric's transition to binary resources is complete filegroup { name: "robolectric_framework_raw_res_files", diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 3c5d951f685e..e17c312dbffc 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -129,6 +129,7 @@ <!-- virtual display test permissions --> <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" /> <uses-permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT" /> + <uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" /> <!-- color extraction test permissions --> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index daf613976358..0f6284d22d10 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -247,6 +247,25 @@ public class VirtualDisplayTest extends AndroidTestCase { assertDisplayUnregistered(display); } + /** + * Ensures that an application can create a trusted virtual display with the permission + * {@code ADD_TRUSTED_DISPLAY}. + */ + public void testTrustedVirtualDisplay() throws Exception { + VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay(NAME, + WIDTH, HEIGHT, DENSITY, mSurface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED); + assertNotNull("virtual display must not be null", virtualDisplay); + + Display display = virtualDisplay.getDisplay(); + try { + assertDisplayRegistered(display, Display.FLAG_PRIVATE | Display.FLAG_TRUSTED); + } finally { + virtualDisplay.release(); + } + assertDisplayUnregistered(display); + } + private void assertDisplayRegistered(Display display, int flags) { assertNotNull("display object must not be null", display); assertTrue("display must be valid", display.isValid()); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index af02b7bdbd90..de128ad6d78e 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -746,6 +746,20 @@ public class InsetsControllerTest { mController.onControlsChanged(createSingletonControl(ITYPE_IME)); assertEquals(newState.getSource(ITYPE_IME), mTestHost.getModifiedState().peekSource(ITYPE_IME)); + + // The modified frames cannot be updated if there is an animation. + mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR)); + mController.hide(navigationBars()); + newState = new InsetsState(mController.getState(), true /* copySource */); + newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--; + mController.onStateChanged(newState); + assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); + + // The modified frames can be updated while the animation is done. + mController.cancelExistingAnimations(); + assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR), + mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); }); } diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index d0e688d3efc1..e122e0026aac 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -144,13 +144,6 @@ prebuilt_etc { } prebuilt_etc { - name: "allowed_privapp_com.android.car.floatingcardslauncher", - sub_dir: "permissions", - src: "com.android.car.floatingcardslauncher.xml", - filename_from_src: true, -} - -prebuilt_etc { name: "allowed_privapp_com.android.car.ui.paintbooth", sub_dir: "permissions", src: "com.android.car.ui.paintbooth.xml", diff --git a/data/etc/car/com.android.car.floatingcardslauncher.xml b/data/etc/car/com.android.car.floatingcardslauncher.xml deleted file mode 100644 index 2755fee4eb55..000000000000 --- a/data/etc/car/com.android.car.floatingcardslauncher.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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.car.floatingcardslauncher"> - <permission name="android.permission.ACTIVITY_EMBEDDING"/> - <permission name="android.permission.INTERACT_ACROSS_USERS"/> - <permission name="android.permission.MANAGE_USERS"/> - <permission name="android.permission.MEDIA_CONTENT_CONTROL"/> - <permission name="android.permission.MODIFY_PHONE_STATE"/> - </privapp-permissions> -</permissions> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 28d7911c771f..964640b106b9 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -3111,7 +3111,7 @@ public class Paint { @CriticalNative private static native boolean nGetFillPath(long paintPtr, long src, long dst); @CriticalNative - private static native long nSetShader(long paintPtr, long shader); + private static native void nSetShader(long paintPtr, long shader); @CriticalNative private static native long nSetColorFilter(long paintPtr, long filter); @CriticalNative diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4d7e5dfea4f7..dfb4009b07e2 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -37,7 +37,6 @@ #include <androidfw/TypeWrappers.h> #include <cutils/atomic.h> #include <utils/ByteOrder.h> -#include <utils/Debug.h> #include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 9ae5ad97ed36..0b13754271b9 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -462,6 +462,13 @@ cc_defaults { "RenderNode.cpp", "RenderProperties.cpp", "RootRenderNode.cpp", + "shader/Shader.cpp", + "shader/BitmapShader.cpp", + "shader/ComposeShader.cpp", + "shader/LinearGradientShader.cpp", + "shader/RadialGradientShader.cpp", + "shader/RuntimeShader.cpp", + "shader/SweepGradientShader.cpp", "SkiaCanvas.cpp", "VectorDrawable.cpp", ], diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 87244427a719..ab9b8b55a4cb 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -56,12 +56,6 @@ class AHBUploader : public RefBase { public: virtual ~AHBUploader() {} - // Called to start creation of the Vulkan and EGL contexts on another thread before we actually - // need to do an upload. - void initialize() { - onInitialize(); - } - void destroy() { std::lock_guard _lock{mLock}; LOG_ALWAYS_FATAL_IF(mPendingUploads, "terminate called while uploads in progress"); @@ -91,7 +85,6 @@ protected: sp<ThreadBase> mUploadThread = nullptr; private: - virtual void onInitialize() = 0; virtual void onIdle() = 0; virtual void onDestroy() = 0; @@ -141,7 +134,6 @@ private: class EGLUploader : public AHBUploader { private: - void onInitialize() override {} void onDestroy() override { mEglManager.destroy(); } @@ -231,62 +223,67 @@ private: class VkUploader : public AHBUploader { private: - void onInitialize() override { - std::lock_guard _lock{mLock}; - if (!mUploadThread) { - mUploadThread = new ThreadBase{}; - } - if (!mUploadThread->isRunning()) { - mUploadThread->start("GrallocUploadThread"); - } - - mUploadThread->queue().post([this]() { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - mVulkanManager.initialize(); - } - }); - } void onDestroy() override { + std::lock_guard _lock{mVkLock}; mGrContext.reset(); - mVulkanManager.destroy(); + mVulkanManagerStrong.clear(); } void onIdle() override { - mGrContext.reset(); + onDestroy(); } - void onBeginUpload() override { - { - std::lock_guard _lock{mVkLock}; - if (!mVulkanManager.hasVkContext()) { - LOG_ALWAYS_FATAL_IF(mGrContext, - "GrContext exists with no VulkanManager for vulkan uploads"); - mUploadThread->queue().runSync([this]() { - mVulkanManager.initialize(); - }); - } - } - if (!mGrContext) { - GrContextOptions options; - mGrContext = mVulkanManager.createContext(options); - LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); - this->postIdleTimeoutCheck(); - } - } + void onBeginUpload() override {} bool onUploadHardwareBitmap(const SkBitmap& bitmap, const FormatInfo& format, AHardwareBuffer* ahb) override { - ATRACE_CALL(); + bool uploadSucceeded = false; + mUploadThread->queue().runSync([this, &uploadSucceeded, bitmap, ahb]() { + ATRACE_CALL(); + std::lock_guard _lock{mVkLock}; + + renderthread::VulkanManager* vkManager = getVulkanManager(); + if (!vkManager->hasVkContext()) { + LOG_ALWAYS_FATAL_IF(mGrContext, + "GrContext exists with no VulkanManager for vulkan uploads"); + vkManager->initialize(); + } + + if (!mGrContext) { + GrContextOptions options; + mGrContext = vkManager->createContext(options, + renderthread::VulkanManager::ContextType::kUploadThread); + LOG_ALWAYS_FATAL_IF(!mGrContext, "failed to create GrContext for vulkan uploads"); + this->postIdleTimeoutCheck(); + } + + sk_sp<SkImage> image = + SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); + mGrContext->submit(true); + + uploadSucceeded = (image.get() != nullptr); + }); + return uploadSucceeded; + } - std::lock_guard _lock{mLock}; + /* must be called on the upload thread after the vkLock has been acquired */ + renderthread::VulkanManager* getVulkanManager() { + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = mVulkanManagerWeak.promote(); + + // create a new manager if we couldn't promote the weak ref + if (!mVulkanManagerStrong) { + mVulkanManagerStrong = renderthread::VulkanManager::getInstance(); + mGrContext.reset(); + mVulkanManagerWeak = mVulkanManagerStrong; + } + } - sk_sp<SkImage> image = - SkImage::MakeFromAHardwareBufferWithData(mGrContext.get(), bitmap.pixmap(), ahb); - return (image.get() != nullptr); + return mVulkanManagerStrong.get(); } sk_sp<GrDirectContext> mGrContext; - renderthread::VulkanManager mVulkanManager; + sp<renderthread::VulkanManager> mVulkanManagerStrong; + wp<renderthread::VulkanManager> mVulkanManagerWeak; std::mutex mVkLock; }; @@ -428,7 +425,6 @@ void HardwareBitmapUploader::initialize() { bool usingGL = uirenderer::Properties::getRenderPipelineType() == uirenderer::RenderPipelineType::SkiaGL; createUploader(usingGL); - sUploader->initialize(); } void HardwareBitmapUploader::terminate() { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 242b8b0d139e..cfba5d4f6aa2 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -42,6 +42,8 @@ #include <SkTextBlob.h> #include <SkVertices.h> +#include <shader/BitmapShader.h> + #include <memory> #include <optional> #include <utility> @@ -49,6 +51,7 @@ namespace android { using uirenderer::PaintUtils; +using uirenderer::BitmapShader; Canvas* Canvas::create_canvas(const SkBitmap& bitmap) { return new SkiaCanvas(bitmap); @@ -681,7 +684,9 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, if (paint) { pnt = *paint; } - pnt.setShader(bitmap.makeImage()->makeShader()); + + pnt.setShader(sk_ref_sp(new BitmapShader( + bitmap.makeImage(), SkTileMode::kClamp, SkTileMode::kClamp, nullptr))); auto v = builder.detach(); apply_looper(&pnt, [&](const SkPaint& p) { mCanvas->drawVertices(v, SkBlendMode::kModulate, p); diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index cd908354aea5..0f566e4b494a 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -23,7 +23,6 @@ #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" -#include "SkShader.h" #include "hwui/Paint.h" #ifdef __ANDROID__ @@ -159,10 +158,10 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { // Draw path's fill, if fill color or gradient is valid bool needsFill = false; - SkPaint paint; + Paint paint; if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getFillGradient()))); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -179,7 +178,7 @@ void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { bool needsStroke = false; if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); - paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient()))); + paint.setShader(sk_sp<Shader>(SkSafeRef(properties.getStrokeGradient()))); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index ac7d41e0d600..d4086f1aa622 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -31,8 +31,8 @@ #include <SkPath.h> #include <SkPathMeasure.h> #include <SkRect.h> -#include <SkShader.h> #include <SkSurface.h> +#include <shader/Shader.h> #include <cutils/compiler.h> #include <stddef.h> @@ -227,20 +227,20 @@ public: strokeGradient = prop.strokeGradient; onPropertyChanged(); } - void setFillGradient(SkShader* gradient) { + void setFillGradient(Shader* gradient) { if (fillGradient.get() != gradient) { fillGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - void setStrokeGradient(SkShader* gradient) { + void setStrokeGradient(Shader* gradient) { if (strokeGradient.get() != gradient) { strokeGradient = sk_ref_sp(gradient); onPropertyChanged(); } } - SkShader* getFillGradient() const { return fillGradient.get(); } - SkShader* getStrokeGradient() const { return strokeGradient.get(); } + Shader* getFillGradient() const { return fillGradient.get(); } + Shader* getStrokeGradient() const { return strokeGradient.get(); } float getStrokeWidth() const { return mPrimitiveFields.strokeWidth; } void setStrokeWidth(float strokeWidth) { VD_SET_PRIMITIVE_FIELD_AND_NOTIFY(strokeWidth, strokeWidth); @@ -320,8 +320,8 @@ public: count, }; PrimitiveFields mPrimitiveFields; - sk_sp<SkShader> fillGradient; - sk_sp<SkShader> strokeGradient; + sk_sp<Shader> fillGradient; + sk_sp<Shader> strokeGradient; }; // Called from UI thread diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index c138a32eacc2..2a377bbb83f2 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -73,7 +73,7 @@ void Canvas::drawTextDecorations(float x, float y, float length, const Paint& pa static void simplifyPaint(int color, Paint* paint) { paint->setColor(color); - paint->setShader(nullptr); + paint->setShader((sk_sp<uirenderer::Shader>)nullptr); paint->setColorFilter(nullptr); paint->setLooper(nullptr); paint->setStrokeWidth(4 + 0.04 * paint->getSkFont().getSize()); diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index e75e9e7c6933..0bb689c19079 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -30,6 +30,8 @@ #include <minikin/FamilyVariant.h> #include <minikin/Hyphenator.h> +#include <shader/Shader.h> + namespace android { class Paint : public SkPaint { @@ -149,8 +151,14 @@ public: // The only respected flags are : [ antialias, dither, filterBitmap ] static uint32_t GetSkPaintJavaFlags(const SkPaint&); static void SetSkPaintJavaFlags(SkPaint*, uint32_t flags); + + void setShader(sk_sp<uirenderer::Shader> shader); private: + + using SkPaint::setShader; + using SkPaint::setImageFilter; + SkFont mFont; sk_sp<SkDrawLooper> mLooper; @@ -169,6 +177,7 @@ private: bool mStrikeThru = false; bool mUnderline = false; bool mDevKern = false; + sk_sp<uirenderer::Shader> mShader; }; } // namespace android diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index fa2674fc2f5e..21f60fd7b671 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -24,7 +24,8 @@ Paint::Paint() , mWordSpacing(0) , mFontFeatureSettings() , mMinikinLocaleListId(0) - , mFamilyVariant(minikin::FamilyVariant::DEFAULT) { + , mFamilyVariant(minikin::FamilyVariant::DEFAULT) + , mShader(nullptr) { // SkPaint::antialiasing defaults to false, but // SkFont::edging defaults to kAntiAlias. To keep them // insync, we manually set the font to kAilas. @@ -45,7 +46,8 @@ Paint::Paint(const Paint& paint) , mAlign(paint.mAlign) , mStrikeThru(paint.mStrikeThru) , mUnderline(paint.mUnderline) - , mDevKern(paint.mDevKern) {} + , mDevKern(paint.mDevKern) + , mShader(paint.mShader){} Paint::~Paint() {} @@ -65,9 +67,30 @@ Paint& Paint::operator=(const Paint& other) { mStrikeThru = other.mStrikeThru; mUnderline = other.mUnderline; mDevKern = other.mDevKern; + mShader = other.mShader; return *this; } +void Paint::setShader(sk_sp<uirenderer::Shader> shader) { + if (shader) { + // If there is an SkShader compatible shader, apply it + sk_sp<SkShader> skShader = shader->asSkShader(); + if (skShader.get()) { + SkPaint::setShader(skShader); + SkPaint::setImageFilter(nullptr); + } else { + // ... otherwise the specified shader can only be represented as an ImageFilter + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(shader->asSkImageFilter()); + } + } else { + // No shader is provided at all, clear out both the SkShader and SkImageFilter slots + SkPaint::setShader(nullptr); + SkPaint::setImageFilter(nullptr); + } + mShader = shader; +} + bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index df8635a8fe5a..554674a331cd 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -47,6 +47,7 @@ #include <minikin/LocaleList.h> #include <minikin/Measurement.h> #include <minikin/MinikinPaint.h> +#include <shader/Shader.h> #include <unicode/utf16.h> #include <cassert> @@ -54,6 +55,8 @@ #include <memory> #include <vector> +using namespace android::uirenderer; + namespace android { struct JMetricsID { @@ -782,11 +785,10 @@ namespace PaintGlue { return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE; } - static jlong setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { - Paint* obj = reinterpret_cast<Paint*>(objHandle); - SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle); - obj->setShader(sk_ref_sp(shader)); - return reinterpret_cast<jlong>(obj->getShader()); + static void setShader(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong shaderHandle) { + auto* paint = reinterpret_cast<Paint*>(objHandle); + auto* shader = reinterpret_cast<Shader*>(shaderHandle); + paint->setShader(sk_ref_sp(shader)); } static jlong setColorFilter(CRITICAL_JNI_PARAMS_COMMA jlong objHandle, jlong filterHandle) { @@ -1097,7 +1099,7 @@ static const JNINativeMethod methods[] = { {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin}, {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin}, {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath}, - {"nSetShader","(JJ)J", (void*) PaintGlue::setShader}, + {"nSetShader","(JJ)V", (void*) PaintGlue::setShader}, {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter}, {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode}, {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect}, diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index e76aace601be..9b1972ed664f 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -5,6 +5,13 @@ #include "SkShader.h" #include "SkBlendMode.h" #include "include/effects/SkRuntimeEffect.h" +#include "shader/Shader.h" +#include "shader/BitmapShader.h" +#include "shader/ComposeShader.h" +#include "shader/LinearGradientShader.h" +#include "shader/RadialGradientShader.h" +#include "shader/RuntimeShader.h" +#include "shader/SweepGradientShader.h" #include <vector> @@ -50,7 +57,7 @@ static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvAr /////////////////////////////////////////////////////////////////////////////////////////////// -static void Shader_safeUnref(SkShader* shader) { +static void Shader_safeUnref(Shader* shader) { SkSafeUnref(shader); } @@ -74,15 +81,15 @@ static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, j SkBitmap bitmap; image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); } - sk_sp<SkShader> shader = image->makeShader( - (SkTileMode)tileModeX, (SkTileMode)tileModeY); - ThrowIAE_IfNull(env, shader.get()); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new BitmapShader( + image, + static_cast<SkTileMode>(tileModeX), + static_cast<SkTileMode>(tileModeY), + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -118,17 +125,18 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr)); - ThrowIAE_IfNull(env, shader); - - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shader = new LinearGradientShader( + pts, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -148,17 +156,20 @@ static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new RadialGradientShader( + center, + radius, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + static_cast<SkTileMode>(tileMode), + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////// @@ -174,54 +185,58 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat #error Need to convert float array to SkScalar array before calling the following function. #endif - sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], - GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), - sGradientShaderFlags, nullptr); - ThrowIAE_IfNull(env, shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - if (matrix) { - shader = shader->makeWithLocalMatrix(*matrix); - } + auto* shader = new SweepGradientShader( + x, + y, + colors, + GraphicsJNI::getNativeColorSpace(colorSpaceHandle), + pos, + sGradientShaderFlags, + matrix + ); - return reinterpret_cast<jlong>(shader.release()); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr, jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) { - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle); - SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle); - SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle); - sk_sp<SkShader> baseShader(SkShaders::Blend(mode, - sk_ref_sp(shaderA), sk_ref_sp(shaderB))); - - SkShader* shader; - - if (matrix) { - shader = baseShader->makeWithLocalMatrix(*matrix).release(); - } else { - shader = baseShader.release(); - } - return reinterpret_cast<jlong>(shader); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); + auto* shaderA = reinterpret_cast<Shader*>(shaderAHandle); + auto* shaderB = reinterpret_cast<Shader*>(shaderBHandle); + + auto mode = static_cast<SkBlendMode>(xfermodeHandle); + + auto* composeShader = new ComposeShader( + *shaderA, + *shaderB, + mode, + matrix + ); + + return reinterpret_cast<jlong>(composeShader); } /////////////////////////////////////////////////////////////////////////////////////////////// static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr, jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) { - SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); + auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory); AutoJavaByteArray arInputs(env, inputs); - sk_sp<SkData> fData; - fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); - const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE); - ThrowIAE_IfNull(env, shader); + auto data = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length()); + auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr); - return reinterpret_cast<jlong>(shader.release()); + auto* shader = new RuntimeShader( + *effect, + std::move(data), + isOpaque == JNI_TRUE, + matrix + ); + return reinterpret_cast<jlong>(shader); } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -239,12 +254,8 @@ static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sks /////////////////////////////////////////////////////////////////////////////////////////////// -static void Effect_safeUnref(SkRuntimeEffect* effect) { - SkSafeUnref(effect); -} - static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref)); + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref)); } /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index fc594da19708..e817ca744c58 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -184,7 +184,9 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz, proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer); } proxy->setSurface(window, enableTimeout); - ANativeWindow_release(window); + if (window) { + ANativeWindow_release(window); + } } static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz, diff --git a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp index 9cffceb308c8..a1adcb30e80d 100644 --- a/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp +++ b/libs/hwui/jni/android_graphics_drawable_VectorDrawable.cpp @@ -143,13 +143,13 @@ static void updateFullPathPropertiesAndStrokeStyles(JNIEnv*, jobject, jlong full static void updateFullPathFillGradient(JNIEnv*, jobject, jlong pathPtr, jlong fillGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* fillShader = reinterpret_cast<SkShader*>(fillGradientPtr); + auto* fillShader = reinterpret_cast<Shader*>(fillGradientPtr); path->mutateStagingProperties()->setFillGradient(fillShader); } static void updateFullPathStrokeGradient(JNIEnv*, jobject, jlong pathPtr, jlong strokeGradientPtr) { VectorDrawable::FullPath* path = reinterpret_cast<VectorDrawable::FullPath*>(pathPtr); - SkShader* strokeShader = reinterpret_cast<SkShader*>(strokeGradientPtr); + auto* strokeShader = reinterpret_cast<Shader*>(strokeGradientPtr); path->mutateStagingProperties()->setStrokeGradient(strokeShader); } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index aad0cca80cdc..b51f6dcfc66f 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -77,10 +77,10 @@ void RenderProxy::setName(const char* name) { } void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { - ANativeWindow_acquire(window); + if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { mContext->setSurface(win, enableTimeout); - ANativeWindow_release(win); + if (win) { ANativeWindow_release(win); } }); } diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 565fb61c8994..4dcbc4458e97 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -131,8 +131,7 @@ RenderThread::RenderThread() , mFrameCallbackTaskPending(false) , mRenderState(nullptr) , mEglManager(nullptr) - , mFunctorManager(WebViewFunctorManager::instance()) - , mVkManager(nullptr) { + , mFunctorManager(WebViewFunctorManager::instance()) { Properties::load(); start("RenderThread"); } @@ -166,7 +165,7 @@ void RenderThread::initThreadLocals() { initializeChoreographer(); mEglManager = new EglManager(); mRenderState = new RenderState(*this); - mVkManager = new VulkanManager(); + mVkManager = VulkanManager::getInstance(); mCacheManager = new CacheManager(); } @@ -196,7 +195,8 @@ void RenderThread::requireGlContext() { } void RenderThread::requireVkContext() { - if (mVkManager->hasVkContext()) { + // the getter creates the context in the event it had been destroyed by destroyRenderingContext + if (vulkanManager().hasVkContext()) { return; } mVkManager->initialize(); @@ -222,11 +222,16 @@ void RenderThread::destroyRenderingContext() { mEglManager->destroy(); } } else { - if (vulkanManager().hasVkContext()) { - setGrContext(nullptr); - vulkanManager().destroy(); - } + setGrContext(nullptr); + mVkManager.clear(); + } +} + +VulkanManager& RenderThread::vulkanManager() { + if (!mVkManager.get()) { + mVkManager = VulkanManager::getInstance(); } + return *mVkManager.get(); } void RenderThread::dumpGraphicsMemory(int fd) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index b8ce55650516..d7dc00b8a5c1 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -110,7 +110,7 @@ public: void setGrContext(sk_sp<GrDirectContext> cxt); CacheManager& cacheManager() { return *mCacheManager; } - VulkanManager& vulkanManager() { return *mVkManager; } + VulkanManager& vulkanManager(); sk_sp<Bitmap> allocateHardwareBitmap(SkBitmap& skBitmap); void dumpGraphicsMemory(int fd); @@ -188,7 +188,7 @@ private: sk_sp<GrDirectContext> mGrContext; CacheManager* mCacheManager; - VulkanManager* mVkManager; + sp<VulkanManager> mVkManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 249936eb485e..0c5cf682e566 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -57,12 +57,22 @@ static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& fe #define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) #define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) -void VulkanManager::destroy() { - if (VK_NULL_HANDLE != mCommandPool) { - mDestroyCommandPool(mDevice, mCommandPool, nullptr); - mCommandPool = VK_NULL_HANDLE; +sp<VulkanManager> VulkanManager::getInstance() { + // cache a weakptr to the context to enable a second thread to share the same vulkan state + static wp<VulkanManager> sWeakInstance = nullptr; + static std::mutex sLock; + + std::lock_guard _lock{sLock}; + sp<VulkanManager> vulkanManager = sWeakInstance.promote(); + if (!vulkanManager.get()) { + vulkanManager = new VulkanManager(); + sWeakInstance = vulkanManager; } + return vulkanManager; +} + +VulkanManager::~VulkanManager() { if (mDevice != VK_NULL_HANDLE) { mDeviceWaitIdle(mDevice); mDestroyDevice(mDevice, nullptr); @@ -73,6 +83,7 @@ void VulkanManager::destroy() { } mGraphicsQueue = VK_NULL_HANDLE; + mAHBUploadQueue = VK_NULL_HANDLE; mPresentQueue = VK_NULL_HANDLE; mDevice = VK_NULL_HANDLE; mPhysicalDevice = VK_NULL_HANDLE; @@ -175,6 +186,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe for (uint32_t i = 0; i < queueCount; i++) { if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { mGraphicsQueueIndex = i; + LOG_ALWAYS_FATAL_IF(queueProps[i].queueCount < 2); break; } } @@ -283,7 +295,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe queueNextPtr, // pNext 0, // VkDeviceQueueCreateFlags mGraphicsQueueIndex, // queueFamilyIndex - 1, // queueCount + 2, // queueCount queuePriorities, // pQueuePriorities }, { @@ -347,6 +359,7 @@ void VulkanManager::initialize() { this->setupDevice(mExtensions, mPhysicalDeviceFeatures2); mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue); // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { @@ -369,7 +382,8 @@ void VulkanManager::initialize() { } } -sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options) { +sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& options, + ContextType contextType) { auto getProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -381,7 +395,8 @@ sk_sp<GrDirectContext> VulkanManager::createContext(const GrContextOptions& opti backendContext.fInstance = mInstance; backendContext.fPhysicalDevice = mPhysicalDevice; backendContext.fDevice = mDevice; - backendContext.fQueue = mGraphicsQueue; + backendContext.fQueue = (contextType == ContextType::kRenderThread) ? mGraphicsQueue + : mAHBUploadQueue; backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; backendContext.fMaxAPIVersion = mAPIVersion; backendContext.fVkExtensions = &mExtensions; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 3f2df8d75d89..13335f32ef06 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -43,10 +43,9 @@ class RenderThread; // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue, // which are re-used by CanvasContext. This class is created once and should be used by all vulkan // windowing contexts. The VulkanManager must be initialized before use. -class VulkanManager { +class VulkanManager final : public RefBase { public: - explicit VulkanManager() {} - ~VulkanManager() { destroy(); } + static sp<VulkanManager> getInstance(); // Sets up the vulkan context that is shared amonst all clients of the VulkanManager. This must // be call once before use of the VulkanManager. Multiple calls after the first will simiply @@ -68,9 +67,6 @@ public: Frame dequeueNextBuffer(VulkanSurface* surface); void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); - // Cleans up all the global state in the VulkanManger. - void destroy(); - // Inserts a wait on fence command into the Vulkan command buffer. status_t fenceWait(int fence, GrDirectContext* grContext); @@ -83,12 +79,24 @@ public: // the internal state of VulkanManager: VulkanManager must be alive to use the returned value. VkFunctorInitParams getVkFunctorInitParams() const; - sk_sp<GrDirectContext> createContext(const GrContextOptions& options); + + enum class ContextType { + kRenderThread, + kUploadThread + }; + + // returns a Skia graphic context used to draw content on the specified thread + sk_sp<GrDirectContext> createContext(const GrContextOptions& options, + ContextType contextType = ContextType::kRenderThread); uint32_t getDriverVersion() const { return mDriverVersion; } private: friend class VulkanSurface; + + explicit VulkanManager() {} + ~VulkanManager(); + // Sets up the VkInstance and VkDevice objects. Also fills out the passed in // VkPhysicalDeviceFeatures struct. void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&); @@ -154,6 +162,7 @@ private: uint32_t mGraphicsQueueIndex; VkQueue mGraphicsQueue = VK_NULL_HANDLE; + VkQueue mAHBUploadQueue = VK_NULL_HANDLE; uint32_t mPresentQueueIndex; VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; diff --git a/libs/hwui/shader/BitmapShader.cpp b/libs/hwui/shader/BitmapShader.cpp new file mode 100644 index 000000000000..fe653e85a021 --- /dev/null +++ b/libs/hwui/shader/BitmapShader.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "BitmapShader.h" + +#include "SkImagePriv.h" + +namespace android::uirenderer { +BitmapShader::BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix) + : Shader(matrix), skShader(image->makeShader(tileModeX, tileModeY)) {} + +sk_sp<SkShader> BitmapShader::makeSkShader() { + return skShader; +} + +BitmapShader::~BitmapShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/BitmapShader.h b/libs/hwui/shader/BitmapShader.h new file mode 100644 index 000000000000..ed6a6e6802d1 --- /dev/null +++ b/libs/hwui/shader/BitmapShader.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a Bitmap as either a SkShader or SkImageFilter + */ +class BitmapShader : public Shader { +public: + BitmapShader(const sk_sp<SkImage>& image, const SkTileMode tileModeX, + const SkTileMode tileModeY, const SkMatrix* matrix); + ~BitmapShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/ComposeShader.cpp b/libs/hwui/shader/ComposeShader.cpp new file mode 100644 index 000000000000..3765489e7431 --- /dev/null +++ b/libs/hwui/shader/ComposeShader.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "ComposeShader.h" + +#include "SkImageFilters.h" +#include "SkShader.h" + +namespace android::uirenderer { + +ComposeShader::ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix) + : Shader(matrix) { + // If both Shaders can be represented as SkShaders then use those, if not + // create an SkImageFilter from both Shaders and create the equivalent SkImageFilter + sk_sp<SkShader> skShaderA = shaderA.asSkShader(); + sk_sp<SkShader> skShaderB = shaderB.asSkShader(); + if (skShaderA.get() && skShaderB.get()) { + skShader = SkShaders::Blend(blendMode, skShaderA, skShaderB); + skImageFilter = nullptr; + } else { + sk_sp<SkImageFilter> skImageFilterA = shaderA.asSkImageFilter(); + sk_sp<SkImageFilter> skImageFilterB = shaderB.asSkImageFilter(); + skShader = nullptr; + skImageFilter = SkImageFilters::Xfermode(blendMode, skImageFilterA, skImageFilterB); + } +} + +sk_sp<SkShader> ComposeShader::makeSkShader() { + return skShader; +} + +sk_sp<SkImageFilter> ComposeShader::makeSkImageFilter() { + return skImageFilter; +} + +ComposeShader::~ComposeShader() {} +} // namespace android::uirenderer diff --git a/libs/hwui/shader/ComposeShader.h b/libs/hwui/shader/ComposeShader.h new file mode 100644 index 000000000000..a246b520d46a --- /dev/null +++ b/libs/hwui/shader/ComposeShader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that can composite 2 Shaders together with the specified blend mode. + * This implementation can appropriately convert the composed result to either an SkShader or + * SkImageFilter depending on the inputs + */ +class ComposeShader : public Shader { +public: + ComposeShader(Shader& shaderA, Shader& shaderB, const SkBlendMode blendMode, + const SkMatrix* matrix); + ~ComposeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + sk_sp<SkImageFilter> makeSkImageFilter() override; + +private: + sk_sp<SkShader> skShader; + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/LinearGradientShader.cpp b/libs/hwui/shader/LinearGradientShader.cpp new file mode 100644 index 000000000000..868fa44fb4b7 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "LinearGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +LinearGradientShader::LinearGradientShader(const SkPoint pts[2], + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeLinear(pts, colors.data(), colorspace, pos, colors.size(), + tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> LinearGradientShader::makeSkShader() { + return skShader; +} + +LinearGradientShader::~LinearGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/LinearGradientShader.h b/libs/hwui/shader/LinearGradientShader.h new file mode 100644 index 000000000000..596f4e009448 --- /dev/null +++ b/libs/hwui/shader/LinearGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp of colors to either as either SkShader or + * SkImageFilter + */ +class LinearGradientShader : public Shader { +public: + LinearGradientShader(const SkPoint pts[2], const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~LinearGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/RadialGradientShader.cpp b/libs/hwui/shader/RadialGradientShader.cpp new file mode 100644 index 000000000000..21ff56fee2f8 --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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. + */ +#include "RadialGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" + +namespace android::uirenderer { + +RadialGradientShader::RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, + sk_sp<SkColorSpace> colorspace, const SkScalar pos[], + const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeRadial(center, radius, colors.data(), colorspace, pos, + colors.size(), tileMode, shaderFlags, nullptr)) {} + +sk_sp<SkShader> RadialGradientShader::makeSkShader() { + return skShader; +} + +RadialGradientShader::~RadialGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RadialGradientShader.h b/libs/hwui/shader/RadialGradientShader.h new file mode 100644 index 000000000000..9a2ff139aedb --- /dev/null +++ b/libs/hwui/shader/RadialGradientShader.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp from the center outward to either as either + * a SkShader or SkImageFilter + */ +class RadialGradientShader : public Shader { +public: + RadialGradientShader(const SkPoint& center, const float radius, + const std::vector<SkColor4f>& colors, sk_sp<SkColorSpace> colorSpace, + const SkScalar pos[], const SkTileMode tileMode, const uint32_t shaderFlags, + const SkMatrix* matrix); + ~RadialGradientShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.cpp b/libs/hwui/shader/RuntimeShader.cpp new file mode 100644 index 000000000000..dd0b6980841a --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "RuntimeShader.h" + +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +RuntimeShader::RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix) + : Shader(nullptr) + , // Explicitly passing null as RuntimeShader is created with the + // matrix directly + skShader(effect.makeShader(std::move(data), nullptr, 0, matrix, isOpaque)) {} + +sk_sp<SkShader> RuntimeShader::makeSkShader() { + return skShader; +} + +RuntimeShader::~RuntimeShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/RuntimeShader.h b/libs/hwui/shader/RuntimeShader.h new file mode 100644 index 000000000000..7fe0b0206467 --- /dev/null +++ b/libs/hwui/shader/RuntimeShader.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" +#include "include/effects/SkRuntimeEffect.h" + +namespace android::uirenderer { + +/** + * RuntimeShader implementation that can map to either a SkShader or SkImageFilter + */ +class RuntimeShader : public Shader { +public: + RuntimeShader(SkRuntimeEffect& effect, sk_sp<SkData> data, bool isOpaque, + const SkMatrix* matrix); + ~RuntimeShader() override; + +protected: + sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/Shader.cpp b/libs/hwui/shader/Shader.cpp new file mode 100644 index 000000000000..45123dd55002 --- /dev/null +++ b/libs/hwui/shader/Shader.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "Shader.h" + +#include "SkImageFilters.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +namespace android::uirenderer { + +Shader::Shader(const SkMatrix* matrix) + : localMatrix(matrix ? *matrix : SkMatrix::I()) + , skShader(nullptr) + , skImageFilter(nullptr) {} + +Shader::~Shader() {} + +sk_sp<SkShader> Shader::asSkShader() { + // If we already have created a shader with these parameters just return the existing + // shader we have already created + if (!this->skShader.get()) { + this->skShader = makeSkShader(); + if (this->skShader.get()) { + if (!localMatrix.isIdentity()) { + this->skShader = this->skShader->makeWithLocalMatrix(localMatrix); + } + } + } + return this->skShader; +} + +/** + * By default return null as we cannot convert all visual effects to SkShader instances + */ +sk_sp<SkShader> Shader::makeSkShader() { + return nullptr; +} + +sk_sp<SkImageFilter> Shader::asSkImageFilter() { + // If we already have created an ImageFilter with these parameters just return the existing + // ImageFilter we have already created + if (!this->skImageFilter.get()) { + // Attempt to create an SkImageFilter from the current Shader implementation + this->skImageFilter = makeSkImageFilter(); + if (this->skImageFilter) { + if (!localMatrix.isIdentity()) { + // If we have created an SkImageFilter and we have a transformation, wrap + // the created SkImageFilter to apply the given matrix + this->skImageFilter = SkImageFilters::MatrixTransform( + localMatrix, kMedium_SkFilterQuality, this->skImageFilter); + } + } else { + // Otherwise if no SkImageFilter implementation is provided, create one from + // the result of asSkShader. Note the matrix is already applied to the shader in + // this case so just convert it to an SkImageFilter using SkImageFilters::Paint + SkPaint paint; + paint.setShader(asSkShader()); + sk_sp<SkImageFilter> paintFilter = SkImageFilters::Paint(paint); + this->skImageFilter = SkImageFilters::Xfermode(SkBlendMode::kDstIn, + std::move(paintFilter)); + } + } + return this->skImageFilter; +} + +/** + * By default return null for subclasses to implement. If there is not a direct SkImageFilter + * conversion + */ +sk_sp<SkImageFilter> Shader::makeSkImageFilter() { + return nullptr; +} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h new file mode 100644 index 000000000000..3c0cdaae8253 --- /dev/null +++ b/libs/hwui/shader/Shader.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "SkImageFilter.h" +#include "SkShader.h" +#include "SkPaint.h" +#include "SkRefCnt.h" + +class SkMatrix; + +namespace android::uirenderer { + +/** + * Shader class that can optionally wrap an SkShader or SkImageFilter depending + * on the implementation + */ +class Shader: public SkRefCnt { +public: + /** + * Creates a Shader instance with an optional transformation matrix + * @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter + */ + Shader(const SkMatrix* matrix); + virtual ~Shader(); + + /** + * Create an SkShader from the current Shader instance or return a previously + * created instance. This can be null if no SkShader could be created from this + * Shader instance. + */ + sk_sp<SkShader> asSkShader(); + + /** + * Create an SkImageFilter from the current Shader instance or return a previously + * created instance. Unlike asSkShader, this method cannot return null. + */ + sk_sp<SkImageFilter> asSkImageFilter(); + +protected: + /** + * Create a new SkShader instance based on this Shader instance + */ + virtual sk_sp<SkShader> makeSkShader(); + + /** + * Create a new SkImageFilter instance based on this Shader instance. If no SkImageFilter + * can be created then return nullptr + */ + virtual sk_sp<SkImageFilter> makeSkImageFilter(); + +private: + /** + * Optional matrix transform + */ + const SkMatrix localMatrix; + + /** + * Cached SkShader instance to be returned on subsequent queries + */ + sk_sp<SkShader> skShader; + + /** + * Cached SkImageFilter instance to be returned on subsequent queries + */ + sk_sp<SkImageFilter> skImageFilter; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/shader/SweepGradientShader.cpp b/libs/hwui/shader/SweepGradientShader.cpp new file mode 100644 index 000000000000..3b1f37f8b051 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "SweepGradientShader.h" + +#include <vector> + +#include "SkGradientShader.h" +#include "SkImageFilters.h" + +namespace android::uirenderer { + +SweepGradientShader::SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix) + : Shader(matrix) + , skShader(SkGradientShader::MakeSweep(x, y, colors.data(), colorspace, pos, colors.size(), + shaderFlags, nullptr)) {} + +sk_sp<SkShader> SweepGradientShader::makeSkShader() { + return skShader; +} + +SweepGradientShader::~SweepGradientShader() {} +} // namespace android::uirenderer
\ No newline at end of file diff --git a/libs/hwui/shader/SweepGradientShader.h b/libs/hwui/shader/SweepGradientShader.h new file mode 100644 index 000000000000..dad3ef0ffad4 --- /dev/null +++ b/libs/hwui/shader/SweepGradientShader.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include "Shader.h" +#include "SkShader.h" + +namespace android::uirenderer { + +/** + * Shader implementation that renders a color ramp clockwise such that the start and end colors + * are visible at 3 o'clock. This handles converting to either an SkShader or SkImageFilter + */ +class SweepGradientShader : public Shader { +public: + SweepGradientShader(float x, float y, const std::vector<SkColor4f>& colors, + const sk_sp<SkColorSpace>& colorspace, const SkScalar pos[], + const uint32_t shaderFlags, const SkMatrix* matrix); + virtual ~SweepGradientShader() override; + +protected: + virtual sk_sp<SkShader> makeSkShader() override; + +private: + sk_sp<SkShader> skShader; +}; +} // namespace android::uirenderer diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index c4067af388e3..e2c1651d823a 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -18,6 +18,7 @@ #include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" +#include <shader/BitmapShader.h> #include "utils/Color.h" class BitmapShaders; @@ -45,15 +46,24 @@ public: }); Paint paint; + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + hwuiBitmap->makeImage(), + SkTileMode::kRepeat, + SkTileMode::kRepeat, + nullptr + ); + sk_sp<SkImage> image = hwuiBitmap->makeImage(); - sk_sp<SkShader> repeatShader = - image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); - paint.setShader(std::move(repeatShader)); + paint.setShader(std::move(bitmapShader)); canvas.drawRoundRect(0, 0, 500, 500, 50.0f, 50.0f, paint); - sk_sp<SkShader> mirrorShader = - image->makeShader(SkTileMode::kMirror, SkTileMode::kMirror); - paint.setShader(std::move(mirrorShader)); + sk_sp<BitmapShader> mirrorBitmapShader = sk_make_sp<BitmapShader>( + image, + SkTileMode::kMirror, + SkTileMode::kMirror, + nullptr + ); + paint.setShader(std::move(mirrorBitmapShader)); canvas.drawRoundRect(0, 600, 500, 1100, 50.0f, 50.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 5886ea39acce..d37bc3c7d37c 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -20,6 +20,10 @@ #include <SkGradientShader.h> #include <SkImagePriv.h> #include <ui/PixelFormat.h> +#include <shader/BitmapShader.h> +#include <shader/LinearGradientShader.h> +#include <shader/RadialGradientShader.h> +#include <shader/ComposeShader.h> class HwBitmapInCompositeShader; @@ -50,20 +54,41 @@ public: pixels[4000 + 4 * i + 3] = 255; } buffer->unlock(); - sk_sp<Bitmap> hardwareBitmap(Bitmap::createFrom(buffer->toAHardwareBuffer(), - SkColorSpace::MakeSRGB())); - sk_sp<SkShader> hardwareShader(createBitmapShader(*hardwareBitmap)); + + sk_sp<BitmapShader> bitmapShader = sk_make_sp<BitmapShader>( + Bitmap::createFrom( + buffer->toAHardwareBuffer(), + SkColorSpace::MakeSRGB() + )->makeImage(), + SkTileMode::kClamp, + SkTileMode::kClamp, + nullptr + ); SkPoint center; center.set(50, 50); - SkColor colors[2]; - colors[0] = Color::Black; - colors[1] = Color::White; - sk_sp<SkShader> gradientShader = SkGradientShader::MakeRadial( - center, 50, colors, nullptr, 2, SkTileMode::kRepeat); - - sk_sp<SkShader> compositeShader( - SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kWhite; + + sk_sp<RadialGradientShader> radialShader = sk_make_sp<RadialGradientShader>( + center, + 50, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kRepeat, + 0, + nullptr + ); + + sk_sp<ComposeShader> compositeShader = sk_make_sp<ComposeShader>( + *bitmapShader.get(), + *radialShader.get(), + SkBlendMode::kDstATop, + nullptr + ); Paint paint; paint.setShader(std::move(compositeShader)); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index a9449b62a1f8..76e39deedd9a 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -17,7 +17,8 @@ #include "TestSceneBase.h" #include "tests/common/TestListViewSceneBase.h" #include "hwui/Paint.h" -#include <SkGradientShader.h> +#include "SkColor.h" +#include <shader/LinearGradientShader.h> class ListOfFadedTextAnimation; @@ -42,15 +43,26 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { pts[0].set(0, 0); pts[1].set(0, 1); - SkColor colors[2] = {Color::Black, Color::Transparent}; - sk_sp<SkShader> s( - SkGradientShader::MakeLinear(pts, colors, NULL, 2, SkTileMode::kClamp)); - SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); + + std::vector<SkColor4f> vColors(2); + vColors[0] = SkColors::kBlack; + vColors[1] = SkColors::kTransparent; + + sk_sp<LinearGradientShader> linearGradientShader = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + 0, + &matrix + ); + Paint fadingPaint; - fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); + fadingPaint.setShader(linearGradientShader); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); canvas.restore(); diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index a0bc5aa245d5..bdc157f85264 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -17,7 +17,7 @@ #include "TestSceneBase.h" #include <SkColorMatrixFilter.h> -#include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleColorMatrixAnimation; @@ -65,9 +65,12 @@ private: // enough renderer might apply it directly to the paint color) float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkColor colors[2] = {Color::DeepPurple_500, Color::DeepOrange_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors, pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> colors(2); + colors[0] = SkColor4f::FromColor(Color::DeepPurple_500); + colors[1] = SkColor4f::FromColor(Color::DeepOrange_500); + paint.setShader(sk_make_sp<LinearGradientShader>( + pts, colors, SkColorSpace::MakeSRGB(), pos, SkTileMode::kClamp, + 0, nullptr)); // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 57a260c8d234..9a15c9d370a4 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -17,6 +17,7 @@ #include "TestSceneBase.h" #include <SkGradientShader.h> +#include <shader/LinearGradientShader.h> class SimpleGradientAnimation; @@ -55,9 +56,24 @@ private: // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it - SkColor colors[3] = {Color::Transparent, Color::Black, Color::Cyan_500}; - paint.setShader(SkGradientShader::MakeLinear(pts, colors + (i % 2), pos, 2, - SkTileMode::kClamp)); + std::vector<SkColor4f> vColors(2); + vColors[0] = ((i % 2) == 0) ? + SkColor4f::FromColor(Color::Transparent) : + SkColor4f::FromColor(Color::Black); + vColors[1] = (((i + 1) % 2) == 0) ? + SkColor4f::FromColor(Color::Black) : + SkColor4f::FromColor(Color::Cyan_500); + + sk_sp<LinearGradientShader> gradient = sk_make_sp<LinearGradientShader>( + pts, + vColors, + SkColorSpace::MakeSRGB(), + pos, + SkTileMode::kClamp, + 0, + nullptr + ); + paint.setShader(gradient); canvas.drawRect(i, i, width, height, paint); } }); diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 6d4c57413f00..5e56b26f46f0 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -17,9 +17,14 @@ #include <gtest/gtest.h> #include "PathParser.h" +#include "GraphicsJNI.h" +#include "SkGradientShader.h" +#include "SkShader.h" #include "VectorDrawable.h" #include "utils/MathUtils.h" #include "utils/VectorDrawableUtils.h" +#include <shader/Shader.h> +#include <shader/LinearGradientShader.h> #include <functional> @@ -395,7 +400,21 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { bitmap.allocN32Pixels(5, 5, false); SkCanvas canvas(bitmap); - sk_sp<SkShader> shader = SkShaders::Color(SK_ColorBLACK); + SkPoint pts[2]; + pts[0].set(0, 0); + pts[1].set(0, 0); + + std::vector<SkColor4f> colors(2); + colors[0] = SkColors::kBlack; + colors[1] = SkColors::kBlack; + + sk_sp<LinearGradientShader> shader = sk_sp(new LinearGradientShader(pts, + colors, + SkColorSpace::MakeSRGB(), + nullptr, + SkTileMode::kClamp, + SkGradientShader::kInterpolateColorsInPremul_Flag, + nullptr)); // Initial ref count is 1 EXPECT_TRUE(shader->unique()); diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl index 21378c80fd56..77f7b54368f8 100644 --- a/media/java/android/media/session/ISession.aidl +++ b/media/java/android/media/session/ISession.aidl @@ -41,7 +41,8 @@ interface ISession { // These commands are for the TransportPerformer void setMetadata(in MediaMetadata metadata, long duration, String metadataDescription); void setPlaybackState(in PlaybackState state); - void setQueue(in ParceledListSlice queue); + void resetQueue(); + IBinder getBinderForSetQueue(); void setQueueTitle(CharSequence title); void setExtras(in Bundle extras); void setRatingType(int type); diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 70bd1609ddbd..6c41f7bcd0bc 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -25,7 +25,6 @@ import android.app.PendingIntent; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; -import android.content.pm.ParceledListSlice; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; @@ -36,6 +35,7 @@ import android.net.Uri; import android.os.BadParcelableException; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; @@ -491,7 +491,12 @@ public final class MediaSession { */ public void setQueue(@Nullable List<QueueItem> queue) { try { - mBinder.setQueue(queue == null ? null : new ParceledListSlice(queue)); + if (queue == null) { + mBinder.resetQueue(); + } else { + IBinder binder = mBinder.getBinderForSetQueue(); + ParcelableListBinder.send(binder, queue); + } } catch (RemoteException e) { Log.wtf("Dead object in setQueue.", e); } diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 6976a3569655..20006934ee46 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -523,7 +523,8 @@ public final class MediaSessionManager { * @param keyEvent The KeyEvent to send. * @hide */ - public void dispatchMediaKeyEventAsSystemService(KeyEvent keyEvent) { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public void dispatchMediaKeyEventAsSystemService(@NonNull KeyEvent keyEvent) { dispatchMediaKeyEventInternal(true, keyEvent, false); } @@ -548,6 +549,7 @@ public final class MediaSessionManager { * @return {@code true} if the event was sent to the session, {@code false} otherwise * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public boolean dispatchMediaKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { @@ -586,10 +588,15 @@ public final class MediaSessionManager { * Should be only called by the {@link com.android.internal.policy.PhoneWindow} or * {@link android.view.FallbackEventHandler} when the foreground activity didn't consume the key * from the hardware devices. + * <p> + * Valid stream types include {@link AudioManager.PublicStreamTypes} and + * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}. * - * @param keyEvent The KeyEvent to send. + * @param keyEvent volume key event + * @param streamType type of stream * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull KeyEvent keyEvent, int streamType) { dispatchVolumeKeyEventInternal(true, keyEvent, streamType, false); } @@ -614,6 +621,7 @@ public final class MediaSessionManager { * @param keyEvent volume key event * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public void dispatchVolumeKeyEventAsSystemService(@NonNull MediaSession.Token sessionToken, @NonNull KeyEvent keyEvent) { if (sessionToken == null) { diff --git a/media/java/android/media/session/ParcelableListBinder.java b/media/java/android/media/session/ParcelableListBinder.java new file mode 100644 index 000000000000..a7aacf2d8fca --- /dev/null +++ b/media/java/android/media/session/ParcelableListBinder.java @@ -0,0 +1,131 @@ +/* + * Copyright 2020 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 android.media.session; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +/** + * Binder to receive a list that has a large number of {@link Parcelable} items. + * + * It's similar to {@link android.content.pm.ParceledListSlice}, but transactions are performed in + * the opposite direction. + * + * @param <T> the type of {@link Parcelable} + * @hide + */ +public class ParcelableListBinder<T extends Parcelable> extends Binder { + + private static final int SUGGESTED_MAX_IPC_SIZE = IBinder.getSuggestedMaxIpcSizeBytes(); + + private static final int END_OF_PARCEL = 0; + private static final int ITEM_CONTINUED = 1; + + private final Consumer<List<T>> mConsumer; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final List<T> mList = new ArrayList<>(); + + @GuardedBy("mLock") + private int mCount; + + @GuardedBy("mLock") + private boolean mConsumed; + + /** + * Creates an instance. + * + * @param consumer a consumer that consumes the list received + */ + public ParcelableListBinder(@NonNull Consumer<List<T>> consumer) { + mConsumer = consumer; + } + + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + if (code != FIRST_CALL_TRANSACTION) { + return super.onTransact(code, data, reply, flags); + } + List<T> listToBeConsumed; + synchronized (mLock) { + if (mConsumed) { + return false; + } + int i = mList.size(); + if (i == 0) { + mCount = data.readInt(); + } + while (i < mCount && data.readInt() != END_OF_PARCEL) { + mList.add(data.readParcelable(null)); + i++; + } + if (i >= mCount) { + listToBeConsumed = mList; + mConsumed = true; + } else { + listToBeConsumed = null; + } + } + if (listToBeConsumed != null) { + mConsumer.accept(listToBeConsumed); + } + return true; + } + + /** + * Sends a list of {@link Parcelable} to a binder. + * + * @param binder a binder interface backed by {@link ParcelableListBinder} + * @param list a list to send + */ + public static <T extends Parcelable> void send(@NonNull IBinder binder, @NonNull List<T> list) + throws RemoteException { + int count = list.size(); + int i = 0; + while (i < count) { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + if (i == 0) { + data.writeInt(count); + } + while (i < count && data.dataSize() < SUGGESTED_MAX_IPC_SIZE) { + data.writeInt(ITEM_CONTINUED); + data.writeParcelable(list.get(i), 0); + i++; + } + if (i < count) { + data.writeInt(END_OF_PARCEL); + } + binder.transact(FIRST_CALL_TRANSACTION, data, reply, 0); + reply.recycle(); + data.recycle(); + } + } +} diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index 617a83f05791..8892a2954afb 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -46,6 +46,13 @@ package android.media.session { field public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 65536; // 0x10000 } + public final class MediaSessionManager { + method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent); + method public boolean dispatchMediaKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int); + method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.media.session.MediaSession.Token, @NonNull android.view.KeyEvent); + } + } package android.os { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index dbf9e9fb4ae5..3fd0ee1c8011 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -11037,6 +11037,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml index b8e1edc46da7..20aa5f79c5cc 100644 --- a/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml +++ b/packages/CarSystemUI/samples/sample1/rro/res/xml/car_sysui_overlays.xml @@ -22,7 +22,8 @@ <item target="attr/icon" value="@attr/icon"/> <item target="attr/selectedIcon" value="@attr/selectedIcon"/> - <item target="attr/intent" value="@attr/longIntent"/> + <item target="attr/intent" value="@attr/intent"/> + <item target="attr/longIntent" value="@attr/longIntent"/> <item target="attr/componentNames" value="@attr/componentNames"/> <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/> <item target="attr/categories" value="@attr/categories"/> diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 9d06c8467e41..72a6074ff89c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -465,7 +465,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + // Add disconnected bluetooth devices only when phone output device is available. + for (MediaDevice device : devices) { + final int type = device.getDeviceType(); + if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE + || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { + mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + break; + } + } } final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index a654fd47ea12..8e850b25159c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -585,6 +585,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -683,6 +684,7 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id"); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index bdf00e38ce94..bcd2ff71b57f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -175,5 +175,6 @@ public class SecureSettings { Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, Settings.Secure.PANIC_GESTURE_ENABLED, Settings.Secure.PANIC_SOUND_ENABLED, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 4ea7f3a23c10..3630f257f583 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -263,5 +263,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.PANIC_SOUND_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ADAPTIVE_CONNECTIVITY_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 47cc6d0fd752..66be8dd68283 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1976,6 +1976,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS); + dumpSetting(s, p, + Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED, + SecureSettingsProto.ADAPTIVE_CONNECTIVITY_ENABLED); final long controlsToken = p.start(SecureSettingsProto.CONTROLS); dumpSetting(s, p, diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index b42d71abaa6d..df66bf5a1051 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -45,4 +45,5 @@ -keep class com.android.systemui.dagger.GlobalRootComponent { *; } -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; } --keep class com.android.systemui.dagger.Dagger** { *; }
\ No newline at end of file +-keep class com.android.systemui.dagger.Dagger** { *; } +-keep class com.android.systemui.tv.Dagger** { *; }
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/bubble_stack_user_education.xml b/packages/SystemUI/res/layout/bubble_stack_user_education.xml index 616403219bc6..fe1ed4b6f726 100644 --- a/packages/SystemUI/res/layout/bubble_stack_user_education.xml +++ b/packages/SystemUI/res/layout/bubble_stack_user_education.xml @@ -15,8 +15,8 @@ ~ limitations under the License. --> <LinearLayout + android:id="@+id/stack_education_layout" xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/user_education_view" android:layout_height="wrap_content" android:layout_width="wrap_content" android:paddingTop="48dp" @@ -25,26 +25,29 @@ android:paddingEnd="16dp" android:layout_marginEnd="24dp" android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg"> - + android:background="@drawable/bubble_stack_user_education_bg" + > <TextView - android:id="@+id/user_education_title" + android:id="@+id/stack_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="16dp" android:fontFamily="@*android:string/config_bodyFontFamilyMedium" android:maxLines="2" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_title" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> <TextView - android:id="@+id/user_education_description" + android:id="@+id/stack_education_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" android:text="@string/bubbles_user_education_description" android:fontFamily="@*android:string/config_bodyFontFamily" android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml index 213bb923db65..b51dc93dc373 100644 --- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml +++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml @@ -14,77 +14,77 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.bubbles.ManageEducationView + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" + android:id="@+id/manage_education_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + android:paddingTop="28dp" + android:paddingBottom="16dp" + android:paddingStart="@dimen/bubble_expanded_view_padding" + android:paddingEnd="48dp" + android:layout_marginEnd="24dp" + android:orientation="vertical" + android:background="@drawable/bubble_stack_user_education_bg" > - <LinearLayout + + <TextView + android:id="@+id/user_education_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:id="@+id/manage_education_view" - android:clickable="true" - android:paddingTop="28dp" + android:paddingStart="16dp" android:paddingBottom="16dp" - android:paddingStart="@dimen/bubble_expanded_view_padding" - android:paddingEnd="48dp" - android:layout_marginEnd="24dp" - android:orientation="vertical" - android:background="@drawable/bubble_stack_user_education_bg" - > + android:fontFamily="@*android:string/config_bodyFontFamilyMedium" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:text="@string/bubbles_user_education_manage_title" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> - <TextView - android:id="@+id/user_education_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="16dp" - android:fontFamily="@*android:string/config_bodyFontFamilyMedium" - android:maxLines="2" - android:ellipsize="end" - android:text="@string/bubbles_user_education_manage_title" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Headline"/> + <TextView + android:id="@+id/user_education_description" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingBottom="24dp" + android:text="@string/bubbles_user_education_manage" + android:maxLines="2" + android:ellipsize="end" + android:gravity="start" + android:textAlignment="viewStart" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> - <TextView - android:id="@+id/user_education_description" + <LinearLayout + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:id="@+id/button_layout" + android:orientation="horizontal" > + + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/manage" + android:layout_gravity="start" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingBottom="24dp" - android:text="@string/bubbles_user_education_manage" - android:maxLines="2" - android:ellipsize="end" - android:fontFamily="@*android:string/config_bodyFontFamily" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body2"/> + android:focusable="true" + android:clickable="false" + android:text="@string/manage_bubbles_text" + android:textColor="?attr/wallpaperTextColor" + /> - <LinearLayout - android:layout_height="wrap_content" + <com.android.systemui.statusbar.AlphaOptimizedButton + style="@android:style/Widget.Material.Button.Borderless" + android:id="@+id/got_it" + android:layout_gravity="start" android:layout_width="wrap_content" - android:id="@+id/button_layout" - android:orientation="horizontal" > - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/manage" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:clickable="false" - android:text="@string/manage_bubbles_text" - android:textColor="?attr/wallpaperTextColor" - /> - - <com.android.systemui.statusbar.AlphaOptimizedButton - style="@android:style/Widget.Material.Button.Borderless" - android:id="@+id/got_it" - android:layout_gravity="start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:focusable="true" - android:text="@string/bubbles_user_education_got_it" - android:textColor="?attr/wallpaperTextColor" - /> - </LinearLayout> + android:layout_height="wrap_content" + android:focusable="true" + android:text="@string/bubbles_user_education_got_it" + android:textColor="?attr/wallpaperTextColor" + /> </LinearLayout> -</com.android.systemui.bubbles.ManageEducationView> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index 732758a2ded2..31a33fbfc308 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -5,6 +5,6 @@ android:id="@+id/udfps_view" android:layout_width="match_parent" android:layout_height="match_parent" - systemui:sensorRadius="140px" - systemui:sensorMarginBottom="630px" + systemui:sensorRadius="130px" + systemui:sensorCenterY="1636px" systemui:sensorTouchAreaCoefficient="0.5"/> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 84dbd60b799d..a62502965dd2 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -159,7 +159,7 @@ <declare-styleable name="UdfpsView"> <attr name="sensorRadius" format="dimension"/> - <attr name="sensorMarginBottom" format="dimension"/> + <attr name="sensorCenterY" format="dimension"/> <attr name="sensorPressureCoefficient" format="float"/> <attr name="sensorTouchAreaCoefficient" format="float"/> </declare-styleable> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 765a9422585a..d12f0103238d 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1342,7 +1342,7 @@ <dimen name="controls_app_divider_height">2dp</dimen> <dimen name="controls_app_divider_side_margin">32dp</dimen> - <dimen name="controls_card_margin">2dp</dimen> + <dimen name="controls_card_margin">@dimen/control_base_item_margin</dimen> <item name="control_card_elevation" type="dimen" format="float">15</item> <dimen name="controls_dialog_padding">32dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 59af458e2402..17b840cc7a20 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -162,8 +162,8 @@ public class ExpandHelper implements Gefingerpoken { * * @param context application context * @param callback the container that holds the items to be manipulated - * @param small the smallest allowable size for the manuipulated items. - * @param large the largest allowable size for the manuipulated items. + * @param small the smallest allowable size for the manipulated items. + * @param large the largest allowable size for the manipulated items. */ public ExpandHelper(Context context, Callback callback, int small, int large) { mSmallSize = small; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 36353fa96956..d7e91384f049 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -53,12 +53,10 @@ public class UdfpsView extends View implements DozeReceiver, private final Paint mScrimPaint; private final Paint mDebugTextPaint; - private float mSensorX; - private float mSensorY; private final RectF mSensorRect; private final Paint mSensorPaint; private final float mSensorRadius; - private final float mSensorMarginBottom; + private final float mSensorCenterY; private final float mSensorTouchAreaCoefficient; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -66,6 +64,10 @@ public class UdfpsView extends View implements DozeReceiver, private final Rect mTouchableRegion; private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener; + // This is calculated from the screen's dimensions at runtime, as opposed to mSensorCenterY, + // which is defined in layout.xml + private float mSensorCenterX; + // AOD anti-burn-in offsets private float mInterpolatedDarkAmount; private float mBurnInOffsetX; @@ -84,7 +86,7 @@ public class UdfpsView extends View implements DozeReceiver, if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) { throw new IllegalArgumentException("UdfpsView must contain sensorRadius"); } - if (!a.hasValue(R.styleable.UdfpsView_sensorMarginBottom)) { + if (!a.hasValue(R.styleable.UdfpsView_sensorCenterY)) { throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom"); } if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) { @@ -92,7 +94,7 @@ public class UdfpsView extends View implements DozeReceiver, "UdfpsView must contain sensorTouchAreaCoefficient"); } mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f); - mSensorMarginBottom = a.getDimension(R.styleable.UdfpsView_sensorMarginBottom, 0f); + mSensorCenterY = a.getDimension(R.styleable.UdfpsView_sensorCenterY, 0f); mSensorTouchAreaCoefficient = a.getFloat( R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f); } finally { @@ -163,10 +165,9 @@ public class UdfpsView extends View implements DozeReceiver, final int h = getLayoutParams().height; final int w = getLayoutParams().width; mScrimRect.set(0 /* left */, 0 /* top */, w, h); - mSensorX = w / 2f; - mSensorY = h - mSensorMarginBottom - mSensorRadius; - mSensorRect.set(mSensorX - mSensorRadius, mSensorY - mSensorRadius, - mSensorX + mSensorRadius, mSensorY + mSensorRadius); + mSensorCenterX = w / 2f; + mSensorRect.set(mSensorCenterX - mSensorRadius, mSensorCenterY - mSensorRadius, + mSensorCenterX + mSensorRadius, mSensorCenterY + mSensorRadius); // Sets mTouchableRegion with rounded up values from mSensorRect. mSensorRect.roundOut(mTouchableRegion); @@ -210,10 +211,10 @@ public class UdfpsView extends View implements DozeReceiver, } boolean isValidTouch(float x, float y, float pressure) { - return x > (mSensorX - mSensorRadius * mSensorTouchAreaCoefficient) - && x < (mSensorX + mSensorRadius * mSensorTouchAreaCoefficient) - && y > (mSensorY - mSensorRadius * mSensorTouchAreaCoefficient) - && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient); + return x > (mSensorCenterX - mSensorRadius * mSensorTouchAreaCoefficient) + && x < (mSensorCenterX + mSensorRadius * mSensorTouchAreaCoefficient) + && y > (mSensorCenterY - mSensorRadius * mSensorTouchAreaCoefficient) + && y < (mSensorCenterY + mSensorRadius * mSensorTouchAreaCoefficient); } void setScrimAlpha(int alpha) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index fa0d2ba84b4e..80150c9b7e32 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -305,7 +305,7 @@ public class BubbleExpandedView extends LinearLayout { mActivityView = new ActivityView(mContext, null /* attrs */, 0 /* defStyle */, true /* singleTaskInstance */, false /* usePublicVirtualDisplay*/, - true /* disableSurfaceViewBackgroundLayer */); + true /* disableSurfaceViewBackgroundLayer */, true /* useTrustedDisplay */); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index ec9644af7013..64df2b99ee22 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -19,11 +19,7 @@ package com.android.systemui.bubbles; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION; -import static com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; -import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_USER_EDUCATION; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -38,7 +34,6 @@ import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Outline; @@ -54,7 +49,6 @@ import android.provider.Settings; import android.util.Log; import android.view.Choreographer; import android.view.DisplayCutout; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -70,10 +64,8 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; -import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; @@ -82,7 +74,6 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Interpolators; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -115,10 +106,6 @@ public class BubbleStackView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES; - /** Animation durations for bubble stack user education views. **/ - static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200; - private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40; - /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */ static final float FLYOUT_DRAG_PERCENT_DISMISS = 0.25f; @@ -556,7 +543,7 @@ public class BubbleStackView extends FrameLayout // Otherwise, we either tapped the stack (which means we're collapsed // and should expand) or the currently selected bubble (we're expanded // and should collapse). - if (!maybeShowStackUserEducation()) { + if (!maybeShowStackEdu()) { mBubbleData.setExpanded(!mBubbleData.isExpanded()); } } @@ -582,7 +569,9 @@ public class BubbleStackView extends FrameLayout } if (mBubbleData.isExpanded()) { - maybeShowManageEducation(false /* show */); + if (mManageEduView != null) { + mManageEduView.hide(false /* show */); + } // If we're expanded, tell the animation controller to prepare to drag this bubble, // dispatching to the individual bubble magnet listener. @@ -637,7 +626,9 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.dragBubbleOut( v, viewInitialX + dx, viewInitialY + dy); } else { - hideStackUserEducation(false /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(false /* fromExpansion */); + } mStackAnimationController.moveStackFromTouch( viewInitialX + dx, viewInitialY + dy); } @@ -684,7 +675,7 @@ public class BubbleStackView extends FrameLayout private OnClickListener mFlyoutClickListener = new OnClickListener() { @Override public void onClick(View view) { - if (maybeShowStackUserEducation()) { + if (maybeShowStackEdu()) { // If we're showing user education, don't open the bubble show the education first mBubbleToExpandAfterFlyoutCollapse = null; } else { @@ -728,7 +719,7 @@ public class BubbleStackView extends FrameLayout mFlyout.removeCallbacks(mHideFlyout); animateFlyoutCollapsed(shouldDismiss, velX); - maybeShowStackUserEducation(); + maybeShowStackEdu(); } }; @@ -737,14 +728,8 @@ public class BubbleStackView extends FrameLayout @Nullable private BubbleOverflow mBubbleOverflow; - - private boolean mShouldShowUserEducation; - private boolean mAnimatingEducationAway; - private View mUserEducationView; - - private boolean mShouldShowManageEducation; - private ManageEducationView mManageEducationView; - private boolean mAnimatingManageEducationAway; + private StackEducationView mStackEduView; + private ManageEducationView mManageEduView; private ViewGroup mManageMenu; private ImageView mManageSettingsIcon; @@ -805,8 +790,6 @@ public class BubbleStackView extends FrameLayout onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; - setUpUserEducation(); - // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or // is centered. It greatly simplifies translation positioning/animations. Views that will // actually lay out differently in RTL, such as the flyout and expanded view, will set their @@ -819,6 +802,8 @@ public class BubbleStackView extends FrameLayout mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); + updateUserEdu(); + mExpandedViewContainer = new FrameLayout(context); mExpandedViewContainer.setElevation(elevation); mExpandedViewContainer.setClipChildren(false); @@ -1092,48 +1077,66 @@ public class BubbleStackView extends FrameLayout addView(mManageMenu); } - private void setUpUserEducation() { - if (mUserEducationView != null) { - removeView(mUserEducationView); - } - mShouldShowUserEducation = shouldShowBubblesEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowUserEducation: " + mShouldShowUserEducation); + /** + * Whether the educational view should show for the expanded view "manage" menu. + */ + private boolean shouldShowManageEdu() { + final boolean seen = Prefs.getBoolean(mContext, + Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false /* default */); + final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext)) + && mExpandedBubble != null; + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show manage edu: " + shouldShow); } - if (mShouldShowUserEducation) { - mUserEducationView = mInflater.inflate(R.layout.bubble_stack_user_education, this, - false /* attachToRoot */); - mUserEducationView.setVisibility(GONE); - - final TypedArray ta = mContext.obtainStyledAttributes( - new int[] {android.R.attr.colorAccent, - android.R.attr.textColorPrimaryInverse}); - final int bgColor = ta.getColor(0, Color.BLACK); - int textColor = ta.getColor(1, Color.WHITE); - ta.recycle(); - textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true); + return shouldShow; + } - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - title.setTextColor(textColor); - description.setTextColor(textColor); + private void maybeShowManageEdu() { + if (!shouldShowManageEdu()) { + return; + } + if (mManageEduView == null) { + mManageEduView = new ManageEducationView(mContext); + addView(mManageEduView); + } + mManageEduView.show(mExpandedBubble.getExpandedView(), mTempRect); + } - updateUserEducationForLayoutDirection(); - addView(mUserEducationView); + /** + * Whether education view should show for the collapsed stack. + */ + private boolean shouldShowStackEdu() { + final boolean seen = Prefs.getBoolean(getContext(), + Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION, false /* default */); + final boolean shouldShow = !seen || BubbleDebugConfig.forceShowUserEducation(mContext); + if (BubbleDebugConfig.DEBUG_USER_EDUCATION) { + Log.d(TAG, "Show stack edu: " + shouldShow); } + return shouldShow; + } - if (mManageEducationView != null) { - removeView(mManageEducationView); + /** + * @return true if education view for collapsed stack should show and was not showing before. + */ + private boolean maybeShowStackEdu() { + if (!shouldShowStackEdu()) { + return false; + } + if (mStackEduView == null) { + mStackEduView = new StackEducationView(mContext); + addView(mStackEduView); } - mShouldShowManageEducation = shouldShowManageEducation(); - if (DEBUG_USER_EDUCATION) { - Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation); + return mStackEduView.show(mStackAnimationController.getStartPosition()); + } + + private void updateUserEdu() { + maybeShowStackEdu(); + if (mManageEduView != null) { + mManageEduView.invalidate(); } - if (mShouldShowManageEducation) { - mManageEducationView = (ManageEducationView) - mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */, - false /* attachToRoot */); - addView(mManageEducationView); + maybeShowManageEdu(); + if (mStackEduView != null) { + mStackEduView.invalidate(); } } @@ -1164,9 +1167,9 @@ public class BubbleStackView extends FrameLayout */ public void onThemeChanged() { setUpFlyout(); - setUpUserEducation(); setUpManageMenu(); updateOverflow(); + updateUserEdu(); updateExpandedViewTheme(); } @@ -1197,12 +1200,11 @@ public class BubbleStackView extends FrameLayout public void onLayoutDirectionChanged(int direction) { mManageMenu.setLayoutDirection(direction); mFlyout.setLayoutDirection(direction); - if (mUserEducationView != null) { - mUserEducationView.setLayoutDirection(direction); - updateUserEducationForLayoutDirection(); + if (mStackEduView != null) { + mStackEduView.setLayoutDirection(direction); } - if (mManageEducationView != null) { - mManageEducationView.setLayoutDirection(direction); + if (mManageEduView != null) { + mManageEduView.setLayoutDirection(direction); } updateExpandedViewDirection(direction); } @@ -1446,7 +1448,7 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, "addBubble: " + bubble); } - if (getBubbleCount() == 0 && mShouldShowUserEducation) { + if (getBubbleCount() == 0 && shouldShowStackEdu()) { // Override the default stack position if we're showing user education. mStackAnimationController.setStackPosition( mStackAnimationController.getStartPosition()); @@ -1649,115 +1651,6 @@ public class BubbleStackView extends FrameLayout notifyExpansionChanged(mExpandedBubble, mIsExpanded); } - /** - * If necessary, shows the user education view for the bubble stack. This appears the first - * time a user taps on a bubble. - * - * @return true if user education was shown, false otherwise. - */ - private boolean maybeShowStackUserEducation() { - if (mShouldShowUserEducation && mUserEducationView.getVisibility() != VISIBLE) { - mUserEducationView.setAlpha(0); - mUserEducationView.setVisibility(VISIBLE); - updateUserEducationForLayoutDirection(); - - // Post so we have height of mUserEducationView - mUserEducationView.post(() -> { - final int viewHeight = mUserEducationView.getHeight(); - PointF stackPosition = mStackAnimationController.getStartPosition(); - final float translationY = stackPosition.y + (mBubbleSize / 2) - (viewHeight / 2); - mUserEducationView.setTranslationY(translationY); - mUserEducationView.animate() - .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION) - .setInterpolator(FAST_OUT_SLOW_IN) - .alpha(1); - }); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, true); - return true; - } - return false; - } - - private void updateUserEducationForLayoutDirection() { - if (mUserEducationView == null) { - return; - } - LinearLayout textLayout = mUserEducationView.findViewById(R.id.user_education_view); - TextView title = mUserEducationView.findViewById(R.id.user_education_title); - TextView description = mUserEducationView.findViewById(R.id.user_education_description); - boolean isLtr = - getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR; - if (isLtr) { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_LTR); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg); - title.setGravity(Gravity.LEFT); - description.setGravity(Gravity.LEFT); - } else { - mUserEducationView.setLayoutDirection(LAYOUT_DIRECTION_RTL); - textLayout.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl); - title.setGravity(Gravity.RIGHT); - description.setGravity(Gravity.RIGHT); - } - } - - /** - * If necessary, hides the user education view for the bubble stack. - * - * @param fromExpansion if true this indicates the hide is happening due to the bubble being - * expanded, false if due to a touch outside of the bubble stack. - */ - void hideStackUserEducation(boolean fromExpansion) { - if (mShouldShowUserEducation - && mUserEducationView.getVisibility() == VISIBLE - && !mAnimatingEducationAway) { - mAnimatingEducationAway = true; - mUserEducationView.animate() - .alpha(0) - .setDuration(fromExpansion - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingEducationAway = false; - mShouldShowUserEducation = shouldShowBubblesEducation(); - mUserEducationView.setVisibility(GONE); - }); - } - } - - /** - * If necessary, toggles the user education view for the manage button. This is shown when the - * bubble stack is expanded for the first time. - * - * @param show whether the user education view should show or not. - */ - void maybeShowManageEducation(boolean show) { - if (mManageEducationView == null) { - return; - } - if (show - && mShouldShowManageEducation - && mManageEducationView.getVisibility() != VISIBLE - && mIsExpanded - && mExpandedBubble.getExpandedView() != null) { - mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect, - () -> maybeShowManageEducation(false) /* run on click */); - Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true); - } else if (!show - && mManageEducationView.getVisibility() == VISIBLE - && !mAnimatingManageEducationAway) { - mManageEducationView.animate() - .alpha(0) - .setDuration(mIsExpansionAnimating - ? ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT - : ANIMATE_STACK_USER_EDUCATION_DURATION) - .withEndAction(() -> { - mAnimatingManageEducationAway = false; - mShouldShowManageEducation = shouldShowManageEducation(); - mManageEducationView.setVisibility(GONE); - }); - } - } - void showExpandedViewContents(int displayId) { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null @@ -1791,7 +1684,9 @@ public class BubbleStackView extends FrameLayout cancelDelayedExpandCollapseSwitchAnimations(); mIsExpanded = true; - hideStackUserEducation(true /* fromExpansion */); + if (mStackEduView != null) { + mStackEduView.hide(true /* fromExpansion */); + } beforeExpandedViewAnimation(); mBubbleContainer.setActiveController(mExpandedAnimationController); @@ -1799,7 +1694,9 @@ public class BubbleStackView extends FrameLayout updatePointerPosition(); mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); - maybeShowManageEducation(true); + if (mIsExpanded && mExpandedBubble.getExpandedView() != null) { + maybeShowManageEdu(); + } } /* after */); mExpandedViewContainer.setTranslationX(0); @@ -1936,7 +1833,9 @@ public class BubbleStackView extends FrameLayout .withEndActions(() -> { final BubbleViewProvider previouslySelected = mExpandedBubble; beforeExpandedViewAnimation(); - maybeShowManageEducation(false); + if (mManageEduView != null) { + mManageEduView.hide(false /* fromExpansion */); + } if (DEBUG_BUBBLE_STACK_VIEW) { Log.d(TAG, "animateCollapse"); @@ -2104,8 +2003,8 @@ public class BubbleStackView extends FrameLayout // from any location. if (!mIsExpanded || mShowingManage - || (mManageEducationView != null - && mManageEducationView.getVisibility() == VISIBLE)) { + || (mManageEduView != null + && mManageEduView.getVisibility() == VISIBLE)) { touchableRegion.setEmpty(); } } @@ -2289,7 +2188,7 @@ public class BubbleStackView extends FrameLayout if (flyoutMessage == null || flyoutMessage.message == null || !bubble.showFlyout() - || (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) + || (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress @@ -2398,7 +2297,7 @@ public class BubbleStackView extends FrameLayout * them. */ public void getTouchableRegion(Rect outRect) { - if (mUserEducationView != null && mUserEducationView.getVisibility() == VISIBLE) { + if (mStackEduView != null && mStackEduView.getVisibility() == VISIBLE) { // When user education shows then capture all touches outRect.set(0, 0, getWidth(), getHeight()); return; @@ -2770,18 +2669,6 @@ public class BubbleStackView extends FrameLayout return mExpandedBubble.getExpandedView().performBackPressIfNeeded(); } - /** Whether the educational view should appear for bubbles. **/ - private boolean shouldShowBubblesEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_EDUCATION, false); - } - - /** Whether the educational view should appear for the expanded view "manage" button. **/ - private boolean shouldShowManageEducation() { - return BubbleDebugConfig.forceShowUserEducation(getContext()) - || !Prefs.getBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, false); - } - /** For debugging only */ List<Bubble> getBubblesOnScreen() { List<Bubble> bubbles = new ArrayList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt index c58ab31c4561..26a9773f9bb8 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt @@ -18,52 +18,56 @@ package com.android.systemui.bubbles import android.content.Context import android.graphics.Color import android.graphics.Rect -import android.util.AttributeSet -import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.widget.Button import android.widget.LinearLayout import android.widget.TextView import com.android.internal.util.ContrastColorUtil import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_MANAGE_EDUCATION import com.android.systemui.R /** - * Educational view to highlight the manage button that allows a user to configure the settings + * User education view to highlight the manage button that allows a user to configure the settings * for the bubble. Shown only the first time a user expands a bubble. */ -class ManageEducationView @JvmOverloads constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { +class ManageEducationView constructor(context: Context) : LinearLayout(context) { + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleManageEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 private val manageView by lazy { findViewById<View>(R.id.manage_education_view) } private val manageButton by lazy { findViewById<Button>(R.id.manage) } private val gotItButton by lazy { findViewById<Button>(R.id.got_it) } private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) } private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) } - private var isInflated = false + + private var isHiding = false init { - this.visibility = View.GONE - this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() - this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE + LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this); + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE } - override fun setLayoutDirection(direction: Int) { - super.setLayoutDirection(direction) - // setLayoutDirection runs before onFinishInflate - // so skip if views haven't inflated; otherwise we'll get NPEs - if (!isInflated) return - setDirection() + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() } override fun onFinishInflate() { super.onFinishInflate() - isInflated = true - setDirection() + layoutDirection = resources.configuration.layoutDirection setTextColor() } @@ -78,29 +82,35 @@ class ManageEducationView @JvmOverloads constructor( descTextView.setTextColor(textColor) } - fun setDirection() { + private fun setDrawableDirection() { manageView.setBackgroundResource( if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL) R.drawable.bubble_stack_user_education_bg_rtl else R.drawable.bubble_stack_user_education_bg) - titleTextView.gravity = Gravity.START - descTextView.gravity = Gravity.START } - fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) { + /** + * If necessary, toggles the user education view for the manage button. This is shown when the + * bubble stack is expanded for the first time. + * + * @param show whether the user education view should show or not. + */ + fun show(expandedView: BubbleExpandedView, rect : Rect) { + if (visibility == VISIBLE) return + alpha = 0f visibility = View.VISIBLE post { expandedView.getManageButtonBoundsOnScreen(rect) - with(hideMenu) { - manageButton - .setOnClickListener { - expandedView.findViewById<View>(R.id.settings_button).performClick() - this.run() - } - gotItButton.setOnClickListener { this.run() } - setOnClickListener { this.run() } - } + + manageButton + .setOnClickListener { + expandedView.findViewById<View>(R.id.settings_button).performClick() + hide(true /* isStackExpanding */) + } + gotItButton.setOnClickListener { hide(true /* isStackExpanding */) } + setOnClickListener { hide(true /* isStackExpanding */) } + with(manageView) { translationX = 0f val inset = resources.getDimensionPixelSize( @@ -109,9 +119,27 @@ class ManageEducationView @JvmOverloads constructor( } bringToFront() animate() - .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong()) + .setDuration(ANIMATE_DURATION) .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) .alpha(1f) } + setShouldShow(false) + } + + fun hide(isStackExpanding: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .withStartAction { isHiding = true } + .alpha(0f) + .setDuration(if (isStackExpanding) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { + isHiding = false + visibility = GONE + }; + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_MANAGE_EDUCATION, !shouldShow) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt new file mode 100644 index 000000000000..3e4c729d8315 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2020 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.bubbles + +import android.content.Context +import android.graphics.Color +import android.graphics.PointF +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import android.widget.TextView +import com.android.internal.util.ContrastColorUtil +import com.android.systemui.Interpolators +import com.android.systemui.Prefs +import com.android.systemui.Prefs.Key.HAS_SEEN_BUBBLES_EDUCATION +import com.android.systemui.R + +/** + * User education view to highlight the collapsed stack of bubbles. + * Shown only the first time a user taps the stack. + */ +class StackEducationView constructor(context: Context) : LinearLayout(context){ + + private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView" + else BubbleDebugConfig.TAG_BUBBLES + + private val ANIMATE_DURATION : Long = 200 + private val ANIMATE_DURATION_SHORT : Long = 40 + + private val view by lazy { findViewById<View>(R.id.stack_education_layout) } + private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) } + private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) } + + private var isHiding = false + + init { + LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this); + + visibility = View.GONE + elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat() + + // BubbleStackView forces LTR by default + // since most of Bubble UI direction depends on positioning by the user. + // This view actually lays out differently in RTL, so we set layout LOCALE here. + layoutDirection = View.LAYOUT_DIRECTION_LOCALE + } + + override fun setLayoutDirection(layoutDirection: Int) { + super.setLayoutDirection(layoutDirection) + setDrawableDirection() + } + + override fun onFinishInflate() { + super.onFinishInflate() + layoutDirection = resources.configuration.layoutDirection + setTextColor() + } + + private fun setTextColor() { + val ta = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent, + android.R.attr.textColorPrimaryInverse)) + val bgColor = ta.getColor(0 /* index */, Color.BLACK) + var textColor = ta.getColor(1 /* index */, Color.WHITE) + ta.recycle() + textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true) + titleTextView.setTextColor(textColor) + descTextView.setTextColor(textColor) + } + + private fun setDrawableDirection() { + view.setBackgroundResource( + if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) + R.drawable.bubble_stack_user_education_bg + else R.drawable.bubble_stack_user_education_bg_rtl) + } + + /** + * If necessary, shows the user education view for the bubble stack. This appears the first + * time a user taps on a bubble. + * + * @return true if user education was shown, false otherwise. + */ + fun show(stackPosition: PointF) : Boolean{ + if (visibility == VISIBLE) return false + + setAlpha(0f) + setVisibility(View.VISIBLE) + post { + with(view) { + val bubbleSize = context.resources.getDimensionPixelSize( + R.dimen.individual_bubble_size) + translationY = stackPosition.y + bubbleSize / 2 - getHeight() / 2 + } + animate() + .setDuration(ANIMATE_DURATION) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .alpha(1f) + } + setShouldShow(false) + return true + } + + /** + * If necessary, hides the stack education view. + * + * @param fromExpansion if true this indicates the hide is happening due to the bubble being + * expanded, false if due to a touch outside of the bubble stack. + */ + fun hide(fromExpansion: Boolean) { + if (visibility != VISIBLE || isHiding) return + + animate() + .alpha(0f) + .setDuration(if (fromExpansion) ANIMATE_DURATION_SHORT else ANIMATE_DURATION) + .withEndAction { visibility = GONE } + } + + private fun setShouldShow(shouldShow: Boolean) { + Prefs.putBoolean(context, HAS_SEEN_BUBBLES_EDUCATION, !shouldShow) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index c683a87d6282..31830b94e8e4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -72,8 +72,13 @@ class ControlAdapter( TYPE_CONTROL -> { ControlHolder( layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply { - layoutParams.apply { + (layoutParams as ViewGroup.MarginLayoutParams).apply { width = ViewGroup.LayoutParams.MATCH_PARENT + // Reset margins as they will be set through the decoration + topMargin = 0 + bottomMargin = 0 + leftMargin = 0 + rightMargin = 0 } elevation = this@ControlAdapter.elevation background = parent.context.getDrawable( @@ -386,7 +391,7 @@ class MarginItemDecorator( val type = parent.adapter?.getItemViewType(position) if (type == ControlAdapter.TYPE_CONTROL) { outRect.apply { - top = topMargin + top = topMargin * 2 // Use double margin, as we are not setting bottom left = sideMargins right = sideMargins bottom = 0 diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 1a1cc072c6bf..19b0ea1db04e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -33,9 +33,6 @@ import java.io.PrintWriter; import javax.inject.Inject; -import dagger.Reusable; - -@Reusable // Don't create multiple DozeServices. public class DozeService extends DreamService implements DozeMachine.Service, RequestDoze, PluginListener<DozeServicePlugin> { private static final String TAG = "DozeService"; @@ -60,7 +57,7 @@ public class DozeService extends DreamService setWindowless(true); mPluginManager.addPluginListener(this, DozeServicePlugin.class, false /* allowMultiple */); - DozeComponent dozeComponent = mDozeComponentBuilder.build(); + DozeComponent dozeComponent = mDozeComponentBuilder.build(this); mDozeMachine = dozeComponent.getDozeMachine(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java index 247285434df9..05050f905e60 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.doze.dagger; import com.android.systemui.doze.DozeMachine; import com.android.systemui.doze.DozeService; +import dagger.BindsInstance; import dagger.Subcomponent; /** @@ -30,7 +31,7 @@ public interface DozeComponent { /** Simple Builder for {@link DozeComponent}. */ @Subcomponent.Factory interface Builder { - DozeComponent build(); + DozeComponent build(@BindsInstance DozeMachine.Service dozeMachineService); } /** Supply a {@link DozeMachine}. */ diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java index a12e280fcca6..04f7c368fdc4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java @@ -33,7 +33,6 @@ import com.android.systemui.doze.DozeScreenBrightness; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.doze.DozeScreenStatePreventingAdapter; import com.android.systemui.doze.DozeSensors; -import com.android.systemui.doze.DozeService; import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter; import com.android.systemui.doze.DozeTriggers; import com.android.systemui.doze.DozeUi; @@ -52,9 +51,9 @@ public abstract class DozeModule { @Provides @DozeScope @WrappedService - static DozeMachine.Service providesWrappedService(DozeService dozeService, DozeHost dozeHost, - DozeParameters dozeParameters) { - DozeMachine.Service wrappedService = dozeService; + static DozeMachine.Service providesWrappedService(DozeMachine.Service dozeMachineService, + DozeHost dozeHost, DozeParameters dozeParameters) { + DozeMachine.Service wrappedService = dozeMachineService; wrappedService = new DozeBrightnessHostForwarder(wrappedService, dozeHost); wrappedService = DozeScreenStatePreventingAdapter.wrapIfNeeded( wrappedService, dozeParameters); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt index 17e62890aadd..3bfdf5caa9b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt @@ -16,9 +16,9 @@ package com.android.systemui.statusbar.notification.collection +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection /** * Stores the state that [ShadeListBuilder] assigns to this [ListEntry] @@ -35,7 +35,6 @@ data class ListAttachState private constructor( * parent's section. Null if not attached to the list. */ var section: NotifSection?, - var sectionIndex: Int, /** * If a [NotifFilter] is excluding this entry from the list, then that filter. Always null for @@ -60,7 +59,6 @@ data class ListAttachState private constructor( fun clone(other: ListAttachState) { parent = other.parent section = other.section - sectionIndex = other.sectionIndex excludingFilter = other.excludingFilter promoter = other.promoter suppressedChanges.clone(other.suppressedChanges) @@ -70,7 +68,6 @@ data class ListAttachState private constructor( fun reset() { parent = null section = null - sectionIndex = -1 excludingFilter = null promoter = null suppressedChanges.reset() @@ -82,7 +79,6 @@ data class ListAttachState private constructor( return ListAttachState( null, null, - -1, null, null, SuppressedAttachState.create()) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 786c97d03712..52c5c3e08118 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -112,11 +112,9 @@ public class ListDumper { .append(")"); } - if (entry.getNotifSection() != null) { - sb.append(" sectionIndex=") - .append(entry.getSection()) - .append(" sectionName=") - .append(entry.getNotifSection().getName()); + if (entry.getSection() != null) { + sb.append(" section=") + .append(entry.getSection().getLabel()); } if (includeRecordKeeping) { @@ -175,12 +173,9 @@ public class ListDumper { } if (notifEntry.getAttachState().getSuppressedChanges().getSection() != null) { - rksb.append("suppressedSectionIndex=") + rksb.append("suppressedSection=") .append(notifEntry.getAttachState().getSuppressedChanges() - .getSectionIndex()) - .append(" sectionName=") - .append(notifEntry.getAttachState().getSuppressedChanges() - .getSection().getName()) + .getSection()) .append(" "); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java index 65f5dc4e5f7c..82c1f243dcdb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java @@ -21,7 +21,7 @@ import android.annotation.UptimeMillisLong; import androidx.annotation.Nullable; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; /** * Abstract superclass for top-level entries, i.e. things that can appear in the final notification @@ -78,13 +78,12 @@ public abstract class ListEntry { return mPreviousAttachState.getParent(); } - /** The section this notification was assigned to (0 to N-1, where N is number of sections). */ - public int getSection() { - return mAttachState.getSectionIndex(); + @Nullable public NotifSection getSection() { + return mAttachState.getSection(); } - @Nullable public NotifSection getNotifSection() { - return mAttachState.getSection(); + public int getSectionIndex() { + return mAttachState.getSection() != null ? mAttachState.getSection().getIndex() : -1; } ListAttachState getAttachState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java index 05dd4df1f2ce..a1844ff5d221 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java @@ -24,7 +24,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.OnBefo import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; @@ -155,10 +155,10 @@ public class NotifPipeline implements CommonNotifCollection { * Sections that are used to sort top-level entries. If two entries have the same section, * NotifComparators are consulted. Sections from this list are called in order for each * notification passed through the pipeline. The first NotifSection to return true for - * {@link NotifSection#isInSection(ListEntry)} sets the entry as part of its Section. + * {@link NotifSectioner#isInSection(ListEntry)} sets the entry as part of its Section. */ - public void setSections(List<NotifSection> sections) { - mShadeListBuilder.setSections(sections); + public void setSections(List<NotifSectioner> sections) { + mShadeListBuilder.setSectioners(sections); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 6cbebf803511..2b545c56c8bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -28,10 +28,11 @@ import static com.android.systemui.statusbar.notification.collection.listbuilder import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_SORTING; import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_TRANSFORMING; +import static java.util.Objects.requireNonNull; + import android.annotation.MainThread; import android.annotation.Nullable; import android.util.ArrayMap; -import android.util.Pair; import androidx.annotation.NonNull; @@ -39,6 +40,7 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -48,7 +50,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; @@ -119,6 +121,8 @@ public class ShadeListBuilder implements Dumpable { mLogger = logger; mInteractionTracker = interactionTracker; dumpManager.registerDumpable(TAG, this); + + setSectioners(Collections.emptyList()); } /** @@ -193,15 +197,17 @@ public class ShadeListBuilder implements Dumpable { promoter.setInvalidationListener(this::onPromoterInvalidated); } - void setSections(List<NotifSection> sections) { + void setSectioners(List<NotifSectioner> sectioners) { Assert.isMainThread(); mPipelineState.requireState(STATE_IDLE); mNotifSections.clear(); - for (NotifSection section : sections) { - mNotifSections.add(section); - section.setInvalidationListener(this::onNotifSectionInvalidated); + for (NotifSectioner sectioner : sectioners) { + mNotifSections.add(new NotifSection(sectioner, mNotifSections.size())); + sectioner.setInvalidationListener(this::onNotifSectionInvalidated); } + + mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size())); } void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) { @@ -275,7 +281,7 @@ public class ShadeListBuilder implements Dumpable { rebuildListIfBefore(STATE_TRANSFORMING); } - private void onNotifSectionInvalidated(NotifSection section) { + private void onNotifSectionInvalidated(NotifSectioner section) { Assert.isMainThread(); mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState()); @@ -652,7 +658,6 @@ public class ShadeListBuilder implements Dumpable { */ private void annulAddition(ListEntry entry) { entry.setParent(null); - entry.getAttachState().setSectionIndex(-1); entry.getAttachState().setSection(null); entry.getAttachState().setPromoter(null); if (entry.mFirstAddedIteration == mIterationCount) { @@ -663,12 +668,12 @@ public class ShadeListBuilder implements Dumpable { private void sortList() { // Assign sections to top-level elements and sort their children for (ListEntry entry : mNotifList) { - Pair<NotifSection, Integer> sectionWithIndex = applySections(entry); + NotifSection section = applySections(entry); if (entry instanceof GroupEntry) { GroupEntry parent = (GroupEntry) entry; for (NotificationEntry child : parent.getChildren()) { - child.getAttachState().setSection(sectionWithIndex.first); - child.getAttachState().setSectionIndex(sectionWithIndex.second); + child.getAttachState().setSection(section); + child.getAttachState().setSection(section); } parent.sortChildren(sChildComparator); } @@ -736,16 +741,13 @@ public class ShadeListBuilder implements Dumpable { mLogger.logSectionChanged( mIterationCount, prev.getSection(), - prev.getSectionIndex(), - curr.getSection(), - curr.getSectionIndex()); + curr.getSection()); } if (curr.getSuppressedChanges().getSection() != null) { mLogger.logSectionChangeSuppressed( mIterationCount, curr.getSuppressedChanges().getSection(), - curr.getSuppressedChanges().getSectionIndex(), curr.getSection()); } } @@ -762,7 +764,10 @@ public class ShadeListBuilder implements Dumpable { callOnCleanup(mNotifPromoters); callOnCleanup(mNotifFinalizeFilters); callOnCleanup(mNotifComparators); - callOnCleanup(mNotifSections); + + for (int i = 0; i < mNotifSections.size(); i++) { + mNotifSections.get(i).getSectioner().onCleanup(); + } if (mNotifStabilityManager != null) { callOnCleanup(List.of(mNotifStabilityManager)); @@ -777,7 +782,9 @@ public class ShadeListBuilder implements Dumpable { private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> { - int cmp = Integer.compare(o1.getSection(), o2.getSection()); + int cmp = Integer.compare( + requireNonNull(o1.getSection()).getIndex(), + requireNonNull(o2.getSection()).getIndex()); if (cmp == 0) { for (int i = 0; i < mNotifComparators.size(); i++) { @@ -855,45 +862,41 @@ public class ShadeListBuilder implements Dumpable { return null; } - private Pair<NotifSection, Integer> applySections(ListEntry entry) { - Pair<NotifSection, Integer> sectionWithIndex = findSection(entry); + private NotifSection applySections(ListEntry entry) { + final NotifSection newSection = findSection(entry); final ListAttachState prevAttachState = entry.getPreviousAttachState(); + NotifSection finalSection = newSection; + // are we changing sections of this entry? if (mNotifStabilityManager != null && prevAttachState.getParent() != null - && (sectionWithIndex.first != prevAttachState.getSection() - || sectionWithIndex.second != prevAttachState.getSectionIndex())) { + && newSection != prevAttachState.getSection()) { // are section changes allowed? - if (!mNotifStabilityManager.isSectionChangeAllowed( - entry.getRepresentativeEntry())) { - entry.getAttachState().getSuppressedChanges().setSection( - sectionWithIndex.first); - entry.getAttachState().getSuppressedChanges().setSectionIndex( - sectionWithIndex.second); + if (!mNotifStabilityManager.isSectionChangeAllowed(entry.getRepresentativeEntry())) { + // record the section that we wanted to change to + entry.getAttachState().getSuppressedChanges().setSection(newSection); // keep the previous section - sectionWithIndex = new Pair( - prevAttachState.getSection(), - prevAttachState.getSectionIndex()); + finalSection = prevAttachState.getSection(); } } - entry.getAttachState().setSection(sectionWithIndex.first); - entry.getAttachState().setSectionIndex(sectionWithIndex.second); + entry.getAttachState().setSection(finalSection); - return sectionWithIndex; + return finalSection; } - private Pair<NotifSection, Integer> findSection(ListEntry entry) { + @NonNull + private NotifSection findSection(ListEntry entry) { for (int i = 0; i < mNotifSections.size(); i++) { - NotifSection sectioner = mNotifSections.get(i); - if (sectioner.isInSection(entry)) { - return new Pair<>(sectioner, i); + NotifSection section = mNotifSections.get(i); + if (section.getSectioner().isInSection(entry)) { + return section; } } - return new Pair<>(sDefaultSection, mNotifSections.size()); + throw new RuntimeException("Missing default sectioner!"); } private void rebuildListIfBefore(@PipelineState.StateName int state) { @@ -963,15 +966,15 @@ public class ShadeListBuilder implements Dumpable { void onRenderList(@NonNull List<ListEntry> entries); } - private static final NotifSection sDefaultSection = - new NotifSection("UnknownSection") { + private static final NotifSectioner DEFAULT_SECTIONER = + new NotifSectioner("UnknownSection") { @Override public boolean isInSection(ListEntry entry) { return true; } }; - private static final String TAG = "ShadeListBuilder"; - private static final int MIN_CHILDREN_FOR_GROUP = 2; + + private static final String TAG = "ShadeListBuilder"; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt index 52612365712e..3eb2e610f329 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt @@ -16,7 +16,7 @@ package com.android.systemui.statusbar.notification.collection -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection /** * Stores the suppressed state that [ShadeListBuilder] assigned to this [ListEntry] before the @@ -33,22 +33,19 @@ data class SuppressedAttachState private constructor( * The assigned section for this ListEntry. If the child of the group, this will be the * parent's section. Null if not attached to the list. */ - var section: NotifSection?, - var sectionIndex: Int + var section: NotifSection? ) { /** Copies the state of another instance. */ fun clone(other: SuppressedAttachState) { parent = other.parent section = other.section - sectionIndex = other.sectionIndex } /** Resets back to a "clean" state (the same as created by the factory method) */ fun reset() { parent = null section = null - sectionIndex = -1 } companion object { @@ -56,8 +53,7 @@ data class SuppressedAttachState private constructor( fun create(): SuppressedAttachState { return SuppressedAttachState( null, - null, - -1) + null) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 0b9bded5ef58..c7ac40346ce1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -83,8 +83,8 @@ public class AppOpsCoordinator implements Coordinator { } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } /** @@ -179,7 +179,7 @@ public class AppOpsCoordinator implements Coordinator { /** * Puts foreground service notifications into its own section. */ - private final NotifSection mNotifSection = new NotifSection("ForegroundService") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("ForegroundService") { @Override public boolean isInSection(ListEntry entry) { NotificationEntry notificationEntry = entry.getRepresentativeEntry(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt index c8e859f27a5c..dea11626a3f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt @@ -21,7 +21,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import javax.inject.Inject @@ -42,7 +42,7 @@ class ConversationCoordinator @Inject constructor( } } - private val mNotifSection: NotifSection = object : NotifSection("People") { + val sectioner = object : NotifSectioner("People") { override fun isInSection(entry: ListEntry): Boolean { return isConversation(entry.representativeEntry!!) } @@ -52,10 +52,6 @@ class ConversationCoordinator @Inject constructor( pipeline.addPromoter(notificationPromoter) } - fun getSection(): NotifSection { - return mNotifSection - } - private fun isConversation(entry: NotificationEntry): Boolean = peopleNotificationIdentifier.getPeopleNotificationType(entry.sbn, entry.ranking) != TYPE_NON_PERSON diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java index 6e6cecaf62fa..c023400ca9ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.java @@ -27,7 +27,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -88,8 +88,8 @@ public class HeadsUpCoordinator implements Coordinator { pipeline.addNotificationLifetimeExtender(mLifetimeExtender); } - public NotifSection getSection() { - return mNotifSection; + public NotifSectioner getSectioner() { + return mNotifSectioner; } private void onHeadsUpViewBound(NotificationEntry entry) { @@ -191,7 +191,7 @@ public class HeadsUpCoordinator implements Coordinator { } }; - private final NotifSection mNotifSection = new NotifSection("HeadsUp") { + private final NotifSectioner mNotifSectioner = new NotifSectioner("HeadsUp") { @Override public boolean isInSection(ListEntry entry) { return isCurrentlyShowingHun(entry); @@ -207,7 +207,7 @@ public class HeadsUpCoordinator implements Coordinator { endNotifLifetimeExtension(); mCurrentHun = newHUN; mNotifPromoter.invalidateList(); - mNotifSection.invalidateList(); + mNotifSectioner.invalidateList(); } if (!isHeadsUp) { mHeadsUpViewBinder.unbindHeadsUpView(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java index 87ca717982f5..ded5e46593f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java @@ -21,7 +21,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; @@ -41,7 +41,7 @@ import javax.inject.Inject; public class NotifCoordinators implements Dumpable { private static final String TAG = "NotifCoordinators"; private final List<Coordinator> mCoordinators = new ArrayList<>(); - private final List<NotifSection> mOrderedSections = new ArrayList<>(); + private final List<NotifSectioner> mOrderedSections = new ArrayList<>(); /** * Creates all the coordinators. @@ -81,12 +81,12 @@ public class NotifCoordinators implements Dumpable { // Manually add Ordered Sections // HeadsUp > FGS > People > Alerting > Silent > Unknown/Default if (featureFlags.isNewNotifPipelineRenderingEnabled()) { - mOrderedSections.add(headsUpCoordinator.getSection()); // HeadsUp + mOrderedSections.add(headsUpCoordinator.getSectioner()); // HeadsUp } - mOrderedSections.add(appOpsCoordinator.getSection()); // ForegroundService - mOrderedSections.add(conversationCoordinator.getSection()); // People - mOrderedSections.add(rankingCoordinator.getAlertingSection()); // Alerting - mOrderedSections.add(rankingCoordinator.getSilentSection()); // Silent + mOrderedSections.add(appOpsCoordinator.getSectioner()); // ForegroundService + mOrderedSections.add(conversationCoordinator.getSectioner()); // People + mOrderedSections.add(rankingCoordinator.getAlertingSectioner()); // Alerting + mOrderedSections.add(rankingCoordinator.getSilentSectioner()); // Silent } /** @@ -109,7 +109,7 @@ public class NotifCoordinators implements Dumpable { pw.println("\t" + c.getClass()); } - for (NotifSection s : mOrderedSections) { + for (NotifSectioner s : mOrderedSections) { pw.println("\t" + s.getName()); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index a32b1636057b..0f08e0ff491c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -22,7 +22,7 @@ import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import javax.inject.Inject; @@ -57,22 +57,22 @@ public class RankingCoordinator implements Coordinator { pipeline.addPreGroupFilter(mDozingFilter); } - public NotifSection getAlertingSection() { - return mAlertingNotifSection; + public NotifSectioner getAlertingSectioner() { + return mAlertingNotifSectioner; } - public NotifSection getSilentSection() { - return mSilentNotifSection; + public NotifSectioner getSilentSectioner() { + return mSilentNotifSectioner; } - private final NotifSection mAlertingNotifSection = new NotifSection("Alerting") { + private final NotifSectioner mAlertingNotifSectioner = new NotifSectioner("Alerting") { @Override public boolean isInSection(ListEntry entry) { return mHighPriorityProvider.isHighPriority(entry); } }; - private final NotifSection mSilentNotifSection = new NotifSection("Silent") { + private final NotifSectioner mSilentNotifSectioner = new NotifSectioner("Silent") { @Override public boolean isInSection(ListEntry entry) { return !mHighPriorityProvider.isHighPriority(entry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt new file mode 100644 index 000000000000..c09122ea3c26 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2020 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.listbuilder + +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner + +data class NotifSection( + val sectioner: NotifSectioner, + val index: Int +) { + val label: String + get() = "Section($index, \"${sectioner.name}\")" +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index f7bfeb7234f0..9ee7db738c20 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection import javax.inject.Inject class ShadeListBuilderLogger @Inject constructor( @@ -211,21 +210,17 @@ class ShadeListBuilderLogger @Inject constructor( fun logSectionChanged( buildId: Int, prevSection: NotifSection?, - prevIndex: Int, - newSection: NotifSection?, - newIndex: Int + newSection: NotifSection? ) { buffer.log(TAG, INFO, { long1 = buildId.toLong() - str1 = prevSection?.name - int1 = prevIndex - str2 = newSection?.name - int2 = newIndex + str1 = prevSection?.label + str2 = newSection?.label }, { if (str1 == null) { - "(Build $long1) Section assigned: '$str2' (#$int2)" + "(Build $long1) Section assigned: $str2" } else { - "(Build $long1) Section changed: '$str1' (#$int1) -> '$str2' (#$int2)" + "(Build $long1) Section changed: $str1 -> $str2" } }) } @@ -233,17 +228,14 @@ class ShadeListBuilderLogger @Inject constructor( fun logSectionChangeSuppressed( buildId: Int, suppressedSection: NotifSection?, - suppressedSectionIndex: Int, assignedSection: NotifSection? ) { buffer.log(TAG, INFO, { long1 = buildId.toLong() - str1 = suppressedSection?.name - int1 = suppressedSectionIndex - str2 = assignedSection?.name + str1 = suppressedSection?.label + str2 = assignedSection?.label }, { - "(Build $long1) Section change suppressed: '$str1' (#$int1). " + - "Keeping section: '$str2'" + "(Build $long1) Suppressing section change to $str1 (staying at $str2)" }) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java index fe5ba3c8e6fc..b57f504189f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java @@ -22,8 +22,8 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; /** * Pluggable for participating in notif sectioning. See {@link ShadeListBuilder#setSections}. */ -public abstract class NotifSection extends Pluggable<NotifSection> { - protected NotifSection(String name) { +public abstract class NotifSectioner extends Pluggable<NotifSectioner> { + protected NotifSectioner(String name) { super(name); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt index e8124944bcb0..a1800ed12125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt @@ -25,10 +25,10 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain * we should just modify NLC to implement the NodeController interface. */ class RootNodeController( - private val listContainer: NotificationListContainer + private val listContainer: NotificationListContainer, + override val view: View ) : NodeController { override val nodeLabel: String = "<root>" - override val view: View = listContainer as View override fun getChildAt(index: Int): View? { return listContainer.getContainerChildAt(index) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt index 118ff4a9fbb7..3c35b7bd8472 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.collection.render +import android.content.Context +import android.view.View import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -30,12 +32,15 @@ import javax.inject.Inject * currently populate the notification shade. */ class ShadeViewManager constructor( + context: Context, listContainer: NotificationListContainer, logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { - private val rootController = RootNodeController(listContainer) + // We pass a shim view here because the listContainer may not actually have a view associated + // with it and the differ never actually cares about the root node's view. + private val rootController = RootNodeController(listContainer, View(context)) private val viewDiffer = ShadeViewDiffer(rootController, logger) fun attach(listBuilder: ShadeListBuilder) { @@ -82,11 +87,17 @@ class ShadeViewManager constructor( } class ShadeViewManagerFactory @Inject constructor( + private val context: Context, private val logger: ShadeViewDifferLogger, private val viewBarn: NotifViewBarn, private val notificationIconAreaController: NotificationIconAreaController ) { fun create(listContainer: NotificationListContainer): ShadeViewManager { - return ShadeViewManager(listContainer, logger, viewBarn, notificationIconAreaController) + return ShadeViewManager( + context, + listContainer, + logger, + viewBarn, + notificationIconAreaController) } } 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 d4c270f45ceb..e061472b3939 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 @@ -95,21 +95,15 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper.DragDownCallback; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -145,7 +139,6 @@ import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -154,11 +147,8 @@ import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.HeadsUpUtil; import com.android.systemui.statusbar.policy.ScrollAdapter; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Assert; @@ -178,7 +168,7 @@ import javax.inject.Named; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ -public class NotificationStackScrollLayout extends ViewGroup implements ScrollAdapter, Dumpable { +public class NotificationStackScrollLayout extends ViewGroup implements Dumpable { public static final float BACKGROUND_ALPHA_DIMMED = 0.7f; private static final String TAG = "StackScroller"; @@ -195,10 +185,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * gap is drawn between them). In this case we don't want to round their corners. */ private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1; - private final KeyguardBypassController mKeyguardBypassController; + private OnMenuEventListener mMenuEventListener; + private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; private final DynamicPrivacyController mDynamicPrivacyController; private final SysuiStatusBarStateController mStatusbarStateController; - private final KeyguardMediaController mKeyguardMediaController; private ExpandHelper mExpandHelper; private final NotificationSwipeHelper mSwipeHelper; @@ -249,6 +239,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mBottomMargin; private int mBottomInset = 0; private float mQsExpansionFraction; + private int mCurrentUserId; /** * The algorithm which calculates the properties for our children @@ -326,7 +317,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * motion. */ private int mMaxScrollAfterExpand; - private ExpandableNotificationRow.LongPressListener mLongPressListener; boolean mCheckForLeavebehind; /** @@ -348,12 +338,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return true; } }; - private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { - @Override - public void onUserChanged(int userId) { - updateSensitiveness(false /* animated */); - } - }; + private StatusBar mStatusBar; private int[] mTempInt2 = new int[2]; private boolean mGenerateChildOrderChangedEvent; @@ -368,7 +353,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mForceNoOverlappingRendering; private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>(); private FalsingManager mFalsingManager; - private final ZenModeController mZenController; private boolean mAnimationRunning; private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater = new ViewTreeObserver.OnPreDrawListener() { @@ -498,7 +482,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private ArrayList<BiConsumer<Float, Float>> mExpandedHeightListeners = new ArrayList<>(); private int mHeadsUpInset; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private final NotificationLockscreenUserManager mLockscreenUserManager; private final Rect mTmpRect = new Rect(); private final FeatureFlags mFeatureFlags; private final NotifPipeline mNotifPipeline; @@ -511,7 +494,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd protected final UiEventLogger mUiEventLogger; private final NotificationRemoteInputManager mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class); - private final SysuiColorExtractor mColorExtractor = Dependency.get(SysuiColorExtractor.class); private final DisplayMetrics mDisplayMetrics = Dependency.get(DisplayMetrics.class); private final LockscreenGestureLogger mLockscreenGestureLogger = @@ -536,11 +518,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private int mWaterfallTopInset; private NotificationStackScrollLayoutController mController; - private SysuiColorExtractor.OnColorsChangedListener mOnColorsChangedListener = - (colorExtractor, which) -> { - final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); - updateDecorViews(useDarkText); - }; + private boolean mKeyguardMediaControllorVisible; private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener = new ExpandableView.OnHeightChangedListener() { @@ -555,20 +533,44 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } }; + private final ScrollAdapter mScrollAdapter = new ScrollAdapter() { + @Override + public boolean isScrolledToTop() { + if (ANCHOR_SCROLLING) { + updateScrollAnchor(); + // TODO: once we're recycling this will need to check the adapter position of the + // child + return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; + } else { + return mOwnScrollY == 0; + } + } + + @Override + public boolean isScrolledToBottom() { + if (ANCHOR_SCROLLING) { + return getMaxPositiveScrollAmount() <= 0; + } else { + return mOwnScrollY >= getScrollRange(); + } + } + + @Override + public View getHostView() { + return NotificationStackScrollLayout.this; + } + }; + @Inject public NotificationStackScrollLayout( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NotificationRoundnessManager notificationRoundnessManager, DynamicPrivacyController dynamicPrivacyController, - SysuiStatusBarStateController statusBarStateController, + SysuiStatusBarStateController statusbarStateController, HeadsUpManagerPhone headsUpManager, - KeyguardBypassController keyguardBypassController, - KeyguardMediaController keyguardMediaController, FalsingManager falsingManager, - NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGutsManager notificationGutsManager, - ZenModeController zenController, NotificationSectionsManager notificationSectionsManager, ForegroundServiceSectionController fgsSectionController, ForegroundServiceDismissalFeatureController fgsFeatureController, @@ -583,13 +585,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mRoundnessManager = notificationRoundnessManager; - mLockscreenUserManager = notificationLockscreenUserManager; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed); - mKeyguardBypassController = keyguardBypassController; mFalsingManager = falsingManager; - mZenController = zenController; mFgsSectionController = fgsSectionController; mSectionsManager = notificationSectionsManager; @@ -608,16 +607,33 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback, minHeight, maxHeight); mExpandHelper.setEventSource(this); - mExpandHelper.setScrollAdapter(this); + mExpandHelper.setScrollAdapter(mScrollAdapter); + + // TODO: move swipe helper into controller. + // The anonymous proxy through to mMenuEventListener is temporary until more can be moved + // into the controller. mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback, - getContext(), mMenuEventListener, mFalsingManager); + getContext(), new OnMenuEventListener() { + @Override + public void onMenuClicked(View row, int x, int y, MenuItem menu) { + mMenuEventListener.onMenuClicked(row, x, y, menu); + } + + @Override + public void onMenuReset(View row) { + mMenuEventListener.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + mMenuEventListener.onMenuShown(row); + } + }, mFalsingManager); mStackScrollAlgorithm = createStackScrollAlgorithm(context); - initView(context); mShouldDrawNotificationBackground = res.getBoolean(R.bool.config_drawNotificationBackground); mFadeNotificationsOnDismiss = res.getBoolean(R.bool.config_fadeNotificationsOnDismiss); - mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); setOutlineProvider(mOutlineProvider); // Blocking helper manager wants to know the expanded state, update as well. @@ -667,20 +683,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } mDynamicPrivacyController = dynamicPrivacyController; - mStatusbarStateController = statusBarStateController; + mStatusbarStateController = statusbarStateController; initializeForegroundServiceSection(fgsFeatureController); mUiEventLogger = uiEventLogger; - mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener); - mKeyguardMediaController = keyguardMediaController; - keyguardMediaController.setVisibilityChangedListener((visible) -> { - if (visible) { - generateAddAnimation(keyguardMediaController.getView(), false /*fromMoreCard */); - } else { - generateRemoveAnimation(keyguardMediaController.getView()); - } - requestChildrenUpdate(); - return null; - }); } private void initializeForegroundServiceSection( @@ -719,7 +724,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd public float getWakeUpHeight() { ExpandableView firstChild = getFirstChildWithBackground(); if (firstChild != null) { - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { return firstChild.getHeadsUpHeightWithoutHeader(); } else { return firstChild.getCollapsedHeight(); @@ -794,21 +799,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd }; } - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - ((SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class)) - .addCallback(mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(StatusBarStateController.class).removeCallback(mStateListener); - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public NotificationSwipeActionHelper getSwipeActionHelper() { return mSwipeHelper; @@ -898,7 +888,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } boolean shouldDrawBackground; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { shouldDrawBackground = isPulseExpanding(); } else { shouldDrawBackground = !mAmbientState.isDozing() || anySectionHasVisibleChild; @@ -1013,9 +1003,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } + private void reinitView() { + initView(getContext(), mKeyguardBypassEnabledProvider); + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void initView(Context context) { + void initView(Context context, + KeyguardBypassEnabledProvider keyguardBypassEnabledProvider) { mScroller = new OverScroller(getContext()); + mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider; + setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setClipChildren(false); final ViewConfiguration configuration = ViewConfiguration.get(context); @@ -1227,7 +1224,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd float end = start + child.getActualHeight(); boolean clip = clipStart > start && clipStart < end || clipEnd >= start && clipEnd <= end; - clip &= !(first && isScrolledToTop()); + clip &= !(first && mScrollAdapter.isScrolledToTop()); child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : ExpandableView.NO_ROUNDNESS); first = false; @@ -1287,7 +1284,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestChildrenUpdate() { + void requestChildrenUpdate() { if (!mChildrenUpdateRequested) { getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater); mChildrenUpdateRequested = true; @@ -1417,7 +1414,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private void notifyAppearChangedListeners() { float appear; float expandAmount; - if (mKeyguardBypassController.getBypassEnabled() && onKeyguard()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()) { appear = calculateAppearFractionBypass(); expandAmount = getPulseHeight(); } else { @@ -1819,7 +1816,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mSwipeHelper.setDensityScale(densityScale); float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop(); mSwipeHelper.setPagingTouchSlop(pagingTouchSlop); - initView(getContext()); + reinitView(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -2220,7 +2217,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd springBack(); } else { float overScrollTop = getCurrentOverScrollAmount(true /* top */); - if (isScrolledToTop() && mScrollAnchorViewY > 0) { + if (mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0) { notifyOverscrollTopListener(mScrollAnchorViewY, isRubberbanded(true /* onTop */)); } else { @@ -2268,7 +2265,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void springBack() { if (ANCHOR_SCROLLING) { - boolean overScrolledTop = isScrolledToTop() && mScrollAnchorViewY > 0; + boolean overScrolledTop = mScrollAdapter.isScrolledToTop() && mScrollAnchorViewY > 0; int maxPositiveScrollAmount = getMaxPositiveScrollAmount(); boolean overscrolledBottom = maxPositiveScrollAmount < 0; if (overScrolledTop || overscrolledBottom) { @@ -2545,8 +2542,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) private void updateForwardAndBackwardScrollability() { - boolean forwardScrollable = mScrollable && !isScrolledToBottom(); - boolean backwardsScrollable = mScrollable && !isScrolledToTop(); + boolean forwardScrollable = mScrollable && !mScrollAdapter.isScrolledToBottom(); + boolean backwardsScrollable = mScrollable && !mScrollAdapter.isScrolledToTop(); boolean changed = forwardScrollable != mForwardScrollable || backwardsScrollable != mBackwardScrollable; mForwardScrollable = forwardScrollable; @@ -2667,7 +2664,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1 && (mAmbientState.isDozing() - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard)); + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard)); for (NotificationSection section : mSections) { int minBottomPosition = minTopPosition; if (section == lastSection) { @@ -2830,7 +2827,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mLastScrollerY = 0; // x velocity is set to 1 to avoid overscroller bug mScroller.fling(0, 0, 1, velocityY, 0, 0, minY, maxY, 0, - mExpandedInThisMotion && !isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); + mExpandedInThisMotion + && !mScrollAdapter.isScrolledToTop() ? 0 : Integer.MAX_VALUE / 2); } } @@ -2937,7 +2935,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } else { mTopPaddingOverflow = 0; } - setTopPadding(topPadding, animate && !mKeyguardBypassController.getBypassEnabled()); + setTopPadding(topPadding, animate && !mKeyguardBypassEnabledProvider.getBypassEnabled()); setExpandedHeight(mExpandedHeight); } @@ -3095,7 +3093,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd * @return Whether an animation was generated. */ @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private boolean generateRemoveAnimation(ExpandableView child) { + boolean generateRemoveAnimation(ExpandableView child) { if (removeRemovedChildFromHeadsUpChangeAnimations(child)) { mAddedHeadsUpChildren.remove(child); return false; @@ -3332,7 +3330,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.COORDINATOR) - void onViewAddedInternal(ExpandableView child) { + private void onViewAddedInternal(ExpandableView child) { updateHideSensitiveForChild(child); child.setOnHeightChangedListener(mOnChildHeightChangedListener); generateAddAnimation(child, false /* fromMoreCard */); @@ -3343,7 +3341,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (ANCHOR_SCROLLING) { // TODO: once we're recycling this will need to check the adapter position of the child - if (child == getFirstChildNotGone() && (isScrolledToTop() || !mIsExpanded)) { + if (child == getFirstChildNotGone() + && (mScrollAdapter.isScrolledToTop() || !mIsExpanded)) { // New child was added at the top while we're scrolled to the top; // make it the new anchor view so that we stay at the top. mScrollAnchorView = child; @@ -3361,6 +3360,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd onViewRemovedInternal(row, childrenContainer); } + public void notifyGroupChildAdded(ExpandableView row) { + onViewAddedInternal(row); + } + @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) public void setAnimationsEnabled(boolean animationsEnabled) { mAnimationsEnabled = animationsEnabled; @@ -3533,7 +3536,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd boolean performDisappearAnimation = !mIsExpanded // Only animate if we still have pinned heads up, otherwise we just have the // regular collapse animation of the lock screen - || (mKeyguardBypassController.getBypassEnabled() && onKeyguard() + || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard() && mHeadsUpManager.hasPinnedHeadsUp()); if (performDisappearAnimation && !isHeadsUp) { type = row.wasJustClicked() @@ -3769,11 +3772,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return y < getHeight() - getEmptyBottomMargin(); } - @ShadeViewRefactor(RefactorComponent.INPUT) - public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) { - mLongPressListener = listener; - } - private float getTouchSlop(MotionEvent event) { // Adjust the touch slop if another gesture may be being performed. return event.getClassification() == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE @@ -4227,7 +4225,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case MotionEvent.ACTION_DOWN: { final int y = (int) ev.getY(); - mScrolledToTopOnFirstDown = isScrolledToTop(); + mScrolledToTopOnFirstDown = mScrollAdapter.isScrolledToTop(); final ExpandableView childAtTouchPos = getChildAtPosition( ev.getX(), y, false /* requireMinHeight */, false /* ignoreDecors */); if (childAtTouchPos == null) { @@ -4411,32 +4409,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToTop() { - if (ANCHOR_SCROLLING) { - updateScrollAnchor(); - // TODO: once we're recycling this will need to check the adapter position of the child - return mScrollAnchorView == getFirstChildNotGone() && mScrollAnchorViewY >= 0; - } else { - return mOwnScrollY == 0; - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.COORDINATOR) - public boolean isScrolledToBottom() { - if (ANCHOR_SCROLLING) { - return getMaxPositiveScrollAmount() <= 0; - } else { - return mOwnScrollY >= getScrollRange(); - } - } - - @Override - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - public View getHostView() { - return this; + boolean isScrolledToBottom() { + return mScrollAdapter.isScrolledToBottom(); } @ShadeViewRefactor(RefactorComponent.COORDINATOR) @@ -4734,8 +4708,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - private void updateSensitiveness(boolean animate) { - boolean hideSensitive = mLockscreenUserManager.isAnyProfilePublicMode(); + void updateSensitiveness(boolean animate, boolean hideSensitive) { if (hideSensitive != mAmbientState.isHideSensitive()) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -4927,7 +4900,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // Since we are clipping to the outline we need to make sure that the shadows aren't // clipped when pulsing float ownTranslationZ = 0; - if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { ExpandableView firstChildNotGone = getFirstChildNotGone(); if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) { ownTranslationZ = firstChildNotGone.getTranslationZ(); @@ -4992,11 +4965,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) - void updateEmptyShadeView(boolean visible) { + void updateEmptyShadeView(boolean visible, boolean notifVisibleInShade) { mEmptyShadeView.setVisible(visible, mIsExpanded && mAnimationsEnabled); int oldTextRes = mEmptyShadeView.getTextResource(); - int newTextRes = mZenController.areNotificationsHiddenInShade() + int newTextRes = notifVisibleInShade ? R.string.dnd_suppressing_shade_text : R.string.empty_shade_text; if (oldTextRes != newTextRes) { mEmptyShadeView.setText(newTextRes); @@ -5093,7 +5066,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private void requestAnimateEverything() { + void requestAnimateEverything() { if (mIsExpanded && mAnimationsEnabled) { mEverythingNeedsAnimation = true; mNeedsAnimation = true; @@ -5431,17 +5404,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mAmbientState.setStatusBarState(statusBarState); } - private void onStatePostChange() { + void onStatePostChange(boolean fromShadeLocked) { boolean onKeyguard = onKeyguard(); + mAmbientState.setActivatedChild(null); + mAmbientState.setDimmed(onKeyguard); + if (mHeadsUpAppearanceController != null) { mHeadsUpAppearanceController.onStateChanged(); } - SysuiStatusBarStateController state = (SysuiStatusBarStateController) - Dependency.get(StatusBarStateController.class); - updateSensitiveness(state.goingToFullShade() /* animate */); - setDimmed(onKeyguard, state.fromShadeLocked() /* animate */); + setDimmed(onKeyguard, fromShadeLocked); setExpandingEnabled(!onKeyguard); ActivatableNotificationView activatedChild = getActivatedChild(); setActivatedChild(null); @@ -5771,7 +5744,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd */ public float setPulseHeight(float height) { mAmbientState.setPulseHeight(height); - if (mKeyguardBypassController.getBypassEnabled()) { + if (mKeyguardBypassEnabledProvider.getBypassEnabled()) { notifyAppearChangedListeners(); } requestChildrenUpdate(); @@ -5849,6 +5822,31 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return mController; } + void setCurrentUserid(int userId) { + mCurrentUserId = userId; + } + + void onMenuShown(View row) { + mSwipeHelper.onMenuShown(row); + } + + void onMenuReset(View row) { + View translatingParentView = mSwipeHelper.getTranslatingParentView(); + if (translatingParentView != null && row == translatingParentView) { + mSwipeHelper.clearExposedMenuView(); + mSwipeHelper.clearTranslatingParentView(); + if (row instanceof ExpandableNotificationRow) { + mHeadsUpManager.setMenuShown( + ((ExpandableNotificationRow) row).getEntry(), false); + + } + } + } + + void setMenuEventListener(OnMenuEventListener menuEventListener) { + mMenuEventListener = menuEventListener; + } + /** * A listener that is notified when the empty space below the notifications is clicked on */ @@ -6204,89 +6202,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } } - @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) - private final StateListener mStateListener = new StateListener() { - @Override - public void onStatePreChange(int oldState, int newState) { - if (oldState == StatusBarState.SHADE_LOCKED && newState == StatusBarState.KEYGUARD) { - requestAnimateEverything(); - } - } - @Override - public void onStateChanged(int newState) { - setStatusBarState(newState); - } - - @Override - public void onStatePostChange() { - NotificationStackScrollLayout.this.onStatePostChange(); - } - }; - - @VisibleForTesting - @ShadeViewRefactor(RefactorComponent.INPUT) - protected final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { - @Override - public void onMenuClicked(View view, int x, int y, MenuItem item) { - if (mLongPressListener == null) { - return; - } - if (view instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) view; - mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) - .setType(MetricsEvent.TYPE_ACTION) - ); - } - mLongPressListener.onLongPress(view, x, y, item); - } - - @Override - public void onMenuReset(View row) { - View translatingParentView = mSwipeHelper.getTranslatingParentView(); - if (translatingParentView != null && row == translatingParentView) { - mSwipeHelper.clearExposedMenuView(); - mSwipeHelper.clearTranslatingParentView(); - if (row instanceof ExpandableNotificationRow) { - mHeadsUpManager.setMenuShown( - ((ExpandableNotificationRow) row).getEntry(), false); - - } - } - } - - @Override - public void onMenuShown(View row) { - if (row instanceof ExpandableNotificationRow) { - ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; - mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() - .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) - .setType(MetricsEvent.TYPE_ACTION)); - mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); - mSwipeHelper.onMenuShown(row); - mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, - false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, - false /* resetMenu */); - - // Check to see if we want to go directly to the notfication guts - NotificationMenuRowPlugin provider = notificationRow.getProvider(); - if (provider.shouldShowGutsOnSnapOpen()) { - MenuItem item = provider.menuItemToExposeOnSnap(); - if (item != null) { - Point origin = provider.getRevealAnimationOrigin(); - mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); - } else { - Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " - + "menu item in menuItemtoExposeOnSnap. Skipping."); - } - - // Close the menu row since we went directly to the guts - resetExposedMenuView(false, true); - } - } - } - }; @ShadeViewRefactor(RefactorComponent.INPUT) private final NotificationSwipeHelper.NotificationCallback mNotificationCallback = @@ -6517,7 +6433,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @SelectedRows int selectedRows) { if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) { if (selectedRows == ROWS_ALL) { - mNotifCollection.dismissAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mNotifCollection.dismissAllNotifications(mCurrentUserId); } else { final List<Pair<NotificationEntry, DismissedByUserStats>> entriesWithRowsDismissedFromShade = new ArrayList<>(); @@ -6546,7 +6462,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } if (selectedRows == ROWS_ALL) { try { - mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId()); + mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } @@ -6568,6 +6484,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd NotificationLogger.getNotificationLocation(entry))); } + public void setKeyguardMediaControllorVisible(boolean keyguardMediaControllorVisible) { + mKeyguardMediaControllorVisible = keyguardMediaControllorVisible; + } + // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------ @ShadeViewRefactor(RefactorComponent.INPUT) @@ -6576,8 +6496,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd /* Only ever called as a consequence of a lockscreen expansion gesture. */ @Override public boolean onDraggedDown(View startingChild, int dragLengthY) { - boolean canDragDown = hasActiveNotifications() - || mKeyguardMediaController.getView().getVisibility() == VISIBLE; + boolean canDragDown = hasActiveNotifications() || mKeyguardMediaControllorVisible; if (mStatusBarState == StatusBarState.KEYGUARD && canDragDown) { mLockscreenGestureLogger.write( MetricsEvent.ACTION_LS_SHADE, @@ -6655,7 +6574,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd @Override public boolean isDragDownAnywhereEnabled() { return mStatusbarStateController.getState() == StatusBarState.KEYGUARD - && !mKeyguardBypassController.getBypassEnabled(); + && !mKeyguardBypassEnabledProvider.getBypassEnabled(); } }; @@ -6838,4 +6757,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd return INVALID; } } + + interface KeyguardBypassEnabledProvider { + boolean getBypassEnabled(); + } } 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 7c29ee2b5483..ca78b2a1fc68 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 @@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; +import android.graphics.Point; import android.graphics.PointF; import android.provider.Settings; +import android.util.Log; import android.view.Display; import android.view.View; import android.view.ViewGroup; @@ -27,9 +29,20 @@ import android.view.WindowInsets; import android.widget.FrameLayout; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; @@ -42,6 +55,7 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScrimController; @@ -49,6 +63,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.dagger.StatusBarComponent; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import java.util.function.BiConsumer; @@ -56,11 +71,15 @@ import java.util.function.BiConsumer; import javax.inject.Inject; import javax.inject.Named; +import kotlin.Unit; + /** * Controller for {@link NotificationStackScrollLayout}. */ @StatusBarComponent.StatusBarScope public class NotificationStackScrollLayoutController { + private static final String TAG = "StackScrollerController"; + private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; private final HeadsUpManagerPhone mHeadsUpManager; @@ -68,9 +87,18 @@ public class NotificationStackScrollLayoutController { private final TunerService mTunerService; private final DynamicPrivacyController mDynamicPrivacyController; private final ConfigurationController mConfigurationController; + private final ZenModeController mZenModeController; + private final MetricsLogger mMetricsLogger; + private final KeyguardMediaController mKeyguardMediaController; + private final SysuiStatusBarStateController mStatusBarStateController; + private final KeyguardBypassController mKeyguardBypassController; + private final SysuiColorExtractor mColorExtractor; + private final NotificationLockscreenUserManager mLockscreenUserManager; + + private NotificationStackScrollLayout mView; + private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); - private NotificationStackScrollLayout mView; @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -78,11 +106,14 @@ public class NotificationStackScrollLayoutController { @Override public void onViewAttachedToWindow(View v) { mConfigurationController.addCallback(mConfigurationListener); + mStatusBarStateController.addCallback( + mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); } @Override public void onViewDetachedFromWindow(View v) { mConfigurationController.removeCallback(mConfigurationListener); + mStatusBarStateController.removeCallback(mStateListener); } }; @@ -122,6 +153,91 @@ public class NotificationStackScrollLayoutController { } }; + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStatePreChange(int oldState, int newState) { + if (oldState == StatusBarState.SHADE_LOCKED + && newState == StatusBarState.KEYGUARD) { + mView.requestAnimateEverything(); + } + } + + @Override + public void onStateChanged(int newState) { + mView.setStatusBarState(newState); + } + + @Override + public void onStatePostChange() { + mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(), + mLockscreenUserManager.isAnyProfilePublicMode()); + mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); + } + }; + + private final UserChangedListener mLockscreenUserChangeListener = new UserChangedListener() { + @Override + public void onUserChanged(int userId) { + mView.setCurrentUserid(userId); + mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode()); + } + }; + + private final OnMenuEventListener mMenuEventListener = new OnMenuEventListener() { + @Override + public void onMenuClicked( + View view, int x, int y, NotificationMenuRowPlugin.MenuItem item) { + if (!mAllowLongPress) { + return; + } + if (view instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) view; + mMetricsLogger.write(row.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_TOUCH_GEAR) + .setType(MetricsEvent.TYPE_ACTION) + ); + } + mNotificationGutsManager.openGuts(view, x, y, item); + } + + @Override + public void onMenuReset(View row) { + mView.onMenuReset(row); + } + + @Override + public void onMenuShown(View row) { + if (row instanceof ExpandableNotificationRow) { + ExpandableNotificationRow notificationRow = (ExpandableNotificationRow) row; + mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker() + .setCategory(MetricsEvent.ACTION_REVEAL_GEAR) + .setType(MetricsEvent.TYPE_ACTION)); + mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true); + mView.onMenuShown(row); + mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, + false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */, + false /* resetMenu */); + + // Check to see if we want to go directly to the notification guts + NotificationMenuRowPlugin provider = notificationRow.getProvider(); + if (provider.shouldShowGutsOnSnapOpen()) { + NotificationMenuRowPlugin.MenuItem item = provider.menuItemToExposeOnSnap(); + if (item != null) { + Point origin = provider.getRevealAnimationOrigin(); + mNotificationGutsManager.openGuts(row, origin.x, origin.y, item); + } else { + Log.e(TAG, "Provider has shouldShowGutsOnSnapOpen, but provided no " + + "menu item in menuItemtoExposeOnSnap. Skipping."); + } + + // Close the menu row since we went directly to the guts + mView.resetExposedMenuView(false, true); + } + } + } + }; + @Inject public NotificationStackScrollLayoutController( @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, @@ -130,7 +246,14 @@ public class NotificationStackScrollLayoutController { NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, DynamicPrivacyController dynamicPrivacyController, - ConfigurationController configurationController) { + ConfigurationController configurationController, + SysuiStatusBarStateController statusBarStateController, + KeyguardMediaController keyguardMediaController, + KeyguardBypassController keyguardBypassController, + ZenModeController zenModeController, + SysuiColorExtractor colorExtractor, + NotificationLockscreenUserManager lockscreenUserManager, + MetricsLogger metricsLogger) { mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; mHeadsUpManager = headsUpManager; @@ -138,19 +261,28 @@ public class NotificationStackScrollLayoutController { mTunerService = tunerService; mDynamicPrivacyController = dynamicPrivacyController; mConfigurationController = configurationController; + mStatusBarStateController = statusBarStateController; + mKeyguardMediaController = keyguardMediaController; + mKeyguardBypassController = keyguardBypassController; + mZenModeController = zenModeController; + mColorExtractor = colorExtractor; + mLockscreenUserManager = lockscreenUserManager; + mMetricsLogger = metricsLogger; } public void attach(NotificationStackScrollLayout view) { mView = view; mView.setController(this); - - if (mAllowLongPress) { - mView.setLongPressListener(mNotificationGutsManager::openGuts); - } + mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled); mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here? mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); + mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener); + mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId()); + + mView.setMenuEventListener(mMenuEventListener); + mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate); mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded); @@ -165,6 +297,23 @@ public class NotificationStackScrollLayoutController { Settings.Secure.NOTIFICATION_DISMISS_RTL, Settings.Secure.NOTIFICATION_HISTORY_ENABLED); + mColorExtractor.addOnColorsChangedListener((colorExtractor, which) -> { + final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); + mView.updateDecorViews(useDarkText); + }); + + mKeyguardMediaController.setVisibilityChangedListener(visible -> { + mView.setKeyguardMediaControllorVisible(visible); + if (visible) { + mView.generateAddAnimation( + mKeyguardMediaController.getView(), false /*fromMoreCard */); + } else { + mView.generateRemoveAnimation(mKeyguardMediaController.getView()); + } + mView.requestChildrenUpdate(); + return Unit.INSTANCE; + }); + if (mView.isAttachedToWindow()) { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } @@ -484,7 +633,7 @@ public class NotificationStackScrollLayoutController { } public void updateEmptyShadeView(boolean visible) { - mView.updateEmptyShadeView(visible); + mView.updateEmptyShadeView(visible, mZenModeController.areNotificationsHiddenInShade()); } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { @@ -646,7 +795,7 @@ public class NotificationStackScrollLayoutController { @Override public void notifyGroupChildAdded(ExpandableView row) { - mView.onViewAddedInternal(row); + mView.notifyGroupChildAdded(row); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java index 1523653dec3c..3dc29a1ae4d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java @@ -53,7 +53,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ private GroupEntry mParent; - private int mSection = -1; /* If set, use this creation time instead of mClock.uptimeMillis */ private long mCreationTime = -1; @@ -68,7 +67,6 @@ public class NotificationEntryBuilder { mRankingBuilder = new RankingBuilder(source.getRanking()); mParent = source.getParent(); - mSection = source.getSection(); mCreationTime = source.getCreationTime(); } @@ -104,7 +102,6 @@ public class NotificationEntryBuilder { /* ListEntry properties */ entry.setParent(mParent); - entry.getAttachState().setSectionIndex(mSection); return entry; } @@ -117,14 +114,6 @@ public class NotificationEntryBuilder { } /** - * Sets the section. - */ - public NotificationEntryBuilder setSection(int section) { - mSection = section; - return this; - } - - /** * Sets the SBN directly. If set, causes all calls to delegated SbnBuilder methods to be * ignored. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 6fa5055c875d..8acb705c744d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -35,6 +35,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static java.util.Collections.singletonList; + import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -46,6 +48,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationInteractionTracker; import com.android.systemui.statusbar.notification.collection.ShadeListBuilder.OnRenderListListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener; import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeSortListener; @@ -54,7 +57,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeL import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.CollectionReadyForBuildListener; import com.android.systemui.util.time.FakeSystemClock; @@ -71,7 +74,6 @@ import org.mockito.Spy; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -496,7 +498,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { assertTrue(entry.hasFinishedInitialization()); // WHEN the pipeline is kicked off - mReadyForBuildListener.onBuildList(Arrays.asList(entry)); + mReadyForBuildListener.onBuildList(singletonList(entry)); // THEN the entry's initialization time is reset assertFalse(entry.hasFinishedInitialization()); @@ -609,13 +611,18 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a filter that removes all PACKAGE_4 notifs and sections that divide // notifs based on package name mListBuilder.addPreGroupFilter(new PackageFilter(PACKAGE_4)); - final NotifSection pkg1Section = spy(new PackageSection(PACKAGE_1)); - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); + final NotifSectioner pkg1Sectioner = spy(new PackageSectioner(PACKAGE_1)); + final NotifSectioner pkg2Sectioner = spy(new PackageSectioner(PACKAGE_2)); // NOTE: no package 3 section explicitly added, so notifs with package 3 will get set by // ShadeListBuilder's sDefaultSection which will demote it to the last section - final NotifSection pkg4Section = spy(new PackageSection(PACKAGE_4)); - final NotifSection pkg5Section = spy(new PackageSection(PACKAGE_5)); - mListBuilder.setSections(Arrays.asList(pkg1Section, pkg2Section, pkg4Section, pkg5Section)); + final NotifSectioner pkg4Sectioner = spy(new PackageSectioner(PACKAGE_4)); + final NotifSectioner pkg5Sectioner = spy(new PackageSectioner(PACKAGE_5)); + mListBuilder.setSectioners( + Arrays.asList(pkg1Sectioner, pkg2Sectioner, pkg4Sectioner, pkg5Sectioner)); + + final NotifSection pkg1Section = new NotifSection(pkg1Sectioner, 0); + final NotifSection pkg2Section = new NotifSection(pkg2Sectioner, 1); + final NotifSection pkg5Section = new NotifSection(pkg5Sectioner, 3); // WHEN we build a list with different packages addNotif(0, PACKAGE_4); @@ -648,72 +655,61 @@ public class ShadeListBuilderTest extends SysuiTestCase { // THEN the first section (pkg1Section) is called on all top level elements (but // no children and no entries that were filtered out) - verify(pkg1Section).isInSection(mEntrySet.get(1)); - verify(pkg1Section).isInSection(mEntrySet.get(2)); - verify(pkg1Section).isInSection(mEntrySet.get(3)); - verify(pkg1Section).isInSection(mEntrySet.get(7)); - verify(pkg1Section).isInSection(mEntrySet.get(8)); - verify(pkg1Section).isInSection(mEntrySet.get(9)); - verify(pkg1Section).isInSection(mBuiltList.get(3)); - - verify(pkg1Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg1Section, never()).isInSection(mEntrySet.get(10)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(1)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(2)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(7)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(8)); + verify(pkg1Sectioner).isInSection(mEntrySet.get(9)); + verify(pkg1Sectioner).isInSection(mBuiltList.get(3)); + + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg1Sectioner, never()).isInSection(mEntrySet.get(10)); // THEN the last section (pkg5Section) is not called on any of the entries that were // filtered or already in a section - verify(pkg5Section, never()).isInSection(mEntrySet.get(0)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(1)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(2)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(4)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(5)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(6)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(7)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(8)); - verify(pkg5Section, never()).isInSection(mEntrySet.get(10)); - - verify(pkg5Section).isInSection(mEntrySet.get(3)); - verify(pkg5Section).isInSection(mEntrySet.get(9)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(0)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(1)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(2)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(4)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(5)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(6)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(7)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(8)); + verify(pkg5Sectioner, never()).isInSection(mEntrySet.get(10)); + + verify(pkg5Sectioner).isInSection(mEntrySet.get(3)); + verify(pkg5Sectioner).isInSection(mEntrySet.get(9)); // THEN the correct section is assigned for entries in pkg1Section - assertEquals(pkg1Section, mEntrySet.get(2).getNotifSection()); - assertEquals(0, mEntrySet.get(2).getSection()); - assertEquals(pkg1Section, mEntrySet.get(7).getNotifSection()); - assertEquals(0, mEntrySet.get(7).getSection()); + assertEquals(pkg1Section, mEntrySet.get(2).getSection()); + assertEquals(pkg1Section, mEntrySet.get(7).getSection()); // THEN the correct section is assigned for entries in pkg2Section - assertEquals(pkg2Section, mEntrySet.get(1).getNotifSection()); - assertEquals(1, mEntrySet.get(1).getSection()); - assertEquals(pkg2Section, mEntrySet.get(8).getNotifSection()); - assertEquals(1, mEntrySet.get(8).getSection()); - assertEquals(pkg2Section, mBuiltList.get(3).getNotifSection()); - assertEquals(1, mBuiltList.get(3).getSection()); + assertEquals(pkg2Section, mEntrySet.get(1).getSection()); + assertEquals(pkg2Section, mEntrySet.get(8).getSection()); + assertEquals(pkg2Section, mBuiltList.get(3).getSection()); // THEN no section was assigned to entries in pkg4Section (since they were filtered) - assertEquals(null, mEntrySet.get(0).getNotifSection()); - assertEquals(-1, mEntrySet.get(0).getSection()); - assertEquals(null, mEntrySet.get(10).getNotifSection()); - assertEquals(-1, mEntrySet.get(10).getSection()); - + assertNull(mEntrySet.get(0).getSection()); + assertNull(mEntrySet.get(10).getSection()); // THEN the correct section is assigned for entries in pkg5Section - assertEquals(pkg5Section, mEntrySet.get(9).getNotifSection()); - assertEquals(3, mEntrySet.get(9).getSection()); + assertEquals(pkg5Section, mEntrySet.get(9).getSection()); // THEN the children entries are assigned the same section as its parent - assertEquals(mBuiltList.get(3).getNotifSection(), child(5).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(5).entry.getSection()); - assertEquals(mBuiltList.get(3).getNotifSection(), child(6).entry.getNotifSection()); assertEquals(mBuiltList.get(3).getSection(), child(6).entry.getSection()); } @Test public void testNotifUsesDefaultSection() { // GIVEN a Section for Package2 - final NotifSection pkg2Section = spy(new PackageSection(PACKAGE_2)); - mListBuilder.setSections(Arrays.asList(pkg2Section)); + final NotifSectioner pkg2Section = spy(new PackageSectioner(PACKAGE_2)); + mListBuilder.setSectioners(singletonList(pkg2Section)); // WHEN we build a list with pkg1 and pkg2 packages addNotif(0, PACKAGE_1); @@ -727,8 +723,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { ); // THEN the entry that didn't have an explicit section gets assigned the DefaultSection - assertEquals(1, notif(0).entry.getSection()); - assertNotNull(notif(0).entry.getNotifSection()); + assertNotNull(notif(0).entry.getSection()); + assertEquals(1, notif(0).entry.getSectionIndex()); } @Test @@ -763,15 +759,15 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a bunch of registered listeners and pluggables NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1)); NotifPromoter promoter = spy(new IdPromoter(3)); - NotifSection section = spy(new PackageSection(PACKAGE_1)); + NotifSectioner section = spy(new PackageSectioner(PACKAGE_1)); NotifComparator comparator = spy(new HypeComparator(PACKAGE_4)); NotifFilter preRenderFilter = spy(new PackageFilter(PACKAGE_5)); mListBuilder.addPreGroupFilter(preGroupFilter); mListBuilder.addOnBeforeTransformGroupsListener(mOnBeforeTransformGroupsListener); mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(mOnBeforeSortListener); - mListBuilder.setComparators(Collections.singletonList(comparator)); - mListBuilder.setSections(Arrays.asList(section)); + mListBuilder.setComparators(singletonList(comparator)); + mListBuilder.setSectioners(singletonList(section)); mListBuilder.addOnBeforeFinalizeFilterListener(mOnBeforeFinalizeFilterListener); mListBuilder.addFinalizeFilter(preRenderFilter); mListBuilder.addOnBeforeRenderListListener(mOnBeforeRenderListListener); @@ -821,13 +817,13 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a variety of pluggables NotifFilter packageFilter = new PackageFilter(PACKAGE_1); NotifPromoter idPromoter = new IdPromoter(4); - NotifSection section = new PackageSection(PACKAGE_1); + NotifSectioner section = new PackageSectioner(PACKAGE_1); NotifComparator hypeComparator = new HypeComparator(PACKAGE_2); mListBuilder.addPreGroupFilter(packageFilter); mListBuilder.addPromoter(idPromoter); - mListBuilder.setSections(Arrays.asList(section)); - mListBuilder.setComparators(Collections.singletonList(hypeComparator)); + mListBuilder.setSectioners(singletonList(section)); + mListBuilder.setComparators(singletonList(hypeComparator)); // GIVEN a set of random notifs addNotif(0, PACKAGE_1); @@ -973,7 +969,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { RecordingOnBeforeSortListener listener = new RecordingOnBeforeSortListener(); mListBuilder.addOnBeforeSortListener(listener); - mListBuilder.setComparators(Arrays.asList(new HypeComparator(PACKAGE_3))); + mListBuilder.setComparators(singletonList(new HypeComparator(PACKAGE_3))); // GIVEN some new notifs out of order addNotif(0, PACKAGE_1); @@ -1093,7 +1089,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { NotifComparator comparator = new HypeComparator(PACKAGE_5); OnBeforeRenderListListener listener = (list) -> comparator.invalidateList(); - mListBuilder.setComparators(Collections.singletonList(comparator)); + mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); // WHEN we try to run the pipeline and the comparator is invalidated @@ -1420,10 +1416,10 @@ public class ShadeListBuilderTest extends SysuiTestCase { } /** Represents a section for the passed pkg */ - private static class PackageSection extends NotifSection { + private static class PackageSectioner extends NotifSectioner { private final String mPackage; - PackageSection(String pkg) { + PackageSectioner(String pkg) { super("PackageSection_" + pkg); mPackage = pkg; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index ce8ce2e39bcc..639e791cbf23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -21,11 +21,9 @@ import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_MIN; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,7 +35,6 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.ArraySet; import androidx.test.filters.SmallTest; @@ -48,8 +45,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; -import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -61,8 +57,6 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -78,7 +72,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { private AppOpsCoordinator mAppOpsCoordinator; private NotifFilter mForegroundFilter; private NotifLifetimeExtender mForegroundNotifLifetimeExtender; - private NotifSection mFgsSection; + private NotifSectioner mFgsSection; private FakeSystemClock mClock = new FakeSystemClock(); private FakeExecutor mExecutor = new FakeExecutor(mClock); @@ -111,7 +105,7 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { lifetimeExtenderCaptor.capture()); mForegroundNotifLifetimeExtender = lifetimeExtenderCaptor.getValue(); - mFgsSection = mAppOpsCoordinator.getSection(); + mFgsSection = mAppOpsCoordinator.getSectioner(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt index be5c8a846afb..c49393d2ed34 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt @@ -25,7 +25,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import org.junit.Assert.assertFalse @@ -45,7 +45,7 @@ import org.mockito.Mockito.`when` as whenever class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: private lateinit var promoter: NotifPromoter - private lateinit var peopleSection: NotifSection + private lateinit var peopleSectioner: NotifSectioner @Mock private lateinit var pipeline: NotifPipeline @@ -70,7 +70,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { verify(pipeline).addPromoter(notifPromoterCaptor.capture()) promoter = notifPromoterCaptor.value - peopleSection = coordinator.getSection() + peopleSectioner = coordinator.sectioner entry = NotificationEntryBuilder().setChannel(channel).build() } @@ -88,7 +88,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { entry.sbn, entry.ranking)).thenReturn(TYPE_PERSON) // only put people notifications in this section - assertTrue(peopleSection.isInSection(entry)) - assertFalse(peopleSection.isInSection(NotificationEntryBuilder().build())) + assertTrue(peopleSectioner.isInSection(entry)) + assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build())) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java index 730481afe638..fa992a5d5dbb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder; @@ -64,7 +64,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { private NotifPromoter mNotifPromoter; private NotifLifetimeExtender mNotifLifetimeExtender; private OnHeadsUpChangedListener mOnHeadsUpChangedListener; - private NotifSection mNotifSection; + private NotifSectioner mNotifSectioner; @Mock private NotifPipeline mNotifPipeline; @Mock private HeadsUpManager mHeadsUpManager; @@ -111,7 +111,7 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { mNotifLifetimeExtender = notifLifetimeExtenderCaptor.getValue(); mOnHeadsUpChangedListener = headsUpChangedListenerCaptor.getValue(); - mNotifSection = mCoordinator.getSection(); + mNotifSectioner = mCoordinator.getSectioner(); mNotifLifetimeExtender.setCallback(mEndLifetimeExtension); mEntry = new NotificationEntryBuilder().build(); } @@ -132,8 +132,8 @@ public class HeadsUpCoordinatorTest extends SysuiTestCase { setCurrentHUN(mEntry); // THEN only section the current HUN, mEntry - assertTrue(mNotifSection.isInSection(mEntry)); - assertFalse(mNotifSection.isInSection(new NotificationEntryBuilder().build())); + assertTrue(mNotifSectioner.isInSection(mEntry)); + assertFalse(mNotifSectioner.isInSection(new NotificationEntryBuilder().build())); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java index 5f10f38b2ee8..3a7d28ab56ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; -import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection; +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; import org.junit.Before; @@ -61,8 +61,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { private NotifFilter mCapturedSuspendedFilter; private NotifFilter mCapturedDozingFilter; - private NotifSection mAlertingSection; - private NotifSection mSilentSection; + private NotifSectioner mAlertingSectioner; + private NotifSectioner mSilentSectioner; @Before public void setup() { @@ -76,8 +76,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { mCapturedSuspendedFilter = mNotifFilterCaptor.getAllValues().get(0); mCapturedDozingFilter = mNotifFilterCaptor.getAllValues().get(1); - mAlertingSection = rankingCoordinator.getAlertingSection(); - mSilentSection = rankingCoordinator.getSilentSection(); + mAlertingSectioner = rankingCoordinator.getAlertingSectioner(); + mSilentSectioner = rankingCoordinator.getSilentSectioner(); } @Test @@ -146,8 +146,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(true); // THEN entry is in the alerting section - assertTrue(mAlertingSection.isInSection(mEntry)); - assertFalse(mSilentSection.isInSection(mEntry)); + assertTrue(mAlertingSectioner.isInSection(mEntry)); + assertFalse(mSilentSectioner.isInSection(mEntry)); } @Test @@ -156,8 +156,8 @@ public class RankingCoordinatorTest extends SysuiTestCase { when(mHighPriorityProvider.isHighPriority(mEntry)).thenReturn(false); // THEN entry is in the silent section - assertFalse(mAlertingSection.isInSection(mEntry)); - assertTrue(mSilentSection.isInSection(mEntry)); + assertFalse(mAlertingSectioner.isInSection(mEntry)); + assertTrue(mSilentSectioner.isInSection(mEntry)); } private RankingBuilder getRankingForUnfilteredNotif() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 9a6674e165e4..bca7b312ff15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -24,9 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; @@ -47,19 +45,15 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.NotificationLockscreenUserManager; -import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -87,14 +81,13 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationIconAreaController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBar; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.util.leak.LeakDetector; import org.junit.After; @@ -103,7 +96,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -135,14 +127,11 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private MetricsLogger mMetricsLogger; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private KeyguardMediaController mKeyguardMediaController; - @Mock private ZenModeController mZenModeController; + @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider; @Mock private NotificationSectionsManager mNotificationSectionsManager; @Mock private NotificationSection mNotificationSection; - @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private FeatureFlags mFeatureFlags; - private UserChangedListener mUserChangedListener; + @Mock private SysuiStatusBarStateController mStatusBarStateController; private NotificationEntryManager mEntryManager; private int mOriginalInterruptionModelSetting; private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake(); @@ -172,8 +161,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mDependency.injectMockDependency(ShadeController.class); when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController); - ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor - .forClass(UserChangedListener.class); mEntryManager = new NotificationEntryManager( mock(NotificationEntryManagerLogger.class), mock(NotificationGroupManager.class), @@ -211,17 +198,15 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // which refer to members of NotificationStackScrollLayout. The spy // holds a copy of the CUT's instances of these KeyguardBypassController, so they still // refer to the CUT's member variables, not the spy's member variables. - mStackScrollerInternal = new NotificationStackScrollLayout(getContext(), null, + mStackScrollerInternal = new NotificationStackScrollLayout( + getContext(), + null, mNotificationRoundnessManager, mock(DynamicPrivacyController.class), - mock(SysuiStatusBarStateController.class), + mStatusBarStateController, mHeadsUpManager, - mKeyguardBypassController, - mKeyguardMediaController, new FalsingManagerFake(), - mLockscreenUserManager, mock(NotificationGutsManager.class), - mZenModeController, mNotificationSectionsManager, mock(ForegroundServiceSectionController.class), mock(ForegroundServiceDismissalFeatureController.class), @@ -231,8 +216,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mock(NotifCollection.class), mUiEventLoggerFake ); - verify(mLockscreenUserManager).addUserChangedListener(userChangedCaptor.capture()); - mUserChangedListener = userChangedCaptor.getValue(); + mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); mStackScroller.setStatusBar(mBar); @@ -269,9 +253,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { @Test public void updateEmptyView_dndSuppressing() { when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -280,9 +263,8 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_dndNotSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); } @@ -291,12 +273,10 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { public void updateEmptyView_noNotificationsToDndSuppressing() { mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mEmptyShadeView.willBeGone()).thenReturn(true); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, false); verify(mEmptyShadeView).setText(R.string.empty_shade_text); - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - mStackScroller.updateEmptyShadeView(true); + mStackScroller.updateEmptyShadeView(true, true); verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text); } @@ -312,12 +292,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - public void testOnStatePostChange_verifyIfProfileIsPublic() { - mUserChangedListener.onUserChanged(0); - verify(mLockscreenUserManager).isAnyProfilePublicMode(); - } - - @Test public void manageNotifications_visible() { FooterView view = mock(FooterView.class); mStackScroller.setFooterView(view); @@ -473,61 +447,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { assertNull(swipeActionHelper.getExposedMenuView()); } - class LogMatcher implements ArgumentMatcher<LogMaker> { - private int mCategory, mType; - - LogMatcher(int category, int type) { - mCategory = category; - mType = type; - } - public boolean matches(LogMaker l) { - return (l.getCategory() == mCategory) - && (l.getType() == mType); - } - - public String toString() { - return String.format("LogMaker(%d, %d)", mCategory, mType); - } - } - - private LogMaker logMatcher(int category, int type) { - return argThat(new LogMatcher(category, type)); - } - - @Test - @UiThreadTest - public void testOnMenuClickedLogging() { - // Set up the object under test to have a valid mLongPressListener. We're testing an - // anonymous-class member, mMenuEventListener, so we need to modify the state of the - // class itself, not the Mockito spy copied from it. See notes in setup. - mStackScrollerInternal.setLongPressListener( - mock(ExpandableNotificationRow.LongPressListener.class)); - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuClicked(row, 0, 0, mock( - NotificationMenuRowPlugin.MenuItem.class)); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - - @Test - @UiThreadTest - public void testOnMenuShownLogging() { ; - - ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); - when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( - MetricsProto.MetricsEvent.VIEW_UNKNOWN)); - - mStackScroller.mMenuEventListener.onMenuShown(row); - verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data - verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, - MetricsProto.MetricsEvent.TYPE_ACTION)); - } - @Test public void testClearNotifications_All() { mStackScroller.clearNotifications(NotificationStackScrollLayout.ROWS_ALL, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index e3acf0213725..83d6ac904bfe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -17,25 +17,45 @@ package com.android.systemui.statusbar.notification.stack; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.metrics.LogMaker; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.SysuiTestCase; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.media.KeyguardMediaController; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.NotificationLockscreenUserManager; +import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -55,15 +75,27 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private TunerService mTunerService; @Mock - private AmbientState mAmbientState; - @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private ConfigurationController mConfigurationController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; + @Mock + private ZenModeController mZenModeController; + @Mock + private KeyguardMediaController mKeyguardMediaController; + @Mock + private SysuiStatusBarStateController mSysuiStatusBarStateController; + @Mock + private KeyguardBypassController mKeyguardBypassController; + @Mock + private SysuiColorExtractor mColorExtractor; + @Mock + private NotificationLockscreenUserManager mNotificationLockscreenUserManager; + @Mock + private MetricsLogger mMetricsLogger; - NotificationStackScrollLayoutController mController; + private NotificationStackScrollLayoutController mController; @Before public void setUp() { @@ -76,7 +108,14 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mNotificationRoundnessManager, mTunerService, mDynamicPrivacyController, - mConfigurationController + mConfigurationController, + mSysuiStatusBarStateController, + mKeyguardMediaController, + mKeyguardBypassController, + mZenModeController, + mColorExtractor, + mNotificationLockscreenUserManager, + mMetricsLogger ); when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true); @@ -112,4 +151,140 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mController.mConfigurationListener.onDensityOrFontScaleChanged(); verify(mNotificationStackScrollLayout).reinflateViews(); } + + @Test + public void testUpdateEmptyShadeView_notificationsVisible() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + true /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + true /* notifVisibleInShade */); + } + + @Test + public void testUpdateEmptyShadeView_notificationsHidden() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + mController.attach(mNotificationStackScrollLayout); + + mController.updateEmptyShadeView(true /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + true /* visible */, + false /* notifVisibleInShade */); + reset(mNotificationStackScrollLayout); + mController.updateEmptyShadeView(false /* visible */); + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + false /* visible */, + false /* notifVisibleInShade */); + } + + @Test + public void testOnUserChange_verifySensitiveProfile() { + when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); + + ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor + .forClass(UserChangedListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationLockscreenUserManager) + .addUserChangedListener(userChangedCaptor.capture()); + reset(mNotificationStackScrollLayout); + + UserChangedListener changedListener = userChangedCaptor.getValue(); + changedListener.onUserChanged(0); + verify(mNotificationStackScrollLayout).setCurrentUserid(0); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + @Test + public void testOnStatePostChange_verifyIfProfileIsPublic() { + when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(true); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mSysuiStatusBarStateController).addCallback( + stateListenerArgumentCaptor.capture(), anyInt()); + + StatusBarStateController.StateListener stateListener = + stateListenerArgumentCaptor.getValue(); + + stateListener.onStatePostChange(); + verify(mNotificationStackScrollLayout).updateSensitiveness(false, true); + } + + + @Test + public void testOnMenuShownLogging() { ; + + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuShown(row); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_REVEAL_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + @Test + public void testOnMenuClickedLogging() { + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS); + when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker( + MetricsProto.MetricsEvent.VIEW_UNKNOWN)); + + + ArgumentCaptor<OnMenuEventListener> onMenuEventListenerArgumentCaptor = + ArgumentCaptor.forClass(OnMenuEventListener.class); + + mController.attach(mNotificationStackScrollLayout); + verify(mNotificationStackScrollLayout).setMenuEventListener( + onMenuEventListenerArgumentCaptor.capture()); + + OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue(); + + onMenuEventListener.onMenuClicked(row, 0, 0, mock( + NotificationMenuRowPlugin.MenuItem.class)); + verify(row.getEntry().getSbn()).getLogMaker(); // This writes most of the log data + verify(mMetricsLogger).write(logMatcher(MetricsProto.MetricsEvent.ACTION_TOUCH_GEAR, + MetricsProto.MetricsEvent.TYPE_ACTION)); + } + + private LogMaker logMatcher(int category, int type) { + return argThat(new LogMatcher(category, type)); + } + + static class LogMatcher implements ArgumentMatcher<LogMaker> { + private int mCategory, mType; + + LogMatcher(int category, int type) { + mCategory = category; + mType = type; + } + public boolean matches(LogMaker l) { + return (l.getCategory() == mCategory) + && (l.getType() == mType); + } + + public String toString() { + return String.format("LogMaker(%d, %d)", mCategory, mType); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java index 260ff2dafeed..33ece0084906 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java @@ -70,9 +70,7 @@ public class DeviceConfigProxyFake extends DeviceConfigProxy { for (Pair<Executor, OnPropertiesChangedListener> listener : mListeners) { Properties.Builder propBuilder = new Properties.Builder(namespace); - for (String key : mProperties.get(namespace).keySet()) { - propBuilder.setString(key, mProperties.get(namespace).get(key)); - } + propBuilder.setString(name, value); listener.first.execute(() -> listener.second.onPropertiesChanged(propBuilder.build())); } return true; diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java new file mode 100644 index 000000000000..64e89579393e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 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.util; + +import static android.provider.DeviceConfig.Properties; + +import static com.google.common.truth.Truth.assertThat; + +import android.provider.DeviceConfig.OnPropertiesChangedListener; + +import androidx.annotation.NonNull; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DeviceConfigProxyFakeTest extends SysuiTestCase { + private static final String NAMESPACE = "foobar"; + + private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); + + private DeviceConfigProxyFake mDeviceConfigProxyFake; + + @Before + public void setup() { + mDeviceConfigProxyFake = new DeviceConfigProxyFake(); + } + + @Test + public void testOnPropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String key = "foo"; + String value = "bar"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, key, value, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(key, "")).isEqualTo(value); + } + + @Test + public void testOnMultiplePropertiesChanged() { + TestableListener onPropertiesChangedListener = new TestableListener(); + String keyA = "foo"; + String valueA = "bar"; + String keyB = "bada"; + String valueB = "boom"; + + mDeviceConfigProxyFake.addOnPropertiesChangedListener( + NAMESPACE, mFakeExecutor, onPropertiesChangedListener); + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyA, valueA, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyA, "")).isEqualTo(valueA); + + mDeviceConfigProxyFake.setProperty(NAMESPACE, keyB, valueB, false); + mFakeExecutor.runAllReady(); + assertThat(onPropertiesChangedListener.mProperties).isNotNull(); + assertThat(onPropertiesChangedListener.mProperties.getKeyset().size()).isEqualTo(1); + assertThat(onPropertiesChangedListener.mProperties.getString(keyB, "")).isEqualTo(valueB); + } + + private static class TestableListener implements OnPropertiesChangedListener { + Properties mProperties; + + TestableListener() { + } + @Override + public void onPropertiesChanged(@NonNull Properties properties) { + mProperties = properties; + } + } +} diff --git a/packages/Tethering/AndroidManifestBase.xml b/packages/Tethering/AndroidManifestBase.xml index fa85f66489d7..97c3988829fe 100644 --- a/packages/Tethering/AndroidManifestBase.xml +++ b/packages/Tethering/AndroidManifestBase.xml @@ -23,6 +23,7 @@ <application android:label="Tethering" android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true"> + android:directBootAware="true" + android:usesCleartextTraffic="true"> </application> </manifest> diff --git a/services/Android.bp b/services/Android.bp index b348b91a1bd7..ef52c2aff002 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -156,10 +156,14 @@ droidstubs { java_library { name: "android_system_server_stubs_current", + defaults: ["android_stubs_dists_default"], srcs: [":services-stubs.sources"], installable: false, static_libs: ["android_module_lib_stubs_current"], sdk_version: "none", system_modules: "none", java_version: "1.8", + dist: { + dir: "apistubs/android/system-server", + }, } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 669bb24e0e77..a75b64ce08f5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -284,11 +284,13 @@ public class AccessibilityWindowManager { * Computes partial interactive region of given windowId. * * @param windowId The windowId + * @param forceComputeRegion set outRegion when the windowId matches one on the screen even + * though the region is not covered by other windows above it. * @param outRegion The output to which to write the bounds. - * @return true if outRegion is not empty. + * @return {@code true} if outRegion is not empty. */ boolean computePartialInteractiveRegionForWindowLocked(int windowId, - @NonNull Region outRegion) { + boolean forceComputeRegion, @NonNull Region outRegion) { if (mWindows == null) { return false; } @@ -309,6 +311,9 @@ public class AccessibilityWindowManager { currentWindow.getRegionInScreen(currentWindowRegions); outRegion.set(currentWindowRegions); windowInteractiveRegion = outRegion; + if (forceComputeRegion) { + windowInteractiveRegionChanged = true; + } continue; } } else if (currentWindow.getType() @@ -1240,10 +1245,13 @@ public class AccessibilityWindowManager { */ public boolean computePartialInteractiveRegionForWindowLocked(int windowId, @NonNull Region outRegion) { - windowId = resolveParentWindowIdLocked(windowId); - final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId); + final int parentWindowId = resolveParentWindowIdLocked(windowId); + final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked( + parentWindowId); + if (observer != null) { - return observer.computePartialInteractiveRegionForWindowLocked(windowId, outRegion); + return observer.computePartialInteractiveRegionForWindowLocked(parentWindowId, + parentWindowId != windowId, outRegion); } return false; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index a2d58c8019fc..b587dd33c4e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -561,24 +561,6 @@ public class TouchExplorer extends BaseEventStreamTransformation // stream consistent. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } - if (mGestureDetector.isMultiFingerGesturesEnabled() - && mGestureDetector.isTwoFingerPassthroughEnabled()) { - if (event.getPointerCount() == 3) { - boolean isOnBottomEdge = false; - // If three fingers go down on the bottom edge of the screen, delegate immediately. - final long screenHeight = mContext.getResources().getDisplayMetrics().heightPixels; - for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { - if (mReceivedPointerTracker.getReceivedPointerDownY(i) - > (screenHeight - mEdgeSwipeHeightPixels)) { - isOnBottomEdge = true; - } - } - if (isOnBottomEdge) { - mState.startDelegating(); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); - } - } - } } /** @@ -644,12 +626,34 @@ public class TouchExplorer extends BaseEventStreamTransformation break; default: if (mGestureDetector.isMultiFingerGesturesEnabled()) { - return; + if (mGestureDetector.isTwoFingerPassthroughEnabled()) { + if (event.getPointerCount() == 3) { + boolean isOnBottomEdge = true; + // If three fingers went down on the bottom edge of the screen, delegate + // immediately. + final long screenHeight = + mContext.getResources().getDisplayMetrics().heightPixels; + for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) { + if (mReceivedPointerTracker.getReceivedPointerDownY(i) + < (screenHeight - mEdgeSwipeHeightPixels)) { + isOnBottomEdge = false; + } + } + if (isOnBottomEdge) { + if (DEBUG) { + Slog.d(LOG_TAG, "Three-finger edge swipe detected."); + } + mState.startDelegating(); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); + } + } + } + } else { + // More than two pointers are delegated to the view hierarchy. + mState.startDelegating(); + event = MotionEvent.obtainNoHistory(event); + mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } - // More than two pointers are delegated to the view hierarchy. - mState.startDelegating(); - event = MotionEvent.obtainNoHistory(event); - mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); break; } } diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index df966c1ea6ac..34e63705d781 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -47,8 +47,8 @@ import java.util.ArrayList; * * {@hide} */ -public class SystemServiceManager { - private static final String TAG = "SystemServiceManager"; +public final class SystemServiceManager { + private static final String TAG = SystemServiceManager.class.getSimpleName(); private static final boolean DEBUG = false; private static final int SERVICE_CALL_WARN_TIME_MS = 50; @@ -250,76 +250,76 @@ public class SystemServiceManager { mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); } - private @NonNull TargetUser getTargetUser(@UserIdInt int userHandle) { + private @NonNull TargetUser getTargetUser(@UserIdInt int userId) { final TargetUser targetUser; synchronized (mTargetUsers) { - targetUser = mTargetUsers.get(userHandle); + targetUser = mTargetUsers.get(userId); } - Preconditions.checkState(targetUser != null, "No TargetUser for " + userHandle); + Preconditions.checkState(targetUser != null, "No TargetUser for " + userId); return targetUser; } /** * Starts the given user. */ - public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) { + public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) { // Create cached TargetUser - final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle); - Preconditions.checkState(userInfo != null, "No UserInfo for " + userHandle); + final UserInfo userInfo = mUserManagerInternal.getUserInfo(userId); + Preconditions.checkState(userInfo != null, "No UserInfo for " + userId); synchronized (mTargetUsers) { - mTargetUsers.put(userHandle, new TargetUser(userInfo)); + mTargetUsers.put(userId, new TargetUser(userInfo)); } - onUser(t, START, userHandle); + onUser(t, START, userId); } /** * Unlocks the given user. */ - public void unlockUser(final @UserIdInt int userHandle) { - onUser(UNLOCKING, userHandle); + public void unlockUser(@UserIdInt int userId) { + onUser(UNLOCKING, userId); } /** * Called after the user was unlocked. */ - public void onUserUnlocked(final @UserIdInt int userHandle) { - onUser(UNLOCKED, userHandle); + public void onUserUnlocked(@UserIdInt int userId) { + onUser(UNLOCKED, userId); } /** * Switches to the given user. */ - public void switchUser(final @UserIdInt int from, final @UserIdInt int to) { + public void switchUser(@UserIdInt int from, @UserIdInt int to) { onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from); } /** * Stops the given user. */ - public void stopUser(final @UserIdInt int userHandle) { - onUser(STOP, userHandle); + public void stopUser(@UserIdInt int userId) { + onUser(STOP, userId); } /** * Cleans up the given user. */ - public void cleanupUser(final @UserIdInt int userHandle) { - onUser(CLEANUP, userHandle); + public void cleanupUser(@UserIdInt int userId) { + onUser(CLEANUP, userId); // Remove cached TargetUser synchronized (mTargetUsers) { - mTargetUsers.remove(userHandle); + mTargetUsers.remove(userId); } } - private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) { - onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle); + private void onUser(@NonNull String onWhat, @UserIdInt int userId) { + onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userId); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, - @UserIdInt int userHandle) { - onUser(t, onWhat, userHandle, UserHandle.USER_NULL); + @UserIdInt int userId) { + onUser(t, onWhat, userId, UserHandle.USER_NULL); } private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 6d71c8e68f77..19871f993d42 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -315,11 +315,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private List<Map<Pair<Integer, ApnSetting>, PreciseDataConnectionState>> mPreciseDataConnectionStates; - static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = - PhoneStateListener.LISTEN_REGISTRATION_FAILURE - | PhoneStateListener.LISTEN_BARRING_INFO; - - static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK = + // Starting in Q, almost all cellular location requires FINE location enforcement. + // Prior to Q, cellular was available with COARSE location enforcement. Bits in this + // list will be checked for COARSE on apps targeting P or earlier and FINE on Q or later. + static final int ENFORCE_LOCATION_PERMISSION_MASK = PhoneStateListener.LISTEN_CELL_LOCATION | PhoneStateListener.LISTEN_CELL_INFO | PhoneStateListener.LISTEN_REGISTRATION_FAILURE @@ -376,7 +375,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " newDefaultPhoneId=" + newDefaultPhoneId); } - //Due to possible risk condition,(notify call back using the new + //Due to possible race condition,(notify call back using the new //defaultSubId comes before new defaultSubId update) we need to recall all //possible missed notify callback synchronized (mRecords) { @@ -909,7 +908,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) { try { if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } @@ -964,7 +964,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " + mCellInfo.get(phoneId)); - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -1518,7 +1519,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellInfoForSubscriber: mCellInfo=" + cellInfo @@ -1797,7 +1799,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { for (Record r : mRecords) { if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) && idMatch(r.subId, subId, phoneId) && - checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) { try { if (DBG_LOC) { log("notifyCellLocation: cellLocation=" + cellLocation @@ -2544,16 +2547,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { boolean shouldCheckLocationPermissions = false; - if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) { + if ((events & ENFORCE_LOCATION_PERMISSION_MASK) != 0) { // Everything that requires fine location started in Q. So far... locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q); - // If we're enforcing fine starting in Q, we also want to enforce coarse starting in Q. - locationQueryBuilder.setMinSdkVersionForCoarse(Build.VERSION_CODES.Q); - locationQueryBuilder.setMinSdkVersionForEnforcement(Build.VERSION_CODES.Q); - shouldCheckLocationPermissions = true; - } - - if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) { + // If we're enforcing fine starting in Q, we also want to enforce coarse even for + // older SDK versions. locationQueryBuilder.setMinSdkVersionForCoarse(0); locationQueryBuilder.setMinSdkVersionForEnforcement(0); shouldCheckLocationPermissions = true; @@ -2750,8 +2748,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { try { if (VDBG) log("checkPossibleMissNotify: onServiceStateChanged state=" + mServiceState[phoneId]); - r.callback.onServiceStateChanged( - new ServiceState(mServiceState[phoneId])); + ServiceState ss = new ServiceState(mServiceState[phoneId]); + if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged(ss); + } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(false)); + } else { + r.callback.onServiceStateChanged( + ss.createLocationInfoSanitizedCopy(true)); + } } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2796,7 +2802,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = " + mCellInfo.get(phoneId)); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { r.callback.onCellInfoChanged(mCellInfo.get(phoneId)); } } catch (RemoteException ex) { @@ -2862,7 +2869,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("checkPossibleMissNotify: onCellLocationChanged mCellIdentity = " + mCellIdentity[phoneId]); } - if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { + if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE) + && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) { // null will be translated to empty CellLocation object in client. r.callback.onCellLocationChanged(mCellIdentity[phoneId]); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 1815dac4c3a8..990a547144a0 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -99,6 +99,7 @@ public class Watchdog { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.audio@7.0::IDevicesFactory", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", "android.hardware.bluetooth@1.0::IBluetoothHci", diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index bf7c68a845c5..0a179e89f757 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1283,6 +1283,7 @@ public class AudioService extends IAudioService.Stub } if (isPlatformTelevision()) { + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller); synchronized (mHdmiClientLock) { if (mHdmiManager != null && mHdmiPlaybackClient != null) { updateHdmiCecSinkLocked(mHdmiCecSink | false); @@ -1302,54 +1303,22 @@ public class AudioService extends IAudioService.Stub } } - /** - * Update volume states for the given device. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param caller caller of this method - */ - private void updateVolumeStatesForAudioDevice(int device, String caller) { + private void checkAddAllFixedVolumeDevices(int device, String caller) { final int numStreamTypes = AudioSystem.getNumStreamTypes(); for (int streamType = 0; streamType < numStreamTypes; streamType++) { - updateVolumeStates(device, streamType, caller); - } - } - - /** - * Update volume states for the given device and given stream. - * - * This will initialize the volume index if no volume index is available. - * If the device is the currently routed device, fixed/full volume policies will be applied. - * - * @param device a single audio device, ensure that this is not a devices bitmask - * @param streamType streamType to be updated - * @param caller caller of this method - */ - private void updateVolumeStates(int device, int streamType, String caller) { - if (!mStreamStates[streamType].hasIndexForDevice(device)) { - // set the default value, if device is affected by a full/fix/abs volume rule, it - // will taken into account in checkFixedVolumeDevices() - mStreamStates[streamType].setIndex( - mStreamStates[mStreamVolumeAlias[streamType]] - .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), - device, caller, true /*hasModifyAudioSettings*/); - } - - // Check if device to be updated is routed for the given audio stream - List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt( - new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build()); - for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) { - if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType( - device)) { - mStreamStates[streamType].checkFixedVolumeDevices(); + if (!mStreamStates[streamType].hasIndexForDevice(device)) { + // set the default value, if device is affected by a full/fix/abs volume rule, it + // will taken into account in checkFixedVolumeDevices() + mStreamStates[streamType].setIndex( + mStreamStates[mStreamVolumeAlias[streamType]] + .getIndex(AudioSystem.DEVICE_OUT_DEFAULT), + device, caller, true /*hasModifyAudioSettings*/); + } + mStreamStates[streamType].checkFixedVolumeDevices(); - // Unmute streams if required if device is full volume - if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) { - mStreamStates[streamType].mute(false); - } + // Unmute streams if device is full volume + if (mFullVolumeDevices.contains(device)) { + mStreamStates[streamType].mute(false); } } } @@ -1899,13 +1868,8 @@ public class AudioService extends IAudioService.Stub /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */ public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( @NonNull AudioAttributes attributes) { - enforceModifyAudioRoutingPermission(); - return getDevicesForAttributesInt(attributes); - } - - protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt( - @NonNull AudioAttributes attributes) { Objects.requireNonNull(attributes); + enforceModifyAudioRoutingPermission(); return AudioSystem.getDevicesForAttributes(attributes); } @@ -4952,15 +4916,7 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { for (int stream = 0; stream < mStreamStates.length; stream++) { if (stream != skipStream) { - int devices = mStreamStates[stream].observeDevicesForStream_syncVSS( - false /*checkOthers*/); - - Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices); - for (Integer device : devicesSet) { - // Update volume states for devices routed for the stream - updateVolumeStates(device, stream, - "AudioService#observeDevicesForStreams"); - } + mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/); } } } @@ -5029,7 +4985,7 @@ public class AudioService extends IAudioService.Stub + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller)); // make sure we have a volume entry for this device, and that volume is updated according // to volume behavior - updateVolumeStatesForAudioDevice(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); + checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller); } /** @@ -7251,9 +7207,10 @@ public class AudioService extends IAudioService.Stub // HDMI output removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI); } - updateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI, - "HdmiPlaybackClient.DisplayStatusCallback"); } + + checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, + "HdmiPlaybackClient.DisplayStatusCallback"); } private class MyHdmiControlStatusChangeListenerCallback diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 041bedc3c575..ec12a971e445 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -210,6 +210,7 @@ public class SyncManager { private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm"; private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock"; + private static final boolean USE_WTF_FOR_ACCOUNT_ERROR = false; private static final int SYNC_OP_STATE_VALID = 0; // "1" used to include errors 3, 4 and 5 but now it's split up. @@ -3446,7 +3447,7 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: account doesn't exist."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: account doesn't exist."); + logAccountError("SYNC_OP_STATE_INVALID: account doesn't exist."); return SYNC_OP_STATE_INVALID_NO_ACCOUNT; } // Drop this sync request if it isn't syncable. @@ -3456,14 +3457,14 @@ public class SyncManager { Slog.v(TAG, " Dropping sync operation: " + "isSyncable == SYNCABLE_NO_ACCOUNT_ACCESS"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); + logAccountError("SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS"); return SYNC_OP_STATE_INVALID_NO_ACCOUNT_ACCESS; } if (state == AuthorityInfo.NOT_SYNCABLE) { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: isSyncable == NOT_SYNCABLE"); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); + logAccountError("SYNC_OP_STATE_INVALID: NOT_SYNCABLE"); return SYNC_OP_STATE_INVALID_NOT_SYNCABLE; } @@ -3482,12 +3483,20 @@ public class SyncManager { if (isLoggable) { Slog.v(TAG, " Dropping sync operation: disallowed by settings/network."); } - Slog.wtf(TAG, "SYNC_OP_STATE_INVALID: disallowed by settings/network"); + logAccountError("SYNC_OP_STATE_INVALID: disallowed by settings/network"); return SYNC_OP_STATE_INVALID_SYNC_DISABLED; } return SYNC_OP_STATE_VALID; } + private void logAccountError(String message) { + if (USE_WTF_FOR_ACCOUNT_ERROR) { + Slog.wtf(TAG, message); + } else { + Slog.e(TAG, message); + } + } + private boolean dispatchSyncOperation(SyncOperation op) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Slog.v(TAG, "dispatchSyncOperation: we are going to sync " + op); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index dab8c7fc9d48..2c632d96e738 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2174,10 +2174,14 @@ public final class DisplayManagerService extends SystemService { } } - if (callingUid == Process.SYSTEM_UID - || checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { - flags |= VIRTUAL_DISPLAY_FLAG_TRUSTED; - } else { + if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { + if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + + "create a trusted virtual display."); + } + } + + if ((flags & VIRTUAL_DISPLAY_FLAG_TRUSTED) == 0) { flags &= ~VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 8777ceacf884..1e02f49c43e4 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -36,6 +36,7 @@ import android.media.session.MediaController; import android.media.session.MediaController.PlaybackInfo; import android.media.session.MediaSession; import android.media.session.MediaSession.QueueItem; +import android.media.session.ParcelableListBinder; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Binder; @@ -905,14 +906,24 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } @Override - public void setQueue(ParceledListSlice queue) throws RemoteException { + public void resetQueue() throws RemoteException { synchronized (mLock) { - mQueue = queue == null ? null : (List<QueueItem>) queue.getList(); + mQueue = null; } mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); } @Override + public IBinder getBinderForSetQueue() throws RemoteException { + return new ParcelableListBinder<QueueItem>((list) -> { + synchronized (mLock) { + mQueue = list; + } + mHandler.post(MessageHandler.MSG_UPDATE_QUEUE); + }); + } + + @Override public void setQueueTitle(CharSequence title) throws RemoteException { mQueueTitle = title; mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE); diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index a8a0e2e937d0..9521611c241d 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -1918,7 +1918,7 @@ public class MediaSessionService extends SystemService implements Monitor { final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier(); final long token = Binder.clearCallingIdentity(); try { - // Don't perform sanity check between controllerPackageName and controllerUid. + // Don't perform check between controllerPackageName and controllerUid. // When an (activity|service) runs on the another apps process by specifying // android:process in the AndroidManifest.xml, then PID and UID would have the // running process' information instead of the (activity|service) that has created diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 8af74631fb2e..4b246c3b330c 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -1284,14 +1284,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements pw.increaseIndent(); List<PackageInstallerSession> finalizedSessions = new ArrayList<>(); + List<PackageInstallerSession> orphanedChildSessions = new ArrayList<>(); int N = mSessions.size(); for (int i = 0; i < N; i++) { final PackageInstallerSession session = mSessions.valueAt(i); - // Do not print finalized staged session as active install sessions final PackageInstallerSession rootSession = session.hasParentSessionId() ? getSession(session.getParentSessionId()) : session; + // Do not print orphaned child sessions as active install sessions + if (rootSession == null) { + orphanedChildSessions.add(session); + continue; + } + + // Do not print finalized staged session as active install sessions if (rootSession.isStagedAndInTerminalState()) { finalizedSessions.add(session); continue; @@ -1303,6 +1310,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements pw.println(); pw.decreaseIndent(); + if (!orphanedChildSessions.isEmpty()) { + // Presence of orphaned sessions indicate leak in cleanup for multi-package and + // should be cleaned up. + pw.println("Orphaned install sessions:"); + pw.increaseIndent(); + N = orphanedChildSessions.size(); + for (int i = 0; i < N; i++) { + final PackageInstallerSession session = orphanedChildSessions.get(i); + session.dump(pw); + pw.println(); + } + pw.println(); + pw.decreaseIndent(); + } + pw.println("Finalized install sessions:"); pw.increaseIndent(); N = finalizedSessions.size(); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9c8b972985eb..5fba8b93ff4c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -367,7 +367,6 @@ import com.android.server.pm.parsing.pkg.ParsedPackage; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; -import com.android.server.pm.permission.PermissionsState; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.rollback.RollbackManagerInternal; import com.android.server.security.VerityUtils; @@ -1818,7 +1817,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_PACKAGE_RESTRICTIONS); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); mDirtyUsers.clear(); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -1838,6 +1837,7 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -2518,7 +2518,7 @@ public class PackageManagerService extends IPackageManager.Stub } mSettings.onVolumeForgotten(fsUuid); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } }; @@ -3442,6 +3442,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -3561,7 +3562,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader t.traceBegin("write settings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); t.traceEnd(); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY, SystemClock.uptimeMillis()); @@ -3765,7 +3766,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e); } mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } catch (PackageManagerException e) { // Whoops! Something went very wrong; roll back to the stub and disable the package @@ -3776,9 +3777,8 @@ public class PackageManagerService extends IPackageManager.Stub // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); } - installPackageFromSystemLIF(stubPkg.getCodePath(), - null /*allUserHandles*/, null /*origUserHandles*/, - null /*origPermissionsState*/, true /*writeSettings*/); + installPackageFromSystemLIF(stubPkg.getCodePath(), null /*allUserHandles*/, + null /*origUserHandles*/, true /*writeSettings*/); } catch (PackageManagerException pme) { // Serious WTF; we have to be able to install the stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.getPackageName(), @@ -3792,7 +3792,7 @@ public class PackageManagerService extends IPackageManager.Stub stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android"); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return false; @@ -13092,7 +13092,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } if (sendRemoved) { - killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId), + killApplication(packageName, pkgSetting.appId, userId, "hiding pkg"); sendApplicationHiddenForUser(packageName, pkgSetting, userId); return true; @@ -16280,7 +16280,7 @@ public class PackageManagerService extends IPackageManager.Stub res.setReturnCode(PackageManager.INSTALL_SUCCEEDED); //to update install status Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings"); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } @@ -18877,6 +18877,10 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedAppId = removedAppId; } + if ((deletedPs.sharedUser == null || deletedPs.sharedUser.packages.size() == 0) + && !isUpdatedSystemApp(deletedPs)) { + mPermissionManager.removePermissionsStateTEMP(removedAppId); + } mPermissionManager.updatePermissions(deletedPs.name, null); if (deletedPs.sharedUser != null) { // Remove permissions associated with package. Since runtime @@ -18886,10 +18890,10 @@ public class PackageManagerService extends IPackageManager.Stub // package is successful and this causes a change in gids. boolean shouldKill = false; for (int userId : UserManagerService.getInstance().getUserIds()) { - final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs, - userId); - shouldKill |= userIdToKill == UserHandle.USER_ALL - || userIdToKill >= UserHandle.USER_SYSTEM; + final int userIdToKill = mPermissionManager + .revokeSharedUserPermissionsForDeletedPackageTEMP(deletedPs, + userId); + shouldKill |= userIdToKill != UserHandle.USER_NULL; } // If gids changed, kill all affected packages. if (shouldKill) { @@ -18933,7 +18937,7 @@ public class PackageManagerService extends IPackageManager.Stub // can downgrade to reader if (writeSettings) { // Save settings now - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } if (installedStateChanged) { mSettings.writeKernelMappingLPr(deletedPs); @@ -19020,8 +19024,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs); try { installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles, - outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(), - writeSettings); + outInfo == null ? null : outInfo.origUsers, writeSettings); } catch (PackageManagerException e) { Slog.w(TAG, "Failed to restore system package:" + deletedPkg.getPackageName() + ": " + e.getMessage()); @@ -19052,9 +19055,8 @@ public class PackageManagerService extends IPackageManager.Stub * Installs a package that's already on the system partition. */ private AndroidPackage installPackageFromSystemLIF(@NonNull String codePathString, - @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, - @Nullable PermissionsState origPermissionState, boolean writeSettings) - throws PackageManagerException { + @Nullable int[] allUserHandles, @Nullable int[] origUserHandles, boolean writeSettings) + throws PackageManagerException { final File codePath = new File(codePathString); @ParseFlags int parseFlags = mDefParseFlags @@ -19091,12 +19093,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - // Propagate the permissions state as we do not want to drop on the floor - // runtime permissions. The update permissions method below will take - // care of removing obsolete permissions and grant install permissions. - if (origPermissionState != null) { - ps.getPermissionsState().copyFrom(origPermissionState); - } + // The update permissions method below will take care of removing obsolete permissions + // and granting install permissions. mPermissionManager.updatePermissions(pkg.getPackageName(), pkg); final boolean applyUserRestrictions @@ -19130,7 +19128,7 @@ public class PackageManagerService extends IPackageManager.Stub } // can downgrade to reader here if (writeSettings) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } return pkg; @@ -19204,7 +19202,7 @@ public class PackageManagerService extends IPackageManager.Stub } else { ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } return true; } @@ -20396,7 +20394,7 @@ public class PackageManagerService extends IPackageManager.Stub (parser1, userId1) -> { synchronized (mLock) { mSettings.readAllDomainVerificationsLPr(parser1, userId1); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } }); } catch (Exception e) { @@ -21747,6 +21745,8 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + DumpState dumpState = new DumpState(); boolean fullPreferred = false; boolean checkin = false; @@ -21942,7 +21942,7 @@ public class PackageManagerService extends IPackageManager.Stub dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS); } else if ("write".equals(cmd)) { synchronized (mLock) { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); pw.println("Settings written."); return; } @@ -22660,7 +22660,7 @@ public class PackageManagerService extends IPackageManager.Stub // Yay, everything is now upgraded ver.forceCurrent(); - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } for (PackageFreezer freezer : freezers) { @@ -22710,7 +22710,7 @@ public class PackageManagerService extends IPackageManager.Stub AttributeCache.instance().removePackage(ps.name); } - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -23623,6 +23623,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mLock) { mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); + mPermissionManager.onUserRemoved(userId); + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); @@ -23723,7 +23725,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); + mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25179,7 +25183,7 @@ public class PackageManagerService extends IPackageManager.Stub if (async) { scheduleWriteSettingsLocked(); } else { - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } } @@ -25226,7 +25230,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE; - mSettings.writeLPr(); + writeSettingsLPrTEMP(); } } @@ -25740,6 +25744,17 @@ public class PackageManagerService extends IPackageManager.Stub public List<String> getMimeGroup(String packageName, String mimeGroup) { return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup); } + + /** + * Temporary method that wraps mSettings.writeLPr() and calls + * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand. + * + * TODO(zhanghai): This should be removed once we finish migration of permission storage. + */ + private void writeSettingsLPrTEMP() { + mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mSettings.writeLPr(); + } } interface PackageSender { diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index de0e4b53adab..491b4fc515ce 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -34,7 +34,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; import android.content.pm.ResolveInfo; @@ -68,7 +67,6 @@ import com.android.server.EventLogTags; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.permission.PermissionsState; import dalvik.system.VMRuntime; @@ -968,20 +966,6 @@ public class PackageManagerServiceUtils { } /** - * Returns the {@link PermissionsState} for the given package. If the {@link PermissionsState} - * could not be found, {@code null} will be returned. - */ - public static PermissionsState getPermissionsState( - PackageManagerInternal packageManagerInternal, AndroidPackage pkg) { - final PackageSetting packageSetting = packageManagerInternal.getPackageSetting( - pkg.getPackageName()); - if (packageSetting == null) { - return null; - } - return packageSetting.getPermissionsState(); - } - - /** * Recursively create target directory */ public static void makeDirRecursive(File targetDir, int mode) throws ErrnoException { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index acb149b9ec3d..d545bd4c1531 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -955,93 +955,6 @@ public final class Settings { } } - /* - * Update the shared user setting when a package with a shared user id is removed. The gids - * associated with each permission of the deleted package are removed from the shared user' - * gid list only if its not in use by other permissions of packages in the shared user setting. - * - * @return the affected user id - */ - @UserIdInt - int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) { - if ((deletedPs == null) || (deletedPs.pkg == null)) { - Slog.i(PackageManagerService.TAG, - "Trying to update info for null package. Just ignoring"); - return UserHandle.USER_NULL; - } - - // No sharedUserId - if (deletedPs.sharedUser == null) { - return UserHandle.USER_NULL; - } - - SharedUserSetting sus = deletedPs.sharedUser; - - int affectedUserId = UserHandle.USER_NULL; - // Update permissions - for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { - BasePermission bp = mPermissions.getPermission(eachPerm); - if (bp == null) { - continue; - } - - // Check if another package in the shared user needs the permission. - boolean used = false; - for (PackageSetting pkg : sus.packages) { - if (pkg.pkg != null - && !pkg.pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) - && pkg.pkg.getRequestedPermissions().contains(eachPerm)) { - used = true; - break; - } - } - if (used) { - continue; - } - - PermissionsState permissionsState = sus.getPermissionsState(); - PackageSetting disabledPs = getDisabledSystemPkgLPr(deletedPs.pkg.getPackageName()); - - // If the package is shadowing is a disabled system package, - // do not drop permissions that the shadowed package requests. - if (disabledPs != null) { - boolean reqByDisabledSysPkg = false; - for (String permission : disabledPs.pkg.getRequestedPermissions()) { - if (permission.equals(eachPerm)) { - reqByDisabledSysPkg = true; - break; - } - } - if (reqByDisabledSysPkg) { - continue; - } - } - - // Try to revoke as an install permission which is for all users. - // The package is gone - no need to keep flags for applying policy. - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - - if (permissionsState.revokeInstallPermission(bp) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - affectedUserId = UserHandle.USER_ALL; - } - - // Try to revoke as an install permission which is per user. - if (permissionsState.revokeRuntimePermission(bp, userId) == - PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - if (affectedUserId == UserHandle.USER_NULL) { - affectedUserId = userId; - } else if (affectedUserId != userId) { - // Multiple users affected. - affectedUserId = UserHandle.USER_ALL; - } - } - } - - return affectedUserId; - } - int removePackageLPw(String name) { final PackageSetting p = mPackages.get(name); if (p != null) { @@ -5533,32 +5446,11 @@ public final class Settings { // Make sure we do not mHandler.removeMessages(userId); - for (SettingBase sb : mPackages.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - - for (SettingBase sb : mSharedUsers.values()) { - revokeRuntimePermissionsAndClearFlags(sb, userId); - } - mPermissionUpgradeNeeded.delete(userId); mVersions.delete(userId); mFingerprints.remove(userId); } - private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) { - PermissionsState permissionsState = sb.getPermissionsState(); - for (PermissionState permissionState - : permissionsState.getRuntimePermissionStates(userId)) { - BasePermission bp = mPermissions.getPermission(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - } - } - } - public void deleteUserRuntimePermissionsFile(int userId) { mPersistence.deleteForUser(UserHandle.of(userId)); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d137fd05f793..c1aebd33889c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -84,6 +84,7 @@ import android.os.storage.StorageManager; import android.security.GateKeeper; import android.service.gatekeeper.IGateKeeperService; import android.stats.devicepolicy.DevicePolicyEnums; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -141,7 +142,6 @@ import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; /** * Service for {@link UserManager}. @@ -159,6 +159,10 @@ public class UserManagerService extends IUserManager.Stub { private static final String LOG_TAG = "UserManagerService"; static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE + + // TODO(b/164159026): remove once owner_name issue on automotive is fixed + // Can be used to track getUsers() / userWithNameLU() behavior + public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE // Can be used for manual testing of id recycling private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE @@ -272,6 +276,25 @@ public class UserManagerService extends IUserManager.Stub { private DevicePolicyManagerInternal mDevicePolicyManagerInternal; /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either + * manually set, or it's {@code null}. + * + * <p>The reference is set just once, but it's {@code name} is updated when it's manually set. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfo; + + /** + * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to + * the localized value of {@code owner_name}. + * + * <p>The reference is set just once, but it's {@code name} is updated everytime the reference + * is used and the locale changed. + */ + @GuardedBy("mUsersLock") + private UserInfo mSystemUserInfoWithName; + + /** * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps. */ @VisibleForTesting @@ -444,11 +467,6 @@ public class UserManagerService extends IUserManager.Stub { } }; - // TODO(b/161915546): remove once userWithName() is fixed / removed - // Use to debug / dump when user 0 is allocated at userWithName() - public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE - public final AtomicInteger mUser0Allocations; - /** * Start an {@link IntentSender} when user is unlocked after disabling quiet mode. * @@ -638,7 +656,6 @@ public class UserManagerService extends IUserManager.Stub { LocalServices.addService(UserManagerInternal.class, mLocalService); mLockPatternUtils = new LockPatternUtils(mContext); mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING); - mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null; } void systemReady() { @@ -784,7 +801,7 @@ public class UserManagerService extends IUserManager.Stub { || (excludePreCreated && ui.preCreated)) { continue; } - users.add(userWithName(ui)); + users.add(userWithNameLU(ui)); } return users; } @@ -853,7 +870,7 @@ public class UserManagerService extends IUserManager.Stub { userInfo.name = null; userInfo.iconPath = null; } else { - userInfo = userWithName(userInfo); + userInfo = userWithNameLU(userInfo); } users.add(userInfo); } @@ -1310,26 +1327,57 @@ public class UserManagerService extends IUserManager.Stub { public UserInfo getUserInfo(@UserIdInt int userId) { checkManageOrCreateUsersPermission("query user"); synchronized (mUsersLock) { - return userWithName(getUserInfoLU(userId)); + return userWithNameLU(getUserInfoLU(userId)); } } /** * Returns a UserInfo object with the name filled in, for Owner, or the original * if the name is already set. + * + * <p>Note:</p> the Owner name is localized, so the current value must be checked every time + * this method is called. */ - private UserInfo userWithName(UserInfo orig) { - if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) { - if (DBG_ALLOCATION) { - final int number = mUser0Allocations.incrementAndGet(); - Slog.w(LOG_TAG, "System user instantiated at least " + number + " times"); - } - UserInfo withName = new UserInfo(orig); - withName.name = getOwnerName(); - return withName; - } else { - return orig; + private UserInfo userWithNameLU(UserInfo orig) { + // Only the system user uses the owner_name string. + if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig; + + if (mSystemUserInfo == null) { + mSystemUserInfo = orig; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString()); + } } + + if (mSystemUserInfo.name != null) { + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString()); + } + return mSystemUserInfo; + } + + final String ownerName = getOwnerName(); + + if (mSystemUserInfoWithName == null) { + mSystemUserInfoWithName = new UserInfo(orig); + mSystemUserInfoWithName.name = ownerName; + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: " + + mSystemUserInfoWithName.toFullString()); + } + } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) { + if (DBG_CACHED_USERINFOS) { + Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from " + + mSystemUserInfoWithName.name + " to " + ownerName); + } + mSystemUserInfoWithName.name = ownerName; + } + + if (DBG_CACHED_USERINFOS) { + Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:" + + mSystemUserInfoWithName.toFullString()); + } + return mSystemUserInfoWithName; } /** Returns whether the given user type is one of the FULL user types. */ @@ -1482,7 +1530,7 @@ public class UserManagerService extends IUserManager.Stub { } final int userId = UserHandle.getUserId(Binder.getCallingUid()); synchronized (mUsersLock) { - UserInfo userInfo = userWithName(getUserInfoLU(userId)); + UserInfo userInfo = userWithNameLU(getUserInfoLU(userId)); return userInfo == null ? "" : userInfo.name; } } @@ -1597,6 +1645,13 @@ public class UserManagerService extends IUserManager.Stub { Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId); return null; } + + if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null + && userData.info != mSystemUserInfo) { + Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info + + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")"); + } + return userData != null ? userData.info : null; } @@ -4855,8 +4910,15 @@ public class UserManagerService extends IUserManager.Stub { pw.println(" Is headless-system mode: " + UserManager.isHeadlessSystemUserMode()); pw.println(" User version: " + mUserVersion); pw.println(" Owner name: " + getOwnerName()); - if (DBG_ALLOCATION) { - pw.println(" System user allocations: " + mUser0Allocations.get()); + if (mSystemUserInfo == null) { + pw.println(" (mSystemUserInfo not set)"); + } else { + pw.println(" System user: " + mSystemUserInfo.toFullString()); + } + if (mSystemUserInfoWithName == null) { + pw.println(" (mSystemUserInfoWithName not set)"); + } else { + pw.println(" System user (with name): " + mSystemUserInfoWithName.toFullString()); } // Dump UserTypes diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index cfa0449aaf33..962638b4f63c 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -38,7 +38,6 @@ import android.util.Slog; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.PackageSetting; import com.android.server.pm.PackageSettingBase; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; @@ -420,8 +419,7 @@ public final class BasePermission { } public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg, - PackageSetting pkgSetting) { - final PermissionsState permsState = pkgSetting.getPermissionsState(); + PermissionsState permsState) { int index = pkg.getRequestedPermissions().indexOf(name); if (!permsState.hasRequestedPermission(name) && index == -1) { throw new SecurityException("Package " + pkg.getPackageName() diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 1be74154b53a..f5dd918a18f3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -162,6 +162,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -227,6 +228,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; + /** Maps from App ID to PermissionsState */ + private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>(); + /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; @@ -671,11 +675,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (pkg == null) { return 0; } - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { - return 0; - } synchronized (mLock) { if (mSettings.getPermissionLocked(permName) == null) { return 0; @@ -684,7 +683,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } - PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return 0; + } return permissionsState.getPermissionFlags(permName, userId); } @@ -771,9 +774,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -789,7 +790,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return; + } + final boolean hadState = permissionsState.getRuntimePermissionState(permName, userId) != null; if (!hadState) { @@ -864,12 +870,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean[] changed = new boolean[1]; mPackageManagerInt.forEachPackage(pkg -> { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - final PermissionsState permissionsState = ps.getPermissionsState(); changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions( userId, effectiveFlagMask, effectiveFlagValues); mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); @@ -923,12 +928,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int uid = UserHandle.getUid(userId, pkg.getUid()); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return PackageManager.PERMISSION_DENIED; } - final PermissionsState permissionsState = ps.getPermissionsState(); if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { return PackageManager.PERMISSION_GRANTED; @@ -1139,9 +1143,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); return null; } @@ -1451,7 +1455,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1464,8 +1474,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { Log.e(TAG, "Cannot grant system fixed permission " @@ -1599,9 +1607,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { "revokeRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - packageName); - if (pkg == null || ps == null) { + if (pkg == null) { Log.e(TAG, "Unknown package: " + packageName); return; } @@ -1613,7 +1619,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, ps); + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + return; + } + + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1624,8 +1636,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final PermissionsState permissionsState = ps.getPermissionsState(); - final int flags = permissionsState.getPermissionFlags(permName, userId); // Only the system may revoke SYSTEM_FIXED permissions. if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 @@ -2456,14 +2466,36 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + private void onUserRemoved(@UserIdInt int userId) { + synchronized (mLock) { + final int appIdStatesSize = mAppIdStates.size(); + for (int i = 0; i < appIdStatesSize; i++) { + PermissionsState permissionsState = mAppIdStates.valueAt(i); + for (PermissionState permissionState + : permissionsState.getRuntimePermissionStates(userId)) { + BasePermission bp = mSettings.getPermission(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + } + } + } + } + } + @NonNull private Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) { final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); if (ps == null) { - return null; + return Collections.emptySet(); + } + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return Collections.emptySet(); } - final PermissionsState permissionsState = ps.getPermissionsState(); if (!ps.getInstantApp(userId)) { return permissionsState.getPermissions(userId); } else { @@ -2503,7 +2535,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return null; } - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getPermissionsState(ps); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName); + return null; + } return permissionsState.computeGids(userId); } @@ -2541,8 +2577,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return; } - - final PermissionsState permissionsState = ps.getPermissionsState(); + final PermissionsState permissionsState = getOrCreatePermissionsState(ps); final int[] userIds = getAllUserIds(); @@ -2614,8 +2649,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { // changed runtime permissions here are promotion of an install to // runtime and revocation of a runtime from a shared user. synchronized (mLock) { - updatedUserIds = revokeUnusedSharedUserPermissionsLocked(ps.getSharedUser(), - userIds); + updatedUserIds = revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), permissionsState, userIds); if (!ArrayUtils.isEmpty(updatedUserIds)) { runtimePermissionsRevoked = true; } @@ -3091,6 +3126,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds); } + // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important + // for shared users. // Persist the runtime permissions state for users with changes. If permissions // were revoked because no app in the shared user declares them we have to // write synchronously to avoid losing runtime permissions state. @@ -3554,37 +3591,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { final PackageSetting disabledPs = mPackageManagerInt .getDisabledSystemPackage(pkg.getPackageName()); final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg; - if (disabledPs != null - && disabledPs.getPermissionsState().hasInstallPermission(perm)) { - // If the original was granted this permission, we take - // that grant decision as read and propagate it to the - // update. - if ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm))) { - allowed = true; - } - } else { - // The system apk may have been updated with an older - // version of the one on the data partition, but which - // granted a new system permission that it didn't have - // before. In this case we do want to allow the app to - // now get the new permission if the ancestral apk is - // privileged to get it. - if (disabledPs != null && disabledPkg != null - && isPackageRequestingPermission(disabledPkg, perm) - && ((privilegedPermission && disabledPs.isPrivileged()) - || (oemPermission && disabledPs.isOem() - && canGrantOemPermission(disabledPs, perm)))) { - allowed = true; - } + if (disabledPkg != null && isPackageRequestingPermission(disabledPkg, perm) + && ((privilegedPermission && disabledPkg.isPrivileged()) + || (oemPermission && canGrantOemPermission(disabledPkg, + perm)))) { + allowed = true; } } else { - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); allowed = (privilegedPermission && pkg.isPrivileged()) - || (oemPermission && pkg.isOem() - && canGrantOemPermission(ps, perm)); + || (oemPermission && canGrantOemPermission(pkg, perm)); } // In any case, don't grant a privileged permission to privileged vendor apps, if // the permission's protectionLevel does not have the extra 'vendorPrivileged' @@ -3735,16 +3750,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } - private static boolean canGrantOemPermission(PackageSetting ps, String permission) { - if (!ps.isOem()) { + private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) { + if (!pkg.isOem()) { return false; } // all oem permissions must explicitly be granted or denied final Boolean granted = - SystemConfig.getInstance().getOemPermissions(ps.name).get(permission); + SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission); if (granted == null) { throw new IllegalStateException("OEM permission" + permission + " requested by package " - + ps.name + " must be explicitly declared granted or not"); + + pkg.getPackageName() + " must be explicitly declared granted or not"); } return Boolean.TRUE == granted; } @@ -3757,12 +3772,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Legacy apps have the permission and get user consent on launch. - final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return false; } - final PermissionsState permissionsState = ps.getPermissionsState(); return permissionsState.isPermissionReviewRequired(userId); } @@ -3787,14 +3801,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { - PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting( - pkg.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); - final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3838,9 +3850,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg, @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid, @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { - final PermissionsState permissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg); + final PermissionsState permissionsState = getPermissionsState(pkg); if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); return; } @@ -3958,9 +3970,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < oldGrantedCount; j++) { final String permission = oldPermsForUser.valueAt(j); // Sometimes we create a new permission state instance during update. - final PermissionsState newPermissionsState = - PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, - pkg); + final PermissionsState newPermissionsState = getPermissionsState(pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + continue; + } if (!newPermissionsState.hasPermission(permission, userId)) { callback.onPermissionRevoked(pkg.getUid(), userId, null); break; @@ -3970,12 +3984,100 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + @UserIdInt + private int revokeSharedUserPermissionsForDeletedPackage(@NonNull PackageSetting deletedPs, + @UserIdInt int userId) { + if ((deletedPs == null) || (deletedPs.pkg == null)) { + Slog.i(TAG, "Trying to update info for null package. Just ignoring"); + return UserHandle.USER_NULL; + } + + SharedUserSetting sus = deletedPs.getSharedUser(); + + // No sharedUserId + if (sus == null) { + return UserHandle.USER_NULL; + } + + int affectedUserId = UserHandle.USER_NULL; + // Update permissions + for (String eachPerm : deletedPs.pkg.getRequestedPermissions()) { + BasePermission bp = mSettings.getPermission(eachPerm); + if (bp == null) { + continue; + } + + // Check if another package in the shared user needs the permission. + boolean used = false; + final List<AndroidPackage> pkgs = sus.getPackages(); + if (pkgs != null) { + for (AndroidPackage pkg : pkgs) { + if (pkg != null + && !pkg.getPackageName().equals(deletedPs.pkg.getPackageName()) + && pkg.getRequestedPermissions().contains(eachPerm)) { + used = true; + break; + } + } + } + if (used) { + continue; + } + + PermissionsState permissionsState = getPermissionsState(deletedPs.pkg); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()); + continue; + } + + PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage( + deletedPs.pkg.getPackageName()); + + // If the package is shadowing is a disabled system package, + // do not drop permissions that the shadowed package requests. + if (disabledPs != null) { + boolean reqByDisabledSysPkg = false; + for (String permission : disabledPs.pkg.getRequestedPermissions()) { + if (permission.equals(eachPerm)) { + reqByDisabledSysPkg = true; + break; + } + } + if (reqByDisabledSysPkg) { + continue; + } + } + + // Try to revoke as an install permission which is for all users. + // The package is gone - no need to keep flags for applying policy. + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); + + if (permissionsState.revokeInstallPermission(bp) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + affectedUserId = UserHandle.USER_ALL; + } + + // Try to revoke as a runtime permission which is per user. + if (permissionsState.revokeRuntimePermission(bp, userId) + == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + if (affectedUserId == UserHandle.USER_NULL) { + affectedUserId = userId; + } else if (affectedUserId != userId) { + // Multiple users affected. + affectedUserId = UserHandle.USER_ALL; + } + } + } + + return affectedUserId; + } + @GuardedBy("mLock") private int[] revokeUnusedSharedUserPermissionsLocked( - SharedUserSetting suSetting, int[] allUserIds) { + List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) { // Collect all used permissions in the UID final ArraySet<String> usedPermissions = new ArraySet<>(); - final List<AndroidPackage> pkgList = suSetting.getPackages(); if (pkgList == null || pkgList.size() == 0) { return EmptyArray.INT; } @@ -3993,7 +4095,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - PermissionsState permissionsState = suSetting.getPermissionsState(); // Prune install permissions List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); final int installPermCount = installPermStates.size(); @@ -4279,12 +4380,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } else { mPackageManagerInt.forEachPackage(p -> { - PackageSetting ps = mPackageManagerInt.getPackageSetting( - p.getPackageName()); - if (ps == null) { + final PermissionsState permissionsState = getPermissionsState(p); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + p.getPackageName()); return; } - PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.getInstallPermissionState(bp.getName()) != null) { permissionsState.revokeInstallPermission(bp); permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, @@ -4695,6 +4795,67 @@ public class PermissionManagerService extends IPermissionManager.Stub { return mBackgroundPermissions; } + @Nullable + private PermissionsState getPermissionsState(@NonNull PackageSetting ps) { + return getPermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) { + return getPermissionsState(pkg.getUid()); + } + + @Nullable + private PermissionsState getPermissionsState(int appId) { + synchronized (mLock) { + return mAppIdStates.get(appId); + } + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) { + return getOrCreatePermissionsState(ps.getAppId()); + } + + @Nullable + private PermissionsState getOrCreatePermissionsState(int appId) { + synchronized (mLock) { + PermissionsState state = mAppIdStates.get(appId); + if (state == null) { + state = new PermissionsState(); + mAppIdStates.put(appId, state); + } + return state; + } + } + + private void removePermissionsState(int appId) { + synchronized (mLock) { + mAppIdStates.remove(appId); + } + } + + private void readPermissionsStateFromPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState())); + } + }); + } + + private void writePermissionsStateToPackageSettings() { + mPackageManagerInt.forEachPackageSetting(ps -> { + synchronized (mLock) { + final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId()); + if (permissionsState == null) { + Slog.e(TAG, "Missing permissions state for " + ps.name); + return; + } + ps.getPermissionsState().copyFrom(permissionsState); + } + }); + } + private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal { @Override public void systemReady() { @@ -4726,6 +4887,29 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void removeAllPermissions(AndroidPackage pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } + @Override + public void readPermissionsStateFromPackageSettingsTEMP() { + PermissionManagerService.this.readPermissionsStateFromPackageSettings(); + } + @Override + public void writePermissionsStateToPackageSettingsTEMP() { + PermissionManagerService.this.writePermissionsStateToPackageSettings(); + } + @Override + public void onUserRemoved(@UserIdInt int userId) { + PermissionManagerService.this.onUserRemoved(userId); + } + @Override + public void removePermissionsStateTEMP(int appId) { + PermissionManagerService.this.removePermissionsState(appId); + } + @Override + @UserIdInt + public int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId) { + return PermissionManagerService.this.revokeSharedUserPermissionsForDeletedPackage( + deletedPs, userId); + } @NonNull @Override public Set<String> getGrantedPermissions(@NonNull String packageName, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index cfa371ddbad3..f319bf495e8b 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -24,6 +24,7 @@ import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; +import com.android.server.pm.PackageSetting; import com.android.server.pm.parsing.pkg.AndroidPackage; import java.util.ArrayList; @@ -265,6 +266,52 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** + * Read {@code PermissionsState} from package settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code PackageSetting} which is a implementation detail that permission should not know. + * Instead, it should retrieve the legacy state via a defined API. + */ + public abstract void readPermissionsStateFromPackageSettingsTEMP(); + + /** + * Write {@code PermissionsState} from to settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + public abstract void writePermissionsStateToPackageSettingsTEMP(); + + /** + * Notify that a user has been removed and its permission state should be removed as well. + */ + public abstract void onUserRemoved(@UserIdInt int userId); + + /** + * Remove the {@code PermissionsState} associated with an app ID, called the same time as the + * removal of a {@code PackageSetitng}. + * + * TODO(zhanghai): This is a temporary method before we figure out a way to get notified of app + * ID removal via API. + */ + public abstract void removePermissionsStateTEMP(int appId); + + /** + * Update the shared user setting when a package with a shared user id is removed. The gids + * associated with each permission of the deleted package are removed from the shared user' + * gid list only if its not in use by other permissions of packages in the shared user setting. + * + * TODO(zhanghai): We should not need this when permission no longer sees an incomplete package + * state where the updated system package is uninstalled but the disabled system package is yet + * to be installed. Then we should handle this in restorePermissionState(). + * + * @return the affected user id, may be a real user ID, USER_ALL, or USER_NULL when none. + */ + @UserIdInt + public abstract int revokeSharedUserPermissionsForDeletedPackageTEMP( + @NonNull PackageSetting deletedPs, @UserIdInt int userId); + + /** * Get all the permissions granted to a package. */ @NonNull diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 64fa6ca590d2..9316c4657826 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -4664,7 +4664,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // case where this is the top activity in a pinned stack. final boolean isTop = this == stack.getTopNonFinishingActivity(); final boolean isTopNotPinnedStack = stack.isAttached() - && stack.getDisplayArea().isTopNotPinnedStack(stack); + && stack.getDisplayArea().isTopNotFinishNotPinnedStack(stack); final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this, visibleIgnoringKeyguard, isTop && isTopNotPinnedStack); @@ -4806,6 +4806,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A Slog.v(TAG_VISIBILITY, "Start visible activity, " + this); } setState(STARTED, "makeActiveIfNeeded"); + + // Update process info while making an activity from invisible to visible, to make + // sure the process state is updated to foreground. + if (app != null) { + app.updateProcessInfo(false /* updateServiceConnectionActivities */, + true /* activityChange */, true /* updateOomAdj */, + true /* addPendingTopUid */); + } + try { mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken, StartActivityItem.obtain()); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 2e879810c085..0215ead7e5de 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3982,9 +3982,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayFrames.onDisplayInfoUpdated(mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation)); - // TODO: Not sure if we really need to set the rotation here since we are updating from - // the display info above... - mDisplayFrames.mRotation = getRotation(); mDisplayPolicy.beginLayoutLw(mDisplayFrames, getConfiguration().uiMode); int seq = mLayoutSeq + 1; diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2e03cb80b189..40fc25b41d9f 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -246,6 +246,9 @@ public class DisplayPolicy { | View.STATUS_BAR_TRANSPARENT | View.NAVIGATION_BAR_TRANSPARENT; + private static final int[] SHOW_TYPES_FOR_SWIPE = {ITYPE_NAVIGATION_BAR, ITYPE_STATUS_BAR}; + private static final int[] SHOW_TYPES_FOR_PANIC = {ITYPE_NAVIGATION_BAR}; + private final WindowManagerService mService; private final Context mContext; private final Context mUiContext; @@ -3353,8 +3356,15 @@ public class DisplayPolicy { return; } + final InsetsState requestedState = controlTarget.getRequestedInsetsState(); + final @InsetsType int restorePositionTypes = + (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + ? Type.navigationBars() : 0) + | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + ? Type.statusBars() : 0); + if (swipeTarget == mNavigationBar - && !getInsetsPolicy().isHidden(ITYPE_NAVIGATION_BAR)) { + && (restorePositionTypes & Type.navigationBars()) != 0) { // Don't show status bar when swiping on already visible navigation bar. // But restore the position of navigation bar if it has been moved by the control // target. @@ -3362,14 +3372,13 @@ public class DisplayPolicy { return; } - int insetsTypesToShow = Type.systemBars(); - if (controlTarget.canShowTransient()) { - insetsTypesToShow &= ~mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); - } - if (insetsTypesToShow != 0) { - controlTarget.showInsets(insetsTypesToShow, false); + // Show transient bars if they are hidden; restore position if they are visible. + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE); + controlTarget.showInsets(restorePositionTypes, false); + } else { + // Restore visibilities and positions of system bars. + controlTarget.showInsets(Type.statusBars() | Type.navigationBars(), false); } } else { boolean sb = mStatusBarController.checkShowTransientBarLw(); @@ -3770,8 +3779,7 @@ public class DisplayPolicy { // we're no longer on the Keyguard and the screen is ready. We can now request the bars. mPendingPanicGestureUptime = 0; if (!isNavBarEmpty(vis)) { - mDisplayContent.getInsetsPolicy().showTransient(IntArray.wrap( - new int[] {ITYPE_NAVIGATION_BAR})); + mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_PANIC); } } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 3ffc26a7a8ad..5e7ed3f80e43 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.inputmethodservice.InputMethodService; +import android.view.InsetsState; import android.view.WindowInsets.Type.InsetsType; /** @@ -38,6 +39,13 @@ interface InsetsControlTarget { } /** + * @return The requested {@link InsetsState} of this target. + */ + default InsetsState getRequestedInsetsState() { + return InsetsState.EMPTY; + } + + /** * Instructs the control target to show inset sources. * * @param types to specify which types of insets source window should be shown. diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index b7287e718bd6..18a25033b1e6 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -42,7 +42,6 @@ import android.view.InsetsState.InternalInsetsType; import android.view.SurfaceControl; import android.view.SyncRtSurfaceTransactionApplier; import android.view.ViewRootImpl; -import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimationControlListener; @@ -153,15 +152,13 @@ class InsetsPolicy { return provider != null && provider.hasWindow() && !provider.getSource().isVisible(); } - @InsetsType int showTransient(IntArray types) { - @InsetsType int showingTransientTypes = 0; + void showTransient(@InternalInsetsType int[] types) { boolean changed = false; - for (int i = types.size() - 1; i >= 0; i--) { - final int type = types.get(i); + for (int i = types.length - 1; i >= 0; i--) { + final @InternalInsetsType int type = types[i]; if (!isHidden(type)) { continue; } - showingTransientTypes |= InsetsState.toPublicType(type); if (mShowingTransientTypes.indexOf(type) != -1) { continue; } @@ -189,7 +186,6 @@ class InsetsPolicy { } }); } - return showingTransientTypes; } void hideTransient() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 0529abf89f6e..50c269e23c53 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3820,7 +3820,10 @@ class Task extends WindowContainer<WindowContainer> { @Override boolean fillsParent() { - return matchParentBounds(); + // From the perspective of policy, we still want to report that this task fills parent + // in fullscreen windowing mode even it doesn't match parent bounds because there will be + // letterbox around its real content. + return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds(); } @Override diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index 32511108836e..6550167683a0 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -1489,9 +1489,13 @@ final class TaskDisplayArea extends DisplayArea<Task> { return stack == getTopStack(); } - boolean isTopNotPinnedStack(Task stack) { + boolean isTopNotFinishNotPinnedStack(Task stack) { for (int i = getStackCount() - 1; i >= 0; --i) { final Task current = getStackAt(i); + final ActivityRecord topAct = current.getTopNonFinishingActivity(); + if (topAct == null) { + continue; + } if (!current.inPinnedWindowingMode()) { return current == stack; } diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java index 61e9e5082d17..5e81e4008680 100644 --- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java +++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java @@ -24,8 +24,6 @@ import android.annotation.NonNull; import android.util.ArrayMap; import android.util.Slog; -import com.android.server.wm.WindowManagerService.H; - import java.io.PrintWriter; /** @@ -102,7 +100,13 @@ class UnknownAppVisibilityController { if (DEBUG_UNKNOWN_APP_VISIBILITY) { Slog.d(TAG, "App launched activity=" + activity); } - mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + // If the activity was started with launchTaskBehind, the lifecycle will goes to paused + // directly, and the process will pass onResume, so we don't need to waiting resume for it. + if (!activity.mLaunchTaskBehind) { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RESUME); + } else { + mUnknownApps.put(activity, UNKNOWN_STATE_WAITING_RELAYOUT); + } } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cd222a97f4d9..32251976eba1 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -929,7 +929,7 @@ public class WindowManagerService extends IWindowManager.Stub private void setShadowRenderer() { mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(), - DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 0) != 0; + DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0; } PowerManager mPowerManager; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 49e623d8dd11..0e455d2a5aa6 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -728,7 +728,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * @return The insets state as requested by the client, i.e. the dispatched insets state * for which the visibilities are overridden with what the client requested. */ - InsetsState getRequestedInsetsState() { + @Override + public InsetsState getRequestedInsetsState() { return mRequestedInsetsState; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java index c29c510b35b5..42ba842f8434 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MockWindowMagnificationConnection.java @@ -17,23 +17,33 @@ package com.android.server.accessibility.magnification; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.view.Display; import android.view.accessibility.IWindowMagnificationConnection; import android.view.accessibility.IWindowMagnificationConnectionCallback; +/** + * Mocks the basic logic of window magnification in System UI. We assume the screen size is + * unlimited, so source bounds is always on the center of the mirror window bounds. + */ class MockWindowMagnificationConnection { + public static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY; private final IWindowMagnificationConnection mConnection; private final Binder mBinder; private IBinder.DeathRecipient mDeathRecipient; private IWindowMagnificationConnectionCallback mIMirrorWindowCallback; + private Rect mMirrorWindowFrame = new Rect(0, 0, 500, 500); MockWindowMagnificationConnection() throws RemoteException { mConnection = mock(IWindowMagnificationConnection.class); @@ -50,6 +60,30 @@ class MockWindowMagnificationConnection { return null; }).when(mBinder).linkToDeath( any(IBinder.DeathRecipient.class), eq(0)); + stubConnection(); + } + + private void stubConnection() throws RemoteException { + doAnswer((invocation) -> { + final int displayId = invocation.getArgument(0); + if (displayId != TEST_DISPLAY) { + throw new IllegalArgumentException("only support default display :" + displayId); + } + computeMirrorWindowFrame(invocation.getArgument(1), invocation.getArgument(2)); + + mIMirrorWindowCallback.onWindowMagnifierBoundsChanged(TEST_DISPLAY, + mMirrorWindowFrame); + return null; + }).when(mConnection).enableWindowMagnification(anyInt(), + anyFloat(), anyFloat(), anyFloat()); + } + + private void computeMirrorWindowFrame(float centerX, float centerY) { + final float offsetX = Float.isNaN(centerX) ? 0 + : centerX - mMirrorWindowFrame.exactCenterX(); + final float offsetY = Float.isNaN(centerY) ? 0 + : centerY - mMirrorWindowFrame.exactCenterY(); + mMirrorWindowFrame.offset((int) offsetX, (int) offsetY); } IWindowMagnificationConnection getConnection() { @@ -60,12 +94,16 @@ class MockWindowMagnificationConnection { return mBinder; } - public IBinder.DeathRecipient getDeathRecipient() { + IBinder.DeathRecipient getDeathRecipient() { return mDeathRecipient; } - public IWindowMagnificationConnectionCallback getConnectionCallback() { + IWindowMagnificationConnectionCallback getConnectionCallback() { return mIMirrorWindowCallback; } + + public Rect getMirrorWindowFrame() { + return new Rect(mMirrorWindowFrame); + } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java index 2b1bdc59d9c8..ed8dc4e470de 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownTest.java @@ -87,15 +87,15 @@ public class TwoFingersDownTest { secondPointerCoords.x = DEFAULT_X + 10; secondPointerCoords.y = DEFAULT_Y + 10; - final MotionEvent pointerDownEvent = TouchEventGenerator.pointerDownEvent( + final MotionEvent twoPointersDownEvent = TouchEventGenerator.twoPointersDownEvent( Display.DEFAULT_DISPLAY, defPointerCoords, secondPointerCoords); mGesturesObserver.onMotionEvent(downEvent, downEvent, 0); - mGesturesObserver.onMotionEvent(pointerDownEvent, pointerDownEvent, 0); + mGesturesObserver.onMotionEvent(twoPointersDownEvent, twoPointersDownEvent, 0); verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted( - MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, pointerDownEvent, - pointerDownEvent, 0); + MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, twoPointersDownEvent, + twoPointersDownEvent, 0); } @Test diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java index e580340a29f7..bec9f26672f4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java @@ -16,14 +16,9 @@ package com.android.server.accessibility.magnification; -import static android.view.MotionEvent.ACTION_POINTER_DOWN; - import static com.android.server.testutils.TestUtils.strictMock; import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyFloat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.content.Context; @@ -63,11 +58,9 @@ public class WindowMagnificationGestureHandlerTest { public static final int LAST_STATE = STATE_SHOW_MAGNIFIER_TRIPLE_TAP; // Co-prime x and y, to potentially catch x-y-swapped errors - public static final float DEFAULT_X = 301; - public static final float DEFAULT_Y = 299; - //Assume first pointer position (DEFAULT_X,DEFAULT_Y) is in the window. - public static Rect DEFAULT_WINDOW_FRAME = new Rect(0, 0, 500, 500); - private static final int DISPLAY_0 = 0; + public static final float DEFAULT_TAP_X = 301; + public static final float DEFAULT_TAP_Y = 299; + private static final int DISPLAY_0 = MockWindowMagnificationConnection.TEST_DISPLAY; private Context mContext; private WindowMagnificationManager mWindowMagnificationManager; @@ -83,14 +76,6 @@ public class WindowMagnificationGestureHandlerTest { mContext, mWindowMagnificationManager, mock(ScaleChangedListener.class), /** detectTripleTap= */true, /** detectShortcutTrigger= */true, DISPLAY_0); mWindowMagnificationManager.setConnection(mMockConnection.getConnection()); - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - doAnswer((invocation) -> { - mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(DISPLAY_0, - DEFAULT_WINDOW_FRAME); - return null; - }).when(mMockConnection.getConnection()).enableWindowMagnification(eq(DISPLAY_0), - anyFloat(), anyFloat(), anyFloat()); mWindowMagnificationGestureHandler.setNext(strictMock(EventStreamTransformation.class)); } @@ -208,10 +193,11 @@ public class WindowMagnificationGestureHandlerTest { break; case STATE_TWO_FINGERS_DOWN: { goFromStateIdleTo(STATE_SHOW_MAGNIFIER); - send(downEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(downEvent(frame.centerX(), frame.centerY())); //Second finger is outside the window. - send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_WINDOW_FRAME.right + 10, - DEFAULT_WINDOW_FRAME.bottom + 10)); + send(twoPointerDownEvent(new float[]{frame.centerX(), frame.centerX() + 10}, + new float[]{frame.centerY(), frame.centerY() + 10})); } break; case STATE_SHOW_MAGNIFIER_TRIPLE_TAP: { @@ -243,7 +229,8 @@ public class WindowMagnificationGestureHandlerTest { } break; case STATE_TWO_FINGERS_DOWN: { - send(upEvent()); + final Rect frame = mMockConnection.getMirrorWindowFrame(); + send(upEvent(frame.centerX(), frame.centerY())); returnToNormalFrom(STATE_SHOW_MAGNIFIER); } break; @@ -286,12 +273,8 @@ public class WindowMagnificationGestureHandlerTest { } } - private MotionEvent downEvent() { - return TouchEventGenerator.downEvent(DISPLAY_0, DEFAULT_X, DEFAULT_Y); - } - - private MotionEvent upEvent() { - return upEvent(DEFAULT_X, DEFAULT_Y); + private MotionEvent downEvent(float x, float y) { + return TouchEventGenerator.downEvent(DISPLAY_0, x, y); } private MotionEvent upEvent(float x, float y) { @@ -299,18 +282,18 @@ public class WindowMagnificationGestureHandlerTest { } private void tap() { - send(downEvent()); - send(upEvent()); + send(downEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); + send(upEvent(DEFAULT_TAP_X, DEFAULT_TAP_Y)); } - private MotionEvent pointerEvent(int action, float x, float y) { + private MotionEvent twoPointerDownEvent(float[] x, float[] y) { final MotionEvent.PointerCoords defPointerCoords = new MotionEvent.PointerCoords(); - defPointerCoords.x = DEFAULT_X; - defPointerCoords.y = DEFAULT_Y; + defPointerCoords.x = x[0]; + defPointerCoords.y = y[0]; final MotionEvent.PointerCoords pointerCoords = new MotionEvent.PointerCoords(); - pointerCoords.x = x; - pointerCoords.y = y; - return TouchEventGenerator.pointerDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); + pointerCoords.x = x[1]; + pointerCoords.y = y[1]; + return TouchEventGenerator.twoPointersDownEvent(DISPLAY_0, defPointerCoords, pointerCoords); } private String stateDump() { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java index 7cbf3ee46594..a05881f78892 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/utils/TouchEventGenerator.java @@ -43,9 +43,9 @@ public class TouchEventGenerator { return generateSingleTouchEvent(displayId, ACTION_UP, x, y); } - public static MotionEvent pointerDownEvent(int displayId, PointerCoords defPointerCoords, + public static MotionEvent twoPointersDownEvent(int displayId, PointerCoords defPointerCoords, PointerCoords pointerCoords) { - return generatePointerEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, + return generateTwoPointersEvent(displayId, ACTION_POINTER_DOWN, defPointerCoords, pointerCoords); } @@ -59,7 +59,7 @@ public class TouchEventGenerator { return ev; } - private static MotionEvent generatePointerEvent(int displayId, int action, + private static MotionEvent generateTwoPointersEvent(int displayId, int action, PointerCoords defPointerCoords, PointerCoords pointerCoords) { final long downTime = SystemClock.uptimeMillis(); MotionEvent.PointerProperties defPointerProperties = new MotionEvent.PointerProperties(); diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt index 946f27e09fdb..d36dcce800eb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageInfoFlagBehaviorTest.kt @@ -20,7 +20,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageParser -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.appInfo import com.android.server.pm.parsing.AndroidPackageInfoFlagBehaviorTest.Companion.Param.Companion.pkgInfo import com.android.server.pm.parsing.pkg.AndroidPackage @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized * This test has to be updated manually whenever the info generation behavior changes, since * there's no single place where flag -> field is defined besides this test. */ -@Presubmit +@Postsubmit @RunWith(Parameterized::class) class AndroidPackageInfoFlagBehaviorTest : AndroidPackageParsingTestBase() { diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt index f96ebda67602..574921cdbd05 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt @@ -17,26 +17,20 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager -import android.platform.test.annotations.Presubmit +import android.platform.test.annotations.Postsubmit import androidx.test.filters.LargeTest import com.google.common.truth.Expect - import org.junit.Rule import org.junit.Test -import org.junit.rules.Timeout -import java.util.concurrent.TimeUnit /** * Collects APKs from the device and verifies that the new parsing behavior outputs * the same exposed Info object as the old parsing logic. */ -@Presubmit +@Postsubmit class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() { @get:Rule - val timeout = Timeout(4, TimeUnit.MINUTES) - - @get:Rule val expect = Expect.create() @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 555906d4c910..608305c33168 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import android.platform.test.annotations.Presubmit; -import android.util.IntArray; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.test.InsetsModeSession; @@ -242,8 +241,7 @@ public class InsetsPolicyTest extends WindowTestsBase { }).when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -271,8 +269,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -301,8 +298,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(mAppWindow); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -340,8 +336,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); policy.updateBarControlTarget(app); - policy.showTransient( - IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR})); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); final InsetsSourceControl[] controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(app); policy.updateBarControlTarget(app2); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 085230d35c6a..59f8cc8c3412 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -346,8 +346,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertTrue(rotatedState.getSource(ITYPE_STATUS_BAR).isVisible()); provider.getSource().setVisible(false); - mDisplayContent.getInsetsPolicy().showTransient( - IntArray.wrap(new int[] { ITYPE_STATUS_BAR })); + mDisplayContent.getInsetsPolicy().showTransient(new int[] { ITYPE_STATUS_BAR }); assertTrue(mDisplayContent.getInsetsPolicy().isTransient(ITYPE_STATUS_BAR)); assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index fc54e1de888f..0fe6510b0fce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -50,6 +50,7 @@ import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -245,17 +246,17 @@ public class TaskRecordTests extends WindowTestsBase { final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); final DisplayContent display = new TestDisplayContent.Builder(mAtm, fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); - assertTrue(mRootWindowContainer.getDisplayContent(display.mDisplayId) != null); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); // Fix the display orientation to landscape which is the natural rotation (0) for the test // display. final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); - Task stack = new StackBuilder(mRootWindowContainer) + final Task stack = new StackBuilder(mRootWindowContainer) .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); - Task task = stack.getBottomMostTask(); - ActivityRecord root = task.getTopNonFinishingActivity(); + final Task task = stack.getBottomMostTask(); + final ActivityRecord root = task.getTopNonFinishingActivity(); assertEquals(fullScreenBounds, task.getBounds()); @@ -267,7 +268,7 @@ public class TaskRecordTests extends WindowTestsBase { assertEquals(fullScreenBounds.height(), task.getBounds().height()); // Top activity gets used - ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); + final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setStack(stack).build(); assertEquals(top, task.getTopNonFinishingActivity()); top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); @@ -304,6 +305,33 @@ public class TaskRecordTests extends WindowTestsBase { } @Test + public void testReportsOrientationRequestInLetterboxForOrientation() { + final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); + final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); + final DisplayContent display = new TestDisplayContent.Builder(mAtm, + fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); + assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); + // Fix the display orientation to landscape which is the natural rotation (0) for the test + // display. + final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); + dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); + dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); + + final Task stack = new StackBuilder(mRootWindowContainer) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); + final Task task = stack.getBottomMostTask(); + ActivityRecord root = task.getTopNonFinishingActivity(); + + assertEquals(fullScreenBounds, task.getBounds()); + + // Setting app to fixed portrait fits within parent + root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); + assertThat(task.getBounds().width()).isLessThan(task.getBounds().height()); + + assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); + } + + @Test public void testIgnoresForcedOrientationWhenParentHandles() { final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); DisplayContent display = new TestDisplayContent.Builder( diff --git a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java index 6ed762283524..78dfd407ff4e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/UnknownAppVisibilityControllerTest.java @@ -55,6 +55,18 @@ public class UnknownAppVisibilityControllerTest extends WindowTestsBase { } @Test + public void testSkipResume() { + final ActivityRecord activity = createTestActivityRecord(mDisplayContent); + activity.mLaunchTaskBehind = true; + mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(activity); + mDisplayContent.mUnknownAppVisibilityController.notifyRelayouted(activity); + + // Make sure our handler processed the message. + waitHandlerIdle(mWm.mH); + assertTrue(mDisplayContent.mUnknownAppVisibilityController.allResolved()); + } + + @Test public void testMultiple() { final ActivityRecord activity1 = createTestActivityRecord(mDisplayContent); final ActivityRecord activity2 = createTestActivityRecord(mDisplayContent); diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 09c16595e2c7..52e0953813a0 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -1625,6 +1625,7 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); + field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 1a606b7ae6a7..2a073a1f1d81 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -851,6 +851,19 @@ public class ProvisioningManager { public static final int KEY_RTT_ENABLED = 66; /** + * An obfuscated string defined by the carrier to indicate VoWiFi entitlement status. + * + * <p>Implementation note: how to generate the value and how it affects VoWiFi service + * should follow carrier requirements. For example, set an empty string could result in + * VoWiFi being disabled by IMS service, and set to a specific string could enable. + * + * <p>Value is in String format. + * @see #setProvisioningStringValue(int, String) + * @see #getProvisioningStringValue(int) + */ + public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; + + /** * Callback for IMS provisioning changes. */ public static class Callback { diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java index d0cec52dfc86..487786045b8e 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -729,7 +729,8 @@ public class ImsConfig { // Expand the operator config items as needed here, need to change // PROVISIONED_CONFIG_END after that. - public static final int PROVISIONED_CONFIG_END = RTT_SETTING_ENABLED; + public static final int PROVISIONED_CONFIG_END = + ProvisioningManager.KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID; // Expand the operator config items as needed here. } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 9a8e37b19e56..57d6127b1cd1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -88,7 +88,7 @@ class OpenAppWarmTest( noUncoveredRegions(Surface.ROTATION_0, rotation, bugId = 141361128) navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation) statusBarLayerRotatesScales(Surface.ROTATION_0, rotation) - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(enabled = false) wallpaperLayerBecomesInvisible() } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt index 91ec211805f7..279092d716e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt @@ -87,9 +87,9 @@ class OpenAppToSplitScreenTest( } layersTrace { - navBarLayerIsAlwaysVisible() + navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible() - noUncoveredRegions(rotation) + noUncoveredRegions(rotation, enabled = false) navBarLayerRotatesAndScales(rotation, bugId = 140855415) statusBarLayerRotatesScales(rotation) |