From 9ce557e3636eee49bd97f559c088322f135337eb Mon Sep 17 00:00:00 2001 From: Hu Guo Date: Fri, 1 Dec 2023 13:58:46 +0000 Subject: [PATCH 001/656] correct up eventTime An incorrect up eventTime affects the value of mHandledByLongPress of SingleKeyGestureDetector, which is set in SingleKeyGestureDetector#interceptKeyUp Change-Id: I71bbdcd0bb964aeb7fe7a09f8f962ce32b8316db --- .../core/java/com/android/server/input/InputShellCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/input/InputShellCommand.java b/services/core/java/com/android/server/input/InputShellCommand.java index 138186ba6191..eac5f83f946c 100644 --- a/services/core/java/com/android/server/input/InputShellCommand.java +++ b/services/core/java/com/android/server/input/InputShellCommand.java @@ -448,6 +448,7 @@ public class InputShellCommand extends ShellCommand { event, nextEventTime, 1 /* repeatCount */, KeyEvent.FLAG_LONG_PRESS); injectKeyEvent(longPressEvent, async); } + event = KeyEvent.changeTimeRepeat(event, SystemClock.uptimeMillis(), 0); injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP), async); } -- GitLab From 3f23bae2707ed648348740426f950e68984b44ed Mon Sep 17 00:00:00 2001 From: helen cheuk Date: Thu, 31 Oct 2024 18:40:03 +0000 Subject: [PATCH 002/656] [Custom Key Glyph] Add getter to fields in KeyCombination Add getter so SysUI could get the fields Bug: 375681062 Test: Manual Flag: com.android.hardware.input.keyboard_glyph_map Change-Id: I69699f908cd753582a58749985b6e70c02a8f9d5 --- core/java/android/hardware/input/KeyGlyphMap.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/java/android/hardware/input/KeyGlyphMap.java b/core/java/android/hardware/input/KeyGlyphMap.java index f82d1cf276b9..de5df9188c17 100644 --- a/core/java/android/hardware/input/KeyGlyphMap.java +++ b/core/java/android/hardware/input/KeyGlyphMap.java @@ -133,6 +133,14 @@ public final class KeyGlyphMap implements Parcelable { } }; + public int getModifierState() { + return mModifierState; + } + + public int getKeycode() { + return mKeycode; + } + @Override public int describeContents() { return 0; -- GitLab From 0c3ad80f2de7bfd6d8fb9bda73e96fd4026401b3 Mon Sep 17 00:00:00 2001 From: Edward Savage-Jones Date: Wed, 9 Oct 2024 09:20:09 +0200 Subject: [PATCH 003/656] Allow packages to be enabled depending on sku value This change allows packages to be enabled on boot depending on the sku of the device. This enables the same product image to be used on different skus. Example: Packages are listed here in a disabled state: /product/etc/sysconfig/disabled-in-sku.xml Then enabled depending on the sku values read from "ro.boot.hardware.sku" e.g. an sku of '00001': /product/etc/sysconfig/sku_00001/enabled-in-sku-override.xml Status of apps after customisation: product2 app - enabled product1 app - disabled and not visible to PackageManager Bug: 376275452 Test: atest FrameworksServicesTests:com.android.server.systemconfig.SystemConfigTest Change-Id: Id1418beda7e264cab90086836c32785341fb5aac --- .../java/com/android/server/SystemConfig.java | 19 +++++++++++ .../server/systemconfig/SystemConfigTest.java | 34 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 9b987e9850c4..8c83ad70625a 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -1319,6 +1319,7 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "disabled-in-sku": case "disabled-until-used-preinstalled-carrier-app": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); @@ -1335,6 +1336,24 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "enabled-in-sku-override": { + if (allowAppConfigs) { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, + "<" + name + "> without " + + "package in " + permFile + " at " + + parser.getPositionDescription()); + } else if (!mDisabledUntilUsedPreinstalledCarrierApps.remove(pkgname)) { + Slog.w(TAG, + "<" + name + "> packagename:" + pkgname + " not included" + + "in disabled-in-sku"); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; case "privapp-permissions": { if (allowPrivappPermissions) { // privapp permissions from system, apex, vendor, product and diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 3bc089fb3f5d..842c441e09f2 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -691,6 +691,40 @@ public class SystemConfigTest { assertThat(actual).isEqualTo(expected); } + /** + * Tests that readPermissions works correctly for the tags: + * disabled-in-sku, enabled-in-sku-override. + * I.e. that disabled-in-sku add package to block list and + * enabled-in-sku-override removes package from the list. + */ + @Test + public void testDisablePackageInSku() throws Exception { + final String disable_in_sku = + "\n" + + " \n" + + " \n" + + "\n"; + + final String enable_in_sku_override = + "\n" + + " \n" + + "\n"; + + final File folder1 = createTempSubfolder("folder1"); + createTempFile(folder1, "permissionFile1.xml", disable_in_sku); + + final File folder2 = createTempSubfolder("folder2"); + createTempFile(folder2, "permissionFile2.xml", enable_in_sku_override); + + readPermissions(folder1, /* Grant all permission flags */ ~0); + readPermissions(folder2, /* Grant all permission flags */ ~0); + + final ArraySet blocklist = mSysConfig.getDisabledUntilUsedPreinstalledCarrierApps(); + + assertThat(blocklist).contains("com.sony.product1.app"); + assertThat(blocklist).doesNotContain("com.sony.product2.app"); + } + private void parseSharedLibraries(String contents) throws IOException { File folder = createTempSubfolder("permissions_folder"); createTempFile(folder, "permissions.xml", contents); -- GitLab From b1a7e34e16f240d84cb720ef1277cd00ec1f457e Mon Sep 17 00:00:00 2001 From: Catherine Liang Date: Tue, 5 Nov 2024 15:49:31 +0000 Subject: [PATCH 004/656] Revert "Revert "Enable color resources loader to be created usin..." Revert submission 30159224-revert-29910507-launcher-color-preview-fix-XVTAANIJBS Reason for revert: Bringing back original change with screenshot test fixes, by keeping both the old color override solution using ARSC file manipulation, and the new color override solution using fabricated runtime resource overlays. Keep the old solution for screenshot tests, and use the new solution for the Customization Picker use case and other use cases needing to overlay private Android resources in a specific context. Reverted changes: /q/submissionid:30159224-revert-29910507-launcher-color-preview-fix-XVTAANIJBS Flag: com.android.systemui.shared.new_customization_picker_ui Test: manually verified Bug: 377545987 Change-Id: Ia34ac28b79bb6b79cb7459e8326f9fa0ea59b65c --- .../android/content/om/OverlayManager.java | 5 +- .../content/om/OverlayManagerTransaction.java | 15 ++++- .../android/content/res/AssetManager.java | 5 +- .../content/res/loader/ResourcesProvider.java | 2 - core/java/android/widget/RemoteViews.java | 67 ++++++++++++++++++- .../internal/content/om/OverlayConfig.java | 3 +- .../content/om/OverlayManagerImpl.java | 20 +++--- .../overlaytest/OverlayManagerImplTest.java | 15 ----- 8 files changed, 99 insertions(+), 33 deletions(-) diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java index ed965b3d1777..6db7dfe4f705 100644 --- a/core/java/android/content/om/OverlayManager.java +++ b/core/java/android/content/om/OverlayManager.java @@ -78,7 +78,8 @@ public class OverlayManager { /** * Applications can use OverlayManager to create overlays to overlay on itself resources. The - * overlay target is itself and the work range is only in caller application. + * overlay target is itself, or the Android package, and the work range is only in caller + * application. * *

In {@link android.content.Context#getSystemService(String)}, it crashes because of {@link * java.lang.NullPointerException} if the parameter is OverlayManager. if the self-targeting is @@ -401,7 +402,7 @@ public class OverlayManager { } /** - * Get the related information of overlays for {@code targetPackageName}. + * Get the related information of self-targeting overlays for {@code targetPackageName}. * * @param targetPackageName the target package name * @return a list of overlay information diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java index becd0ea285af..87b2e9350aa1 100644 --- a/core/java/android/content/om/OverlayManagerTransaction.java +++ b/core/java/android/content/om/OverlayManagerTransaction.java @@ -209,6 +209,7 @@ public final class OverlayManagerTransaction implements Parcelable { */ public static final class Builder { private final List mRequests = new ArrayList<>(); + private boolean mSelfTargeting = false; /** * Request that an overlay package be enabled and change its loading @@ -245,6 +246,18 @@ public final class OverlayManagerTransaction implements Parcelable { return this; } + /** + * Request that an overlay package be self-targeting. Self-targeting overlays enable + * applications to overlay on itself resources. The overlay target is itself, or the Android + * package, and the work range is only in caller application. + * @param selfTargeting whether the overlay is self-targeting, the default is false. + * @hide + */ + public Builder setSelfTargeting(boolean selfTargeting) { + mSelfTargeting = selfTargeting; + return this; + } + /** * Registers the fabricated overlay with the overlay manager so it can be enabled and * disabled for any user. @@ -286,7 +299,7 @@ public final class OverlayManagerTransaction implements Parcelable { */ @NonNull public OverlayManagerTransaction build() { - return new OverlayManagerTransaction(mRequests, false /* selfTargeting */); + return new OverlayManagerTransaction(mRequests, mSelfTargeting); } } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 6fd4d0141977..347bebdf46e7 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -75,7 +75,10 @@ public final class AssetManager implements AutoCloseable { private static final String TAG = "AssetManager"; private static final boolean DEBUG_REFS = false; - private static final String FRAMEWORK_APK_PATH = getFrameworkApkPath(); + /** + * @hide + */ + public static final String FRAMEWORK_APK_PATH = getFrameworkApkPath(); private static final String FRAMEWORK_APK_PATH_DEVICE = "/system/framework/framework-res.apk"; private static final String FRAMEWORK_APK_PATH_RAVENWOOD = "ravenwood-data/framework-res.apk"; diff --git a/core/java/android/content/res/loader/ResourcesProvider.java b/core/java/android/content/res/loader/ResourcesProvider.java index b097bc0dcd8c..830b7e0fa2d0 100644 --- a/core/java/android/content/res/loader/ResourcesProvider.java +++ b/core/java/android/content/res/loader/ResourcesProvider.java @@ -90,8 +90,6 @@ public class ResourcesProvider implements AutoCloseable, Closeable { throws IOException { Objects.requireNonNull(overlayInfo); Preconditions.checkArgument(overlayInfo.isFabricated(), "Not accepted overlay"); - Preconditions.checkStringNotEmpty( - overlayInfo.getTargetOverlayableName(), "Without overlayable name"); final String overlayName = OverlayManagerImpl.checkOverlayNameValid(overlayInfo.getOverlayName()); final String path = diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 9b6311f35d17..388bdc480be1 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -20,6 +20,7 @@ import static android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL; import static android.appwidget.flags.Flags.FLAG_REMOTE_VIEWS_PROTO; import static android.appwidget.flags.Flags.drawDataParcel; import static android.appwidget.flags.Flags.remoteAdapterConversion; +import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8; import static android.util.proto.ProtoInputStream.NO_MORE_FIELDS; import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR; @@ -54,6 +55,10 @@ import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentSender; import android.content.ServiceConnection; +import android.content.om.FabricatedOverlay; +import android.content.om.OverlayInfo; +import android.content.om.OverlayManager; +import android.content.om.OverlayManagerTransaction; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.ColorStateList; @@ -8547,8 +8552,6 @@ public class RemoteViews implements Parcelable, Filter { /** * Object allowing the modification of a context to overload the system's dynamic colors. * - * Only colors from {@link android.R.color#system_accent1_0} to - * {@link android.R.color#system_neutral2_1000} can be overloaded. * @hide */ public static final class ColorResources { @@ -8559,6 +8562,9 @@ public class RemoteViews implements Parcelable, Filter { // Size, in bytes, of an entry in the array of colors in an ARSC file. private static final int ARSC_ENTRY_SIZE = 16; + private static final String OVERLAY_NAME = "remote_views_color_resources"; + private static final String OVERLAY_TARGET_PACKAGE_NAME = "android"; + private final ResourcesLoader mLoader; private final SparseIntArray mColorMapping; @@ -8629,7 +8635,11 @@ public class RemoteViews implements Parcelable, Filter { } /** - * Adds a resource loader for theme colors to the given context. + * Adds a resource loader for theme colors to the given context. The loader is created + * based on resource files created at build time. + * + *

Only colors from {@link android.R.color#system_accent1_0} to + * {@link android.R.color#system_error_1000} can be overloaded.

* * @param context Context of the view hosting the widget. * @param colorMapping Mapping of resources to color values. @@ -8667,6 +8677,57 @@ public class RemoteViews implements Parcelable, Filter { } return null; } + + /** + * Adds a resource loader for theme colors to the given context. The loader is created + * using fabricated runtime resource overlay (FRRO). + * + *

The created class can overlay any color resources, private or public, at runtime.

+ * + * @param context Context of the view hosting the widget. + * @param colorMapping Mapping of resources to color values. + * + * @hide + */ + @Nullable + public static ColorResources createWithOverlay(Context context, + SparseIntArray colorMapping) { + try { + String owningPackage = context.getPackageName(); + FabricatedOverlay overlay = new FabricatedOverlay.Builder(owningPackage, + OVERLAY_NAME, OVERLAY_TARGET_PACKAGE_NAME).build(); + + for (int i = 0; i < colorMapping.size(); i++) { + overlay.setResourceValue( + context.getResources().getResourceName(colorMapping.keyAt(i)), + TYPE_INT_COLOR_ARGB8, colorMapping.valueAt(i), null); + } + OverlayManager overlayManager = context.getSystemService(OverlayManager.class); + OverlayManagerTransaction.Builder transaction = + new OverlayManagerTransaction.Builder() + .registerFabricatedOverlay(overlay) + .setSelfTargeting(true); + overlayManager.commit(transaction.build()); + + OverlayInfo overlayInfo = + overlayManager.getOverlayInfosForTarget(OVERLAY_TARGET_PACKAGE_NAME) + .stream() + .filter(info -> TextUtils.equals(info.overlayName, OVERLAY_NAME) + && TextUtils.equals(info.packageName, owningPackage)) + .findFirst() + .orElse(null); + if (overlayInfo == null) { + Log.e(LOG_TAG, "Failed to get overlay info ", new Throwable()); + return null; + } + ResourcesLoader colorsLoader = new ResourcesLoader(); + colorsLoader.addProvider(ResourcesProvider.loadOverlay(overlayInfo)); + return new ColorResources(colorsLoader, colorMapping.clone()); + } catch (Exception e) { + Log.e(LOG_TAG, "Failed to add theme color overlay into loader", e); + } + return null; + } } /** diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index 07e178c0ba27..38593b4a2a99 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -19,6 +19,7 @@ package com.android.internal.content.om; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackagePartitions; +import android.content.res.AssetManager; import android.os.Build; import android.os.Trace; import android.util.ArrayMap; @@ -533,7 +534,7 @@ public class OverlayConfig { */ @NonNull public String[] createImmutableFrameworkIdmapsInZygote() { - final String targetPath = "/system/framework/framework-res.apk"; + final String targetPath = AssetManager.FRAMEWORK_APK_PATH; final ArrayList idmapPaths = new ArrayList<>(); final ArrayList idmapInvocations = getImmutableFrameworkOverlayIdmapInvocations(); diff --git a/core/java/com/android/internal/content/om/OverlayManagerImpl.java b/core/java/com/android/internal/content/om/OverlayManagerImpl.java index c4624498138d..fa5cf2a396b9 100644 --- a/core/java/com/android/internal/content/om/OverlayManagerImpl.java +++ b/core/java/com/android/internal/content/om/OverlayManagerImpl.java @@ -35,6 +35,7 @@ import android.content.om.OverlayManagerTransaction.Request; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.parsing.FrameworkParsingPackageUtils; +import android.content.res.AssetManager; import android.os.FabricatedOverlayInfo; import android.os.FabricatedOverlayInternal; import android.os.FabricatedOverlayInternalEntry; @@ -60,8 +61,8 @@ import java.util.List; import java.util.Objects; /** - * This class provides the functionalities of registering an overlay, unregistering an overlay, and - * getting the list of overlays information. + * This class provides the functionalities for managing self-targeting overlays, including + * registering an overlay, unregistering an overlay, and getting the list of overlays information. */ public class OverlayManagerImpl { private static final String TAG = "OverlayManagerImpl"; @@ -234,14 +235,17 @@ public class OverlayManagerImpl { Preconditions.checkArgument(!entryList.isEmpty(), "overlay entries shouldn't be empty"); final String overlayName = checkOverlayNameValid(overlayInternal.overlayName); checkPackageName(overlayInternal.packageName); - checkPackageName(overlayInternal.targetPackageName); - Preconditions.checkStringNotEmpty( - overlayInternal.targetOverlayable, - "Target overlayable should be neither null nor empty string."); + Preconditions.checkStringNotEmpty(overlayInternal.targetPackageName); final ApplicationInfo applicationInfo = mContext.getApplicationInfo(); - final String targetPackage = Preconditions.checkStringNotEmpty( - applicationInfo.getBaseCodePath()); + String targetPackage = null; + if (TextUtils.equals(overlayInternal.targetPackageName, "android")) { + targetPackage = AssetManager.FRAMEWORK_APK_PATH; + } else { + targetPackage = Preconditions.checkStringNotEmpty( + applicationInfo.getBaseCodePath()); + } + final Path frroPath = mBasePath.resolve(overlayName + FRRO_EXTENSION); final Path idmapPath = mBasePath.resolve(overlayName + IDMAP_EXTENSION); diff --git a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java index 40d0bef2fb0f..28d6545c8a5b 100644 --- a/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java +++ b/core/tests/overlaytests/device_self_targeting/src/com/android/overlaytest/OverlayManagerImplTest.java @@ -209,21 +209,6 @@ public class OverlayManagerImplTest { return overlayInternal; } - @Test - public void registerOverlay_forAndroidPackage_shouldFail() { - FabricatedOverlayInternal overlayInternal = - createOverlayWithName( - mOverlayName, - SYSTEM_APP_OVERLAYABLE, - "android", - List.of(Pair.create("color/white", Pair.create(null, Color.BLACK)))); - - assertThrows( - "Wrong target package name", - IllegalArgumentException.class, - () -> mOverlayManagerImpl.registerFabricatedOverlay(overlayInternal)); - } - @Test public void getOverlayInfosForTarget_defaultShouldBeZero() { List overlayInfos = -- GitLab From e863b7b8285f122fafbab5439ad3c337172bff6c Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 2 Jun 2023 13:57:57 +0000 Subject: [PATCH 005/656] InputMethodSubtypeArray: prevent negative count injection Fixes an issue where negative counts could be injected via the Parcel constructor. The writeToParcel method in that case would write data that a subsequent read would not consume. Fixes: 277916797 Fixes: 354682735 Test: atest InputMethodSubtypeArrayTest Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e --- .../inputmethod/InputMethodSubtypeArray.java | 4 +++ .../InputMethodSubtypeArrayTest.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..5af8558ccde9 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + Parcel p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + Parcel p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { -- GitLab From 1e973616542153aaed999c4f1c292ce493f40049 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 2 Jun 2023 13:57:57 +0000 Subject: [PATCH 006/656] InputMethodSubtypeArray: prevent negative count injection Fixes an issue where negative counts could be injected via the Parcel constructor. The writeToParcel method in that case would write data that a subsequent read would not consume. Fixes: 277916797 Fixes: 354682735 Test: atest InputMethodSubtypeArrayTest Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e --- .../inputmethod/InputMethodSubtypeArray.java | 4 +++ .../InputMethodSubtypeArrayTest.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..5af8558ccde9 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + Parcel p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + Parcel p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { -- GitLab From 649bb61461b7e0d3b55c55fe9a55792b5fd5c741 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 2 Jun 2023 13:57:57 +0000 Subject: [PATCH 007/656] InputMethodSubtypeArray: prevent negative count injection Fixes an issue where negative counts could be injected via the Parcel constructor. The writeToParcel method in that case would write data that a subsequent read would not consume. Fixes: 277916797 Fixes: 354682735 Test: atest InputMethodSubtypeArrayTest Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e --- .../inputmethod/InputMethodSubtypeArray.java | 4 +++ .../InputMethodSubtypeArrayTest.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..5af8558ccde9 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + Parcel p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + Parcel p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { -- GitLab From b7acc399ad02f3c2faa6cdb61a86a3c642418208 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 2 Jun 2023 13:57:57 +0000 Subject: [PATCH 008/656] InputMethodSubtypeArray: prevent negative count injection Fixes an issue where negative counts could be injected via the Parcel constructor. The writeToParcel method in that case would write data that a subsequent read would not consume. Fixes: 277916797 Fixes: 354682735 Test: atest InputMethodSubtypeArrayTest Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e --- .../inputmethod/InputMethodSubtypeArray.java | 4 +++ .../InputMethodSubtypeArrayTest.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..5af8558ccde9 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + Parcel p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + Parcel p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { -- GitLab From 86906b7cb471fa0f9f7fd8a391d3b10b2fc29046 Mon Sep 17 00:00:00 2001 From: Adrian Roos Date: Fri, 2 Jun 2023 13:57:57 +0000 Subject: [PATCH 009/656] InputMethodSubtypeArray: prevent negative count injection Fixes an issue where negative counts could be injected via the Parcel constructor. The writeToParcel method in that case would write data that a subsequent read would not consume. Fixes: 277916797 Fixes: 354682735 Test: atest InputMethodSubtypeArrayTest Merged-In: I7e881d82415051179c59bf5df97f8ba0a41e693e Change-Id: I7e881d82415051179c59bf5df97f8ba0a41e693e --- .../inputmethod/InputMethodSubtypeArray.java | 4 +++ .../InputMethodSubtypeArrayTest.java | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java index 50e95c80cfed..ee36dc72e346 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java +++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.compat.annotation.UnsupportedAppUsage; +import android.os.BadParcelableException; import android.os.Parcel; import android.util.Slog; @@ -69,6 +70,9 @@ public class InputMethodSubtypeArray { */ public InputMethodSubtypeArray(final Parcel source) { mCount = source.readInt(); + if (mCount < 0) { + throw new BadParcelableException("mCount must be non-negative."); + } if (mCount > 0) { mDecompressedSize = source.readInt(); mCompressedData = source.createByteArray(); diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java index e2fb46af5b64..5af8558ccde9 100644 --- a/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodSubtypeArrayTest.java @@ -16,9 +16,14 @@ package android.view.inputmethod; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; +import android.os.BadParcelableException; import android.os.Parcel; +import android.platform.test.annotations.Presubmit; import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; import androidx.test.filters.SmallTest; @@ -31,6 +36,7 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) +@Presubmit public class InputMethodSubtypeArrayTest { @Test @@ -59,6 +65,36 @@ public class InputMethodSubtypeArrayTest { assertEquals(clonedArray.get(2), clonedClonedArray.get(2)); } + @Test + public void testNegativeCount() throws Exception { + InputMethodSubtypeArray negativeCountArray; + try { + // Construct a InputMethodSubtypeArray with: mCount = -1 + Parcel p = Parcel.obtain(); + p.writeInt(-1); + p.setDataPosition(0); + negativeCountArray = new InputMethodSubtypeArray(p); + } catch (BadParcelableException e) { + // Expected with fix: Prevent negative mCount + assertThat(e).hasMessageThat().contains("mCount"); + return; + } + assertWithMessage("Test set-up failed") + .that(negativeCountArray.getCount()).isEqualTo(-1); + + Parcel p = Parcel.obtain(); + // Writes: int (mCount), int (mDecompressedSize), byte[] (mCompressedData) + negativeCountArray.writeToParcel(p); + p.setDataPosition(0); + // Reads: int (mCount) + // Leaves: int (mDecompressedSize), byte[] (mCompressedData) + new InputMethodSubtypeArray(p); + + assertWithMessage("Didn't read all data that was previously written") + .that(p.dataPosition()) + .isEqualTo(p.dataSize()); + } + InputMethodSubtypeArray cloneViaParcel(final InputMethodSubtypeArray original) { Parcel parcel = null; try { -- GitLab From 13ab01a2e3852b9fd04f03cb5e83b94b7135f2f4 Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Fri, 8 Nov 2024 11:10:58 -0800 Subject: [PATCH 010/656] MockContentResolver: add logging for missing providers This will allow easier debugging for failing unit tests. Bug: 365800992 Test: m Flag: EXEMPT logging only Change-Id: I672a5b00cf378b27e5438f5ad89d2755152bd33b --- test-mock/src/android/test/mock/MockContentResolver.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test-mock/src/android/test/mock/MockContentResolver.java b/test-mock/src/android/test/mock/MockContentResolver.java index 8f4bcccb0cba..af6ee3d0f71b 100644 --- a/test-mock/src/android/test/mock/MockContentResolver.java +++ b/test-mock/src/android/test/mock/MockContentResolver.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.IContentProvider; import android.database.ContentObserver; import android.net.Uri; +import android.util.Log; import java.util.Collection; import java.util.HashMap; @@ -53,6 +54,7 @@ import java.util.Map; * */ public class MockContentResolver extends ContentResolver { + private static final String TAG = "MockContentResolver"; Map mProviders; /** @@ -105,6 +107,7 @@ public class MockContentResolver extends ContentResolver { if (provider != null) { return provider.getIContentProvider(); } else { + Log.w(TAG, "Provider does not exist: " + name); return null; } } -- GitLab From 119205ec7899518f9d05bb9368e4a9fdc2fd9678 Mon Sep 17 00:00:00 2001 From: maheshkkv Date: Mon, 4 Nov 2024 18:04:24 -0800 Subject: [PATCH 011/656] Register USD service Flag: android.net.wifi.flags.usd Bug: 340878198 Test: build successfully Change-Id: Ie9f519cd05bf98c75a7858bed3bf37d12acd585e --- core/api/system-current.txt | 1 + core/java/android/content/Context.java | 14 ++++++++++++++ services/java/com/android/server/SystemServer.java | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0e521c664340..c9c6691eb880 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3905,6 +3905,7 @@ package android.content { field public static final String WIFI_NL80211_SERVICE = "wifinl80211"; field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager"; field public static final String WIFI_SCANNING_SERVICE = "wifiscanner"; + field @FlaggedApi("android.net.wifi.flags.usd") public static final String WIFI_USD_SERVICE = "wifi_usd"; } public final class ContextParams { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6086f2455a31..d68d153d2e64 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4249,6 +4249,7 @@ public abstract class Context { //@hide: WIFI_RTT_SERVICE, //@hide: ETHERNET_SERVICE, WIFI_RTT_RANGING_SERVICE, + WIFI_USD_SERVICE, NSD_SERVICE, AUDIO_SERVICE, AUDIO_DEVICE_VOLUME_SERVICE, @@ -5090,6 +5091,19 @@ public abstract class Context { */ public static final String WIFI_RTT_RANGING_SERVICE = "wifirtt"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.wifi.usd.UsdManager} for Unsynchronized Service Discovery (USD) operation. + * + * @see #getSystemService(String) + * @see android.net.wifi.usd.UsdManager + * @hide + */ + @FlaggedApi(android.net.wifi.flags.Flags.FLAG_USD) + @SystemApi + public static final String WIFI_USD_SERVICE = "wifi_usd"; + /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.net.lowpan.LowpanManager} for handling management of diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 19b03437292f..fff280ad13e5 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -419,6 +419,8 @@ public final class SystemServer implements Dumpable { "com.android.server.wifi.aware.WifiAwareService"; private static final String WIFI_P2P_SERVICE_CLASS = "com.android.server.wifi.p2p.WifiP2pService"; + private static final String WIFI_USD_SERVICE_CLASS = + "com.android.server.wifi.usd.UsdService"; private static final String CONNECTIVITY_SERVICE_APEX_PATH = "/apex/com.android.tethering/javalib/service-connectivity.jar"; private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = @@ -2128,6 +2130,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startServiceFromJar( WIFI_SCANNING_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); t.traceEnd(); + // Start USD service + if (android.net.wifi.flags.Flags.usd()) { + t.traceBegin("StartUsd"); + mSystemServiceManager.startServiceFromJar( + WIFI_USD_SERVICE_CLASS, WIFI_APEX_SERVICE_JAR_PATH); + t.traceEnd(); + } } if (context.getPackageManager().hasSystemFeature( -- GitLab From 7ccfc019a33a6d7a1221261ea37c150b9b332c2e Mon Sep 17 00:00:00 2001 From: Shubang Lu Date: Tue, 12 Nov 2024 18:01:17 -0800 Subject: [PATCH 012/656] [MQ] Add a method to get profile handle Test: mmm Flag: android.media.tv.flags.media_quality_fw Bug: 374849325 Change-Id: Ib261f7d5c395e8f7c473dc14628e1977480365dc --- .../android/media/quality/IMediaQualityManager.aidl | 2 ++ .../android/media/quality/MediaQualityManager.java | 11 +++++++++++ .../server/media/quality/MediaQualityService.java | 6 ++++++ 3 files changed, 19 insertions(+) diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl index 250d59b7c2d7..9c9cf200a34c 100644 --- a/media/java/android/media/quality/IMediaQualityManager.aidl +++ b/media/java/android/media/quality/IMediaQualityManager.aidl @@ -21,6 +21,7 @@ import android.media.quality.IAmbientBacklightCallback; import android.media.quality.IPictureProfileCallback; import android.media.quality.ISoundProfileCallback; import android.media.quality.ParamCapability; +import android.media.quality.PictureProfileHandle; import android.media.quality.PictureProfile; import android.media.quality.SoundProfile; @@ -38,6 +39,7 @@ interface IMediaQualityManager { List getPictureProfilePackageNames(); List getPictureProfileAllowList(); void setPictureProfileAllowList(in List packages); + PictureProfileHandle getPictureProfileHandle(in String id); SoundProfile createSoundProfile(in SoundProfile pp); void updateSoundProfile(in String id, in SoundProfile pp); diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java index 4d4526cf9925..1febf2f1a1ba 100644 --- a/media/java/android/media/quality/MediaQualityManager.java +++ b/media/java/android/media/quality/MediaQualityManager.java @@ -254,6 +254,17 @@ public final class MediaQualityManager { } } + /** + * Gets picture profile handle by profile ID. + * @hide + */ + public PictureProfileHandle getPictureProfileHandle(String id) { + try { + return mService.getPictureProfileHandle(id); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * Creates a picture profile and store it in the system. diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index 84413d5710d0..204105e6038a 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -28,6 +28,7 @@ import android.media.quality.ISoundProfileCallback; import android.media.quality.MediaQualityContract.PictureQuality; import android.media.quality.ParamCapability; import android.media.quality.PictureProfile; +import android.media.quality.PictureProfileHandle; import android.media.quality.SoundProfile; import android.os.Bundle; import android.util.Log; @@ -158,6 +159,11 @@ public class MediaQualityService extends SystemService { return new ArrayList<>(); } + @Override + public PictureProfileHandle getPictureProfileHandle(String id) { + return null; + } + @Override public SoundProfile createSoundProfile(SoundProfile pp) { // TODO: implement -- GitLab From 6d3fb884091426cb852693133129a3abcc519061 Mon Sep 17 00:00:00 2001 From: Ray Chin Date: Wed, 13 Nov 2024 09:25:42 +0800 Subject: [PATCH 013/656] Rename Ext to Extension Flag: EXEMPT refactor Bug: 376118401 Test: atest TunerTest on cf_x86_tv (1 failure not caused by this change) Change-Id: I73de0ac23ccdfca51be7618bacfc09fca7554670 --- core/api/system-current.txt | 10 ++++---- .../tv/tuner/frontend/FrontendStatus.java | 16 ++++++------- ...tandardExt.java => StandardExtension.java} | 24 +++++++++---------- media/jni/android_media_tv_Tuner.cpp | 6 ++--- 4 files changed, 28 insertions(+), 28 deletions(-) rename media/java/android/media/tv/tuner/frontend/{StandardExt.java => StandardExtension.java} (74%) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 62c8a34ac1f3..e201da967e67 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -9604,7 +9604,7 @@ package android.media.tv.tuner.frontend { method public int getSignalStrength(); method public int getSnr(); method public int getSpectralInversion(); - method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExt getStandardExt(); + method @FlaggedApi("android.media.tv.flags.tuner_w_apis") @NonNull public android.media.tv.tuner.frontend.StandardExtension getStandardExtension(); method @NonNull public int[] getStreamIds(); method public int getSymbolRate(); method @IntRange(from=0, to=65535) public int getSystemId(); @@ -9659,7 +9659,7 @@ package android.media.tv.tuner.frontend { field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6 field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1 field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa - field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = 47; // 0x2f + field @FlaggedApi("android.media.tv.flags.tuner_w_apis") public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION = 47; // 0x2f field public static final int FRONTEND_STATUS_TYPE_STREAM_IDS = 39; // 0x27 field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7 field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d @@ -9951,9 +9951,9 @@ package android.media.tv.tuner.frontend { method public default void onUnlocked(); } - @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExt { - method public int getDvbsStandardExt(); - method public int getDvbtStandardExt(); + @FlaggedApi("android.media.tv.flags.tuner_w_apis") public final class StandardExtension { + method public int getDvbsStandardExtension(); + method public int getDvbtStandardExtension(); } } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index 898a8bf02edb..4de71235df8c 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -63,7 +63,7 @@ public class FrontendStatus { FRONTEND_STATUS_TYPE_DVBT_CELL_IDS, FRONTEND_STATUS_TYPE_ATSC3_ALL_PLP_INFO, FRONTEND_STATUS_TYPE_IPTV_CONTENT_URL, FRONTEND_STATUS_TYPE_IPTV_PACKETS_LOST, FRONTEND_STATUS_TYPE_IPTV_PACKETS_RECEIVED, FRONTEND_STATUS_TYPE_IPTV_WORST_JITTER_MS, - FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXT}) + FRONTEND_STATUS_TYPE_IPTV_AVERAGE_JITTER_MS, FRONTEND_STATUS_TYPE_STANDARD_EXTENSION}) @Retention(RetentionPolicy.SOURCE) public @interface FrontendStatusType {} @@ -317,7 +317,7 @@ public class FrontendStatus { * Standard extension. */ @FlaggedApi(Flags.FLAG_TUNER_W_APIS) - public static final int FRONTEND_STATUS_TYPE_STANDARD_EXT = + public static final int FRONTEND_STATUS_TYPE_STANDARD_EXTENSION = android.hardware.tv.tuner.FrontendStatusType.STANDARD_EXT; /** @hide */ @@ -567,7 +567,7 @@ public class FrontendStatus { private Long mIptvPacketsReceived; private Integer mIptvWorstJitterMs; private Integer mIptvAverageJitterMs; - private StandardExt mStandardExt; + private StandardExtension mStandardExtension; // Constructed and fields set by JNI code. private FrontendStatus() { @@ -1298,12 +1298,12 @@ public class FrontendStatus { */ @NonNull @FlaggedApi(Flags.FLAG_TUNER_W_APIS) - public StandardExt getStandardExt() { + public StandardExtension getStandardExtension() { TunerVersionChecker.checkHigherOrEqualVersionTo( - TunerVersionChecker.TUNER_VERSION_4_0, "StandardExt status"); - if (mStandardExt == null) { - throw new IllegalStateException("StandardExt status is empty"); + TunerVersionChecker.TUNER_VERSION_4_0, "StandardExtension status"); + if (mStandardExtension == null) { + throw new IllegalStateException("StandardExtension status is empty"); } - return mStandardExt; + return mStandardExtension; } } diff --git a/media/java/android/media/tv/tuner/frontend/StandardExt.java b/media/java/android/media/tv/tuner/frontend/StandardExtension.java similarity index 74% rename from media/java/android/media/tv/tuner/frontend/StandardExt.java rename to media/java/android/media/tv/tuner/frontend/StandardExtension.java index 490727278b46..8bff3dd99cdb 100644 --- a/media/java/android/media/tv/tuner/frontend/StandardExt.java +++ b/media/java/android/media/tv/tuner/frontend/StandardExtension.java @@ -29,16 +29,16 @@ import android.media.tv.flags.Flags; */ @SystemApi @FlaggedApi(Flags.FLAG_TUNER_W_APIS) -public final class StandardExt { - private final int mDvbsStandardExt; - private final int mDvbtStandardExt; +public final class StandardExtension { + private final int mDvbsStandardExtension; + private final int mDvbtStandardExtension; /** * Private constructor called by JNI only. */ - private StandardExt(int dvbsStandardExt, int dvbtStandardExt) { - mDvbsStandardExt = dvbsStandardExt; - mDvbtStandardExt = dvbtStandardExt; + private StandardExtension(int dvbsStandardExtension, int dvbtStandardExtension) { + mDvbsStandardExtension = dvbsStandardExtension; + mDvbtStandardExtension = dvbtStandardExtension; } /** @@ -50,11 +50,11 @@ public final class StandardExt { * @see android.media.tv.tuner.frontend.DvbsFrontendSettings */ @DvbsFrontendSettings.Standard - public int getDvbsStandardExt() { - if (mDvbsStandardExt == FrontendDvbsStandard.UNDEFINED) { + public int getDvbsStandardExtension() { + if (mDvbsStandardExtension == FrontendDvbsStandard.UNDEFINED) { throw new IllegalStateException("No DVB-S standard transition"); } - return mDvbsStandardExt; + return mDvbsStandardExtension; } /** @@ -66,10 +66,10 @@ public final class StandardExt { * @see android.media.tv.tuner.frontend.DvbtFrontendSettings */ @DvbtFrontendSettings.Standard - public int getDvbtStandardExt() { - if (mDvbtStandardExt == FrontendDvbtStandard.UNDEFINED) { + public int getDvbtStandardExtension() { + if (mDvbtStandardExtension == FrontendDvbtStandard.UNDEFINED) { throw new IllegalStateException("No DVB-T standard transition"); } - return mDvbtStandardExt; + return mDvbtStandardExtension; } } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 80ca4f239a26..2fe069af638a 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -2940,10 +2940,10 @@ jobject JTuner::getFrontendStatus(jintArray types) { break; } case FrontendStatus::Tag::standardExt: { - jfieldID field = env->GetFieldID(clazz, "mStandardExt", - "Landroid/media/tv/tuner/frontend/StandardExt;"); + jfieldID field = env->GetFieldID(clazz, "mStandardExtension", + "Landroid/media/tv/tuner/frontend/StandardExtension;"); ScopedLocalRef standardExtClazz(env, - env->FindClass("android/media/tv/tuner/frontend/StandardExt")); + env->FindClass("android/media/tv/tuner/frontend/StandardExtension")); jmethodID initStandardExt = env->GetMethodID(standardExtClazz.get(), "", "(II)V"); -- GitLab From 8ba6a2c49820753182a2cd31645fe51587e1272c Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 13 Nov 2024 10:43:28 -0500 Subject: [PATCH 014/656] Add some API docs for common confusions Bug: n/a Test: doc only Flag: EXEMPT doc only change Change-Id: I08ea07e91cebc77dfd1bce15d051553e6e062578 --- core/java/android/content/pm/ActivityInfo.java | 7 +++++++ core/java/android/view/SurfaceView.java | 7 ++++++- core/java/android/view/Window.java | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 4f062090ca40..0113129f5203 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -451,6 +451,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * Value of {@link #colorMode} indicating that the activity should use a * high dynamic range if the presentation display supports it. * + *

Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent HDR support.

+ * + *

Important: Although this value was added in API 26, it is strongly recommended + * to avoid using it until API 34 which is when HDR support for the UI toolkit was officially + * added.

+ * * @see android.R.attr#colorMode */ public static final int COLOR_MODE_HDR = 2; diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 1d70d18ac4c8..455e680e0bbc 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -830,7 +830,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall /** * Sets the desired amount of HDR headroom to be used when HDR content is presented on this - * SurfaceView. + * SurfaceView. This is expressed as the ratio of maximum HDR white point over the SDR + * white point, not as absolute nits. * *

By default the system will choose an amount of HDR headroom that is appropriate * for the underlying device capabilities & bit-depth of the panel. However, for some types @@ -844,6 +845,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the * current value.

* + *

Note: This API operates independently of both the + * {@link Window#setColorMode Widow color mode} and the + * {@link Window#setDesiredHdrHeadroom Window desiredHdrHeadroom}

+ * * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR) * and <= 10,000.0. Passing 0.0 will reset to the default, automatically * chosen value. diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0582afe6655d..cbee56365606 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -1334,6 +1334,9 @@ public abstract class Window { *

The requested color mode is not guaranteed to be honored. Please refer to * {@link #getColorMode()} for more information.

* + *

Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent color mode and HDR parameters.

+ * * @see #getColorMode() * @see Display#isWideColorGamut() * @see Configuration#isScreenWideColorGamut() @@ -1361,6 +1364,9 @@ public abstract class Window { * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the * current value.

* + *

Note: This does not impact SurfaceViews or SurfaceControls, as those have their own + * independent desired HDR headroom and HDR capabilities.

+ * * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR) * and <= 10,000.0. Passing 0.0 will reset to the default, automatically * chosen value. -- GitLab From 3c0d959a59dad50df7ff199de6e7f9cbeb118f91 Mon Sep 17 00:00:00 2001 From: Julia Tuttle Date: Tue, 15 Oct 2024 18:03:34 -0400 Subject: [PATCH 015/656] Make Notification.ProgressStyle.createProgressModel /** @hide */ public This will be needed for the PromotedNotificationContentExtractor. Bug: 369151941 Test: builds Flag: EXEMPT not possible Change-Id: I85f60bce5a364ba0259611e70fa43ca5fffd2fed --- core/java/android/app/Notification.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 0381ee0e25ac..612549a69fb9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -11637,8 +11637,10 @@ public class Notification implements Parcelable return points; } - @NonNull - private NotificationProgressModel createProgressModel(int defaultProgressColor, + /** + * @hide + */ + public @NonNull NotificationProgressModel createProgressModel(int defaultProgressColor, int backgroundColor) { final NotificationProgressModel model; if (mIndeterminate) { -- GitLab From 29a9ab42f62210f402aee1dba666a12413f04165 Mon Sep 17 00:00:00 2001 From: Michael Groover Date: Wed, 13 Nov 2024 16:56:52 -0600 Subject: [PATCH 016/656] Allow AMS fgs notification rate limit with ALLOWLIST permission Android 16 limits the shell user to only modify the DeviceConfig flags that have been allowlisted; to support this, the WRITE_DEVICE_CONFIG permission will be removed from the shell user. Some CTS tests adopt the shell permission identity to invoke the AMS service method enableFgsNotificationRateLimit which is currently guarded with the WRITE_DEVICE_CONFIG permission. To support these tests, this commit updates this method to also allow the action if the caller has the WRITE_ALLOWLISTED_DEVICE_CONFIG permission. Bug: 364083026 Flag: android.security.protect_device_config_flags Test: atest ServiceTest Change-Id: I7ac4d3d92a73bc60546bbfcb9c9d5469004f76ce --- core/java/android/app/IActivityManager.aidl | 3 ++- .../com/android/server/am/ActivityManagerService.java | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 34a3ad19b270..97bcea262181 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -864,7 +864,8 @@ interface IActivityManager { /** * Suppress or reenable the rate limit on foreground service notification deferral. - * This is for use within CTS and is protected by android.permission.WRITE_DEVICE_CONFIG. + * This is for use within CTS and is protected by android.permission.WRITE_DEVICE_CONFIG + * and WRITE_ALLOWLISTED_DEVICE_CONFIG. * * @param enable false to suppress rate-limit policy; true to reenable it. */ diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1c3569dd52d0..daf0561a0381 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19066,8 +19066,13 @@ public class ActivityManagerService extends IActivityManager.Stub */ @Override public boolean enableFgsNotificationRateLimit(boolean enable) { - enforceCallingPermission(permission.WRITE_DEVICE_CONFIG, - "enableFgsNotificationRateLimit"); + if (android.security.Flags.protectDeviceConfigFlags()) { + enforceCallingHasAtLeastOnePermission("enableFgsNotificationRateLimit", + permission.WRITE_DEVICE_CONFIG, permission.WRITE_ALLOWLISTED_DEVICE_CONFIG); + } else { + enforceCallingPermission(permission.WRITE_DEVICE_CONFIG, + "enableFgsNotificationRateLimit"); + } synchronized (this) { return mServices.enableFgsNotificationRateLimitLocked(enable); } -- GitLab From c939f2cb680d3b74f4050e35ad51a89c49199758 Mon Sep 17 00:00:00 2001 From: Pradeep Sawlani Date: Thu, 31 Oct 2024 19:54:48 -0700 Subject: [PATCH 017/656] ActivityManagerService: Add method to set media fgs inactive. Changes adds a new method in activity manager service to set foreground service of type media playback inactive. This method is expected to be called from MediaSessionService when media session is "user-disengaged" for certain amount of time (>10mins). This method will move foreground service to background and allowing application to be subject to other system policies to save resources. Flag: com.android.media.flags.enable_notifying_activity_manager_with_media_session_status_change BUG: 281762171 Test: atest cts/tests/app/src/android/app/cts/ActivityManagerNotifyMediaFGSTypeTest.java Change-Id: If4e60dabeec6cc595703c0d07369d7526fe8d862 --- .../android/app/ActivityManagerInternal.java | 14 +++++++ .../com/android/server/am/ActiveServices.java | 41 +++++++++++++++++++ .../server/am/ActivityManagerService.java | 9 ++++ 3 files changed, 64 insertions(+) diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index f80121d0c9b6..d9f8d33f0545 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1149,6 +1149,20 @@ public abstract class ActivityManagerInternal { */ public abstract void stopForegroundServiceDelegate(@NonNull ServiceConnection connection); + /** + * Notifies that a media foreground service associated with a media session has + * transitioned to a "user-disengaged" state. + * Upon receiving this notification, service may be removed from the foreground state. It + * should only be called by {@link com.android.server.media.MediaSessionService} + * + * @param packageName The package name of the app running the media foreground service. + * @param userId The user ID associated with the foreground service. + * @param notificationId The ID of the media notification associated with the foreground + * service. + */ + public abstract void notifyInactiveMediaForegroundService(@NonNull String packageName, + @UserIdInt int userId, int notificationId); + /** * Same as {@link android.app.IActivityManager#startProfile(int userId)}, but it would succeed * even if the profile is disabled - it should only be called by diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 3f540ad43da1..ab3ab159ba12 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -115,6 +115,7 @@ import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_ import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM; import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__REQUEST_TYPE__BIND; import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__REQUEST_TYPE__START; +import static com.android.media.flags.Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU; @@ -9319,6 +9320,46 @@ public final class ActiveServices { } } + /** + * Handles notifications from MediaSessionService about inactive media foreground services. + * This method evaluates the provided information and determines whether to stop the + * corresponding foreground service. + * + * @param packageName The package name of the app running the foreground service. + * @param userId The user ID associated with the foreground service. + * @param notificationId The ID of the media notification associated with the foreground + * service. + */ + void notifyInactiveMediaForegroundServiceLocked(@NonNull String packageName, + @UserIdInt int userId, int notificationId) { + if (!enableNotifyingActivityManagerWithMediaSessionStatusChange()) { + return; + } + + final ServiceMap smap = mServiceMap.get(userId); + if (smap == null) { + return; + } + final int serviceSize = smap.mServicesByInstanceName.size(); + for (int i = 0; i < serviceSize; i++) { + final ServiceRecord sr = smap.mServicesByInstanceName.valueAt(i); + if (sr.appInfo.packageName.equals(packageName) && sr.isForeground) { + if (sr.foregroundServiceType + == ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + && sr.foregroundId == notificationId) { + if (DEBUG_FOREGROUND_SERVICE) { + Slog.d(TAG, "Forcing media foreground service to background for package " + + packageName); + } + setServiceForegroundInnerLocked(sr, /* id */ 0, + /* notification */ null, /* flags */ 0, + /* foregroundServiceType */ 0, /* callingUidStart */ 0); + } + } + } + } + + private static void getClientPackages(ServiceRecord sr, ArraySet output) { var connections = sr.getConnections(); for (int conni = connections.size() - 1; conni >= 0; conni--) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1c3569dd52d0..54f84aed3718 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -17910,6 +17910,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void notifyInactiveMediaForegroundService(@NonNull String packageName, + @UserIdInt int userId, int notificationId) { + synchronized (ActivityManagerService.this) { + mServices.notifyInactiveMediaForegroundServiceLocked(packageName, userId, + notificationId); + } + } + @Override public ArraySet getClientPackages(String servicePackageName) { synchronized (ActivityManagerService.this) { -- GitLab From 882ec1a081d7e58492ecd1f35e370ed7d4f2f386 Mon Sep 17 00:00:00 2001 From: davidct Date: Tue, 17 Sep 2024 08:00:40 +0000 Subject: [PATCH 018/656] Propogate the BcSmartspaceConfigPlugin to the BcSmartspaceDataPlugin in order to flag ViewPager2. Bug: 259566300 Flag: com.android.systemui.smartspace_viewpager2 Test: Tested manually Test: test_gradle_build.sh - failing from b/365968705 Change-Id: I526360b1b531e479d052b80b4b94f2bcce645d6d --- .../plugins/BcSmartspaceDataPlugin.java | 9 + .../LockscreenSmartspaceController.kt | 422 +++++++++--------- 2 files changed, 221 insertions(+), 210 deletions(-) diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java index 074277c55c88..dcb15a7cd9aa 100644 --- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java +++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java @@ -103,6 +103,15 @@ public interface BcSmartspaceDataPlugin extends Plugin { void onSmartspaceTargetsUpdated(List targets); } + /** + * Sets {@link BcSmartspaceConfigPlugin}. + * + * TODO: b/259566300 - Remove once isViewPager2Enabled is fully rolled out + */ + default void registerConfigProvider(BcSmartspaceConfigPlugin configProvider) { + throw new UnsupportedOperationException("Not implemented by " + getClass()); + } + /** View to which this plugin can be registered, in order to get updates. */ interface SmartspaceView { void registerDataProvider(BcSmartspaceDataPlugin plugin); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index a24f2672f251..85cd50565f88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -83,39 +83,36 @@ import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Named - /** Controller for managing the smartspace view on the lockscreen */ @SysUISingleton class LockscreenSmartspaceController @Inject constructor( - private val context: Context, - private val featureFlags: FeatureFlags, - private val activityStarter: ActivityStarter, - private val falsingManager: FalsingManager, - private val systemClock: SystemClock, - private val secureSettings: SecureSettings, - private val userTracker: UserTracker, - private val contentResolver: ContentResolver, - private val configurationController: ConfigurationController, - private val statusBarStateController: StatusBarStateController, - private val deviceProvisionedController: DeviceProvisionedController, - private val bypassController: KeyguardBypassController, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val wakefulnessLifecycle: WakefulnessLifecycle, - private val smartspaceViewModelFactory: SmartspaceViewModel.Factory, - private val dumpManager: DumpManager, - private val execution: Execution, - @Main private val uiExecutor: Executor, - @Background private val bgExecutor: Executor, - @Main private val handler: Handler, - @Background private val bgHandler: Handler, - @Named(DATE_SMARTSPACE_DATA_PLUGIN) - optionalDatePlugin: Optional, - @Named(WEATHER_SMARTSPACE_DATA_PLUGIN) - optionalWeatherPlugin: Optional, - optionalPlugin: Optional, - optionalConfigPlugin: Optional, + private val context: Context, + private val featureFlags: FeatureFlags, + private val activityStarter: ActivityStarter, + private val falsingManager: FalsingManager, + private val systemClock: SystemClock, + private val secureSettings: SecureSettings, + private val userTracker: UserTracker, + private val contentResolver: ContentResolver, + private val configurationController: ConfigurationController, + private val statusBarStateController: StatusBarStateController, + private val deviceProvisionedController: DeviceProvisionedController, + private val bypassController: KeyguardBypassController, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val wakefulnessLifecycle: WakefulnessLifecycle, + private val smartspaceViewModelFactory: SmartspaceViewModel.Factory, + private val dumpManager: DumpManager, + private val execution: Execution, + @Main private val uiExecutor: Executor, + @Background private val bgExecutor: Executor, + @Main private val handler: Handler, + @Background private val bgHandler: Handler, + @Named(DATE_SMARTSPACE_DATA_PLUGIN) optionalDatePlugin: Optional, + @Named(WEATHER_SMARTSPACE_DATA_PLUGIN) optionalWeatherPlugin: Optional, + optionalPlugin: Optional, + optionalConfigPlugin: Optional, ) : Dumpable { companion object { private const val TAG = "LockscreenSmartspaceController" @@ -135,11 +132,9 @@ constructor( // Smartspace can be used on multiple displays, such as when the user casts their screen @VisibleForTesting var smartspaceViews = mutableSetOf() - private var regionSamplers = - mutableMapOf() + private var regionSamplers = mutableMapOf() - private val regionSamplingEnabled = - featureFlags.isEnabled(Flags.REGION_SAMPLING) + private val regionSamplingEnabled = featureFlags.isEnabled(Flags.REGION_SAMPLING) private var isRegionSamplersCreated = false private var showNotifications = false private var showSensitiveContentForCurrentUser = false @@ -157,119 +152,130 @@ constructor( // how we test color updates when theme changes (See testThemeChangeUpdatesTextColor). // TODO: Move logic into SmartspaceView - var stateChangeListener = object : View.OnAttachStateChangeListener { - override fun onViewAttachedToWindow(v: View) { - (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled) - smartspaceViews.add(v as SmartspaceView) - - connectSession() - - updateTextColorFromWallpaper() - statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) - - if (regionSamplingEnabled && (!regionSamplers.containsKey(v))) { - var regionSampler = RegionSampler( - v as View, - uiExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ) { updateTextColorFromRegionSampler() } - initializeTextColors(regionSampler) - regionSamplers[v] = regionSampler - regionSampler.startRegionSampler() + var stateChangeListener = + object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled) + smartspaceViews.add(v as SmartspaceView) + + connectSession() + + updateTextColorFromWallpaper() + statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) + + if (regionSamplingEnabled && (!regionSamplers.containsKey(v))) { + var regionSampler = + RegionSampler( + v as View, + uiExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ) { + updateTextColorFromRegionSampler() + } + initializeTextColors(regionSampler) + regionSamplers[v] = regionSampler + regionSampler.startRegionSampler() + } } - } - override fun onViewDetachedFromWindow(v: View) { - smartspaceViews.remove(v as SmartspaceView) + override fun onViewDetachedFromWindow(v: View) { + smartspaceViews.remove(v as SmartspaceView) - regionSamplers[v]?.stopRegionSampler() - regionSamplers.remove(v as SmartspaceView) + regionSamplers[v]?.stopRegionSampler() + regionSamplers.remove(v as SmartspaceView) - if (smartspaceViews.isEmpty()) { - disconnect() + if (smartspaceViews.isEmpty()) { + disconnect() + } } } - } - private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets -> - execution.assertIsMainThread() + private val sessionListener = + SmartspaceSession.OnTargetsAvailableListener { targets -> + execution.assertIsMainThread() - // The weather data plugin takes unfiltered targets and performs the filtering internally. - weatherPlugin?.onTargetsAvailable(targets) - - val now = Instant.ofEpochMilli(systemClock.currentTimeMillis()) - val weatherTarget = targets.find { t -> - t.featureType == SmartspaceTarget.FEATURE_WEATHER && - now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) && - now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis)) - } - if (weatherTarget != null) { - val clickIntent = weatherTarget.headerAction?.intent - val weatherData = weatherTarget.baseAction?.extras?.let { extras -> - WeatherData.fromBundle( - extras, - ) { _ -> - if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { - activityStarter.startActivity( - clickIntent, - true, /* dismissShade */ - null, - false) - } + // The weather data plugin takes unfiltered targets and performs the filtering + // internally. + weatherPlugin?.onTargetsAvailable(targets) + + val now = Instant.ofEpochMilli(systemClock.currentTimeMillis()) + val weatherTarget = + targets.find { t -> + t.featureType == SmartspaceTarget.FEATURE_WEATHER && + now.isAfter(Instant.ofEpochMilli(t.creationTimeMillis)) && + now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis)) } - } + if (weatherTarget != null) { + val clickIntent = weatherTarget.headerAction?.intent + val weatherData = + weatherTarget.baseAction?.extras?.let { extras -> + WeatherData.fromBundle(extras) { _ -> + if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { + activityStarter.startActivity( + clickIntent, + true, /* dismissShade */ + null, + false, + ) + } + } + } - if (weatherData != null) { - keyguardUpdateMonitor.sendWeatherData(weatherData) + if (weatherData != null) { + keyguardUpdateMonitor.sendWeatherData(weatherData) + } } - } - val filteredTargets = targets.filter(::filterSmartspaceTarget) + val filteredTargets = targets.filter(::filterSmartspaceTarget) - synchronized(recentSmartspaceData) { - recentSmartspaceData.offerLast(filteredTargets) - if (recentSmartspaceData.size > MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP) { - recentSmartspaceData.pollFirst() + synchronized(recentSmartspaceData) { + recentSmartspaceData.offerLast(filteredTargets) + if (recentSmartspaceData.size > MAX_RECENT_SMARTSPACE_DATA_FOR_DUMP) { + recentSmartspaceData.pollFirst() + } } - } - - plugin?.onTargetsAvailable(filteredTargets) - } - private val userTrackerCallback = object : UserTracker.Callback { - override fun onUserChanged(newUser: Int, userContext: Context) { - execution.assertIsMainThread() - reloadSmartspace() + plugin?.onTargetsAvailable(filteredTargets) } - } - private val settingsObserver = object : ContentObserver(handler) { - override fun onChange(selfChange: Boolean, uri: Uri?) { - execution.assertIsMainThread() - reloadSmartspace() + private val userTrackerCallback = + object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + execution.assertIsMainThread() + reloadSmartspace() + } } - } - private val configChangeListener = object : ConfigurationController.ConfigurationListener { - override fun onThemeChanged() { - execution.assertIsMainThread() - updateTextColorFromWallpaper() + private val settingsObserver = + object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean, uri: Uri?) { + execution.assertIsMainThread() + reloadSmartspace() + } } - } - private val statusBarStateListener = object : StatusBarStateController.StateListener { - override fun onDozeAmountChanged(linear: Float, eased: Float) { - execution.assertIsMainThread() - smartspaceViews.forEach { it.setDozeAmount(eased) } + private val configChangeListener = + object : ConfigurationController.ConfigurationListener { + override fun onThemeChanged() { + execution.assertIsMainThread() + updateTextColorFromWallpaper() + } } - override fun onDozingChanged(isDozing: Boolean) { - execution.assertIsMainThread() - smartspaceViews.forEach { it.setDozing(isDozing) } + private val statusBarStateListener = + object : StatusBarStateController.StateListener { + override fun onDozeAmountChanged(linear: Float, eased: Float) { + execution.assertIsMainThread() + smartspaceViews.forEach { it.setDozeAmount(eased) } + } + + override fun onDozingChanged(isDozing: Boolean) { + execution.assertIsMainThread() + smartspaceViews.forEach { it.setDozing(isDozing) } + } } - } private val deviceProvisionedListener = object : DeviceProvisionedController.DeviceProvisionedListener { @@ -313,11 +319,8 @@ constructor( val isWeatherEnabled: Boolean get() { val showWeather = - secureSettings.getIntForUser( - LOCK_SCREEN_WEATHER_ENABLED, - 1, - userTracker.userId, - ) == 1 + secureSettings.getIntForUser(LOCK_SCREEN_WEATHER_ENABLED, 1, userTracker.userId) == + 1 return showWeather } @@ -326,9 +329,7 @@ constructor( smartspaceViews.forEach { it.setKeyguardBypassEnabled(bypassEnabled) } } - /** - * Constructs the date view and connects it to the smartspace service. - */ + /** Constructs the date view and connects it to the smartspace service. */ fun buildAndConnectDateView(parent: ViewGroup): View? { execution.assertIsMainThread() @@ -343,16 +344,14 @@ constructor( buildView( surfaceName = SmartspaceViewModel.SURFACE_DATE_VIEW, parent = parent, - plugin = datePlugin + plugin = datePlugin, ) connectSession() return view } - /** - * Constructs the weather view and connects it to the smartspace service. - */ + /** Constructs the weather view and connects it to the smartspace service. */ fun buildAndConnectWeatherView(parent: ViewGroup): View? { execution.assertIsMainThread() @@ -367,16 +366,14 @@ constructor( buildView( surfaceName = SmartspaceViewModel.SURFACE_WEATHER_VIEW, parent = parent, - plugin = weatherPlugin + plugin = weatherPlugin, ) connectSession() return view } - /** - * Constructs the smartspace view and connects it to the smartspace service. - */ + /** Constructs the smartspace view and connects it to the smartspace service. */ fun buildAndConnectView(parent: ViewGroup): View? { execution.assertIsMainThread() @@ -384,12 +381,14 @@ constructor( throw RuntimeException("Cannot build view when not enabled") } + configPlugin?.let { plugin?.registerConfigProvider(it) } + val view = buildView( surfaceName = SmartspaceViewModel.SURFACE_GENERAL_VIEW, parent = parent, plugin = plugin, - configPlugin = configPlugin + configPlugin = configPlugin, ) connectSession() @@ -400,7 +399,7 @@ constructor( surfaceName: String, parent: ViewGroup, plugin: BcSmartspaceDataPlugin?, - configPlugin: BcSmartspaceConfigPlugin? = null + configPlugin: BcSmartspaceConfigPlugin? = null, ): View? { if (plugin == null) { return null @@ -413,37 +412,41 @@ constructor( ssView.setTimeChangedDelegate(SmartspaceTimeChangedDelegate(keyguardUpdateMonitor)) ssView.registerDataProvider(plugin) - ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { - override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) { - if (showOnLockscreen) { - activityStarter.startActivity( + ssView.setIntentStarter( + object : BcSmartspaceDataPlugin.IntentStarter { + override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) { + if (showOnLockscreen) { + activityStarter.startActivity( intent, true, /* dismissShade */ // launch animator - looks bad with the transparent smartspace bg null, - true - ) - } else { - activityStarter.postStartActivityDismissingKeyguard(intent, 0) + true, + ) + } else { + activityStarter.postStartActivityDismissingKeyguard(intent, 0) + } } - } - override fun startPendingIntent( + override fun startPendingIntent( view: View, pi: PendingIntent, - showOnLockscreen: Boolean - ) { - if (showOnLockscreen) { - val options = ActivityOptions.makeBasic() - .setPendingIntentBackgroundActivityStartMode( - ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED) - .toBundle() - pi.send(options) - } else { - activityStarter.postStartActivityDismissingKeyguard(pi) + showOnLockscreen: Boolean, + ) { + if (showOnLockscreen) { + val options = + ActivityOptions.makeBasic() + .setPendingIntentBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + ) + .toBundle() + pi.send(options) + } else { + activityStarter.postStartActivityDismissingKeyguard(pi) + } } } - }) + ) ssView.setFalsingManager(falsingManager) ssView.setKeyguardBypassEnabled(bypassController.bypassEnabled) return (ssView as View).apply { @@ -452,10 +455,7 @@ constructor( if (smartspaceLockscreenViewmodel()) { val viewModel = smartspaceViewModelFactory.create(surfaceName) - SmartspaceViewBinder.bind( - smartspaceView = ssView, - viewModel = viewModel, - ) + SmartspaceViewBinder.bind(smartspaceView = ssView, viewModel = viewModel) } } } @@ -473,34 +473,41 @@ constructor( // Only connect after the device is fully provisioned to avoid connection caching // issues - if (!deviceProvisionedController.isDeviceProvisioned() || - !deviceProvisionedController.isCurrentUserSetup()) { + if ( + !deviceProvisionedController.isDeviceProvisioned() || + !deviceProvisionedController.isCurrentUserSetup() + ) { return } - val newSession = userSmartspaceManager?.createSmartspaceSession( - SmartspaceConfig.Builder( - userTracker.userContext, BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD - ).build() + val newSession = + userSmartspaceManager?.createSmartspaceSession( + SmartspaceConfig.Builder( + userTracker.userContext, + BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD, + ) + .build() + ) + Log.d( + TAG, + "Starting smartspace session for " + BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD, ) - Log.d(TAG, "Starting smartspace session for " + - BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) newSession?.addOnTargetsAvailableListener(uiExecutor, sessionListener) this.session = newSession deviceProvisionedController.removeCallback(deviceProvisionedListener) userTracker.addCallback(userTrackerCallback, uiExecutor) contentResolver.registerContentObserver( - secureSettings.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), - true, - settingsObserver, - UserHandle.USER_ALL + secureSettings.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS), + true, + settingsObserver, + UserHandle.USER_ALL, ) contentResolver.registerContentObserver( - secureSettings.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS), - true, - settingsObserver, - UserHandle.USER_ALL + secureSettings.getUriFor(LOCK_SCREEN_SHOW_NOTIFICATIONS), + true, + settingsObserver, + UserHandle.USER_ALL, ) configurationController.addCallback(configChangeListener) statusBarStateController.addCallback(statusBarStateListener) @@ -522,16 +529,12 @@ constructor( smartspaceViews.forEach { it.setSplitShadeEnabled(enabled) } } - /** - * Requests the smartspace session for an update. - */ + /** Requests the smartspace session for an update. */ fun requestSmartspaceUpdate() { session?.requestSmartspaceUpdate() } - /** - * Disconnects the smartspace view from the smartspace service and cleans up any resources. - */ + /** Disconnects the smartspace view from the smartspace service and cleans up any resources. */ fun disconnect() { if (!smartspaceViews.isEmpty()) return if (suppressDisconnects) return @@ -638,7 +641,7 @@ constructor( private fun updateTextColorFromWallpaper() { if (!regionSamplingEnabled || regionSamplers.isEmpty()) { val wallpaperTextColor = - Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) + Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor) smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) } } else { updateTextColorFromRegionSampler() @@ -646,26 +649,25 @@ constructor( } private fun reloadSmartspace() { - showNotifications = secureSettings.getIntForUser( - LOCK_SCREEN_SHOW_NOTIFICATIONS, - 0, - userTracker.userId - ) == 1 - - showSensitiveContentForCurrentUser = secureSettings.getIntForUser( - LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, - 0, - userTracker.userId - ) == 1 + showNotifications = + secureSettings.getIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userTracker.userId) == 1 - managedUserHandle = getWorkProfileUser() - val managedId = managedUserHandle?.identifier - if (managedId != null) { - showSensitiveContentForManagedUser = secureSettings.getIntForUser( + showSensitiveContentForCurrentUser = + secureSettings.getIntForUser( LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, - managedId + userTracker.userId, ) == 1 + + managedUserHandle = getWorkProfileUser() + val managedId = managedUserHandle?.identifier + if (managedId != null) { + showSensitiveContentForManagedUser = + secureSettings.getIntForUser( + LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, + 0, + managedId, + ) == 1 } session?.requestSmartspaceUpdate() @@ -682,9 +684,7 @@ constructor( override fun dump(pw: PrintWriter, args: Array) { pw.asIndenting().run { - printCollection("Region Samplers", regionSamplers.values) { - it.dump(this) - } + printCollection("Region Samplers", regionSamplers.values) { it.dump(this) } } pw.println("Recent BC Smartspace Targets (most recent first)") @@ -707,15 +707,17 @@ constructor( private val keyguardUpdateMonitor: KeyguardUpdateMonitor ) : TimeChangedDelegate { private var keyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback? = null + override fun register(callback: Runnable) { if (keyguardUpdateMonitorCallback != null) { unregister() } - keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { - override fun onTimeChanged() { - callback.run() + keyguardUpdateMonitorCallback = + object : KeyguardUpdateMonitorCallback() { + override fun onTimeChanged() { + callback.run() + } } - } keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) callback.run() } -- GitLab From a5aa7db96da52a91feb2bcec03a7e3e3a6a90734 Mon Sep 17 00:00:00 2001 From: Hidayat Khan Date: Wed, 13 Nov 2024 16:31:10 +0000 Subject: [PATCH 019/656] Enable Satellite Notifications by default in telephony config Bug: 378782898 Test: config change Flag: EXEMPT config change Change-Id: Id2a9e6f7ff8663d0888d60200393deaced1bb3af --- core/res/res/values/config_telephony.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 31e9913dd988..4385395acbcd 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -478,7 +478,7 @@ - false + true -- GitLab From b90b26cbd66597d9bb167cf490dff677286a91bd Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Thu, 14 Nov 2024 06:56:23 +0000 Subject: [PATCH 020/656] Make AppIconCacheManager instance volatile to prevent thread caching issue Bug: 378525195 Test: atest Flag: EXEMPT bug fix Change-Id: I09c8eedfe05ef17affa910d6cfbc940bb4e29115 --- .../applications/AppIconCacheManager.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java index c0117b952beb..30ce13bf3b00 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java @@ -22,6 +22,7 @@ import android.os.UserHandle; import android.util.Log; import android.util.LruCache; +import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; /** @@ -33,7 +34,7 @@ public class AppIconCacheManager { @VisibleForTesting static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb(); private static final String DELIMITER = ":"; - private static AppIconCacheManager sAppIconCacheManager; + private static volatile AppIconCacheManager sAppIconCacheManager; private final LruCache mDrawableCache; private AppIconCacheManager() { @@ -52,11 +53,18 @@ public class AppIconCacheManager { /** * Get an {@link AppIconCacheManager} instance. */ - public static synchronized AppIconCacheManager getInstance() { - if (sAppIconCacheManager == null) { - sAppIconCacheManager = new AppIconCacheManager(); + public static @NonNull AppIconCacheManager getInstance() { + AppIconCacheManager result = sAppIconCacheManager; + if (result == null) { + synchronized (AppIconCacheManager.class) { + result = sAppIconCacheManager; + if (result == null) { + result = new AppIconCacheManager(); + sAppIconCacheManager = result; + } + } } - return sAppIconCacheManager; + return result; } /** @@ -118,7 +126,7 @@ public class AppIconCacheManager { * * @see android.content.ComponentCallbacks2#onTrimMemory(int) */ - public void trimMemory(int level) { + public static void trimMemory(int level) { if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) { // Time to clear everything if (sAppIconCacheManager != null) { -- GitLab From c4d316f6e43ef5d146748ce902e18a8d657b810f Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Tue, 1 Oct 2024 02:11:28 +0000 Subject: [PATCH 021/656] Expose VCN APIs for framework code As the first step of mainlining VCN, VCN will be moved from framework to its own java_sdk_library and hidden APIs will no longer be accessible to framework code. This CL exposes these classes, methods and constants as @SystemApi so that framework code can continue accessing them. Note, these APIs are temporarily exposed from framework.jar. Followup CLs will move them to the framework-connectivity-b.jar Bug: 376339506 Test: atest FrameworksVcnTests && atest CtsVcnTestCases Flag: android.net.vcn.mainline_vcn_module_api API-Coverage-Bug: 376553318 Change-Id: Ibf8a2d32ec3eae73cfb279dde2a0c74e4b4c5b33 Merged-In: Ibf8a2d32ec3eae73cfb279dde2a0c74e4b4c5b33 --- core/api/current.txt | 1 + core/api/module-lib-current.txt | 23 ++++ .../android/app/SystemServiceRegistry.java | 4 +- ...nectivityFrameworkInitializerBaklava.java} | 18 +++- .../net/vcn/VcnGatewayConnectionConfig.java | 19 +++- .../android/net/vcn/VcnTransportInfo.java | 100 +++++++++++++++--- 6 files changed, 140 insertions(+), 25 deletions(-) rename core/java/android/net/{vcn/VcnFrameworkInitializer.java => ConnectivityFrameworkInitializerBaklava.java} (87%) diff --git a/core/api/current.txt b/core/api/current.txt index 9a5e0de1f9b0..8d351982780a 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -29518,6 +29518,7 @@ package android.net.vcn { method @NonNull public java.util.List getVcnUnderlyingNetworkPriorities(); method public boolean hasGatewayOption(int); method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled(); + field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; // 0xffffffff field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0 } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 0b891f6e0a71..529e7f9b0bd3 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -260,6 +260,10 @@ package android.media.session { package android.net { + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class ConnectivityFrameworkInitializerBaklava { + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static void registerServiceWrappers(); + } + public class LocalSocket implements java.io.Closeable { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } @@ -319,6 +323,25 @@ package android.net.netstats { } +package android.net.vcn { + + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class VcnTransportInfo implements android.os.Parcelable android.net.TransportInfo { + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int describeContents(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public long getApplicableRedactions(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int getMinUdpPort4500NatTimeoutSeconds(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.TransportInfo makeCopy(long); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final class VcnTransportInfo.Builder { + ctor @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public VcnTransportInfo.Builder(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo build(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int); + } + +} + package android.nfc { public class NfcServiceManager { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 093dad6ec165..918ae1cb8ab6 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -150,6 +150,7 @@ import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.nearby.NearbyFrameworkInitializer; import android.net.ConnectivityFrameworkInitializer; +import android.net.ConnectivityFrameworkInitializerBaklava; import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.INetworkPolicyManager; import android.net.IPacProxyManager; @@ -160,7 +161,6 @@ import android.net.NetworkWatchlistManager; import android.net.PacProxyManager; import android.net.TetheringManager; import android.net.VpnManager; -import android.net.vcn.VcnFrameworkInitializer; import android.net.wifi.WifiFrameworkInitializer; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; @@ -1691,7 +1691,7 @@ public final class SystemServiceRegistry { OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers(); DeviceLockFrameworkInitializer.registerServiceWrappers(); VirtualizationFrameworkInitializer.registerServiceWrappers(); - VcnFrameworkInitializer.registerServiceWrappers(); + ConnectivityFrameworkInitializerBaklava.registerServiceWrappers(); if (com.android.server.telecom.flags.Flags.telecomMainlineBlockedNumbersManager()) { ProviderFrameworkInitializer.registerServiceWrappers(); diff --git a/core/java/android/net/vcn/VcnFrameworkInitializer.java b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java similarity index 87% rename from core/java/android/net/vcn/VcnFrameworkInitializer.java rename to core/java/android/net/ConnectivityFrameworkInitializerBaklava.java index 8cb213b306be..1f0fa92d7976 100644 --- a/core/java/android/net/vcn/VcnFrameworkInitializer.java +++ b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java @@ -14,15 +14,21 @@ * limitations under the License. */ -package android.net.vcn; +package android.net; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; + +import android.annotation.FlaggedApi; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.SystemServiceRegistry; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Context; import android.content.pm.PackageManager; +import android.net.vcn.IVcnManagementService; +import android.net.vcn.VcnManager; import android.os.Build; import android.os.SystemProperties; @@ -31,8 +37,9 @@ import android.os.SystemProperties; * * @hide */ -// TODO: Expose it as @SystemApi(client = MODULE_LIBRARIES) -public final class VcnFrameworkInitializer { +@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class ConnectivityFrameworkInitializerBaklava { /** * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers @@ -55,7 +62,7 @@ public final class VcnFrameworkInitializer { */ private static final int VENDOR_API_FOR_ANDROID_V = 202404; - private VcnFrameworkInitializer() {} + private ConnectivityFrameworkInitializerBaklava() {} // Suppressing AndroidFrameworkCompatChange because we're querying vendor // partition SDK level, not application's target SDK version (which BTW we @@ -86,7 +93,10 @@ public final class VcnFrameworkInitializer { * * @throws IllegalStateException if this is called anywhere besides {@link * SystemServiceRegistry}. + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static void registerServiceWrappers() { SystemServiceRegistry.registerContextAwareService( VcnManager.VCN_MANAGEMENT_SERVICE_STRING, diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index af93c964a8ba..3219ce81c256 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; @@ -82,7 +83,15 @@ import java.util.concurrent.TimeUnit; * */ public final class VcnGatewayConnectionConfig { - /** @hide */ + /** + * Minimum NAT timeout not set. + * + *

When the timeout is not set, the device will automatically choose a keepalive interval and + * may reduce the keepalive frequency for power-optimization. + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + // This constant does not represent a minimum value. It indicates the value is not configured. + @SuppressLint("MinMaxConstant") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; /** @hide */ @@ -773,7 +782,7 @@ public final class VcnGatewayConnectionConfig { * * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN - * Gateway. + * Gateway; or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET} to clear this value. * @return this {@link Builder} instance, for chaining */ @NonNull @@ -781,8 +790,10 @@ public final class VcnGatewayConnectionConfig { @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( - minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, - "Timeout must be at least 120s"); + minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + || minUdpPort4500NatTimeoutSeconds + >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return this; diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java index 1fc91eea3138..3638429f33fb 100644 --- a/core/java/android/net/vcn/VcnTransportInfo.java +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -17,13 +17,16 @@ package android.net.vcn; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.NetworkCapabilities; import android.net.TransportInfo; import android.net.wifi.WifiInfo; @@ -52,23 +55,29 @@ import java.util.Objects; * @hide */ // TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore -public class VcnTransportInfo implements TransportInfo, Parcelable { +@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; private final int mMinUdpPort4500NatTimeoutSeconds; + /** @hide */ public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { this(wifiInfo, INVALID_SUBSCRIPTION_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET); } + /** @hide */ public VcnTransportInfo(@NonNull WifiInfo wifiInfo, int minUdpPort4500NatTimeoutSeconds) { this(wifiInfo, INVALID_SUBSCRIPTION_ID, minUdpPort4500NatTimeoutSeconds); } + /** @hide */ public VcnTransportInfo(int subId) { this(null /* wifiInfo */, subId, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET); } + /** @hide */ public VcnTransportInfo(int subId, int minUdpPort4500NatTimeoutSeconds) { this(null /* wifiInfo */, subId, minUdpPort4500NatTimeoutSeconds); } @@ -86,6 +95,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { *

If the underlying Network for the associated VCN is Cellular, returns null. * * @return the WifiInfo if there is an underlying WiFi connection, else null. + * @hide */ @Nullable public WifiInfo getWifiInfo() { @@ -100,17 +110,27 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { * * @return the Subscription ID if a cellular underlying Network is present, else {@link * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @hide */ public int getSubId() { return mSubId; } /** - * Get the VCN provided UDP port 4500 NAT timeout + * Get the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping. * - * @return the UDP 4500 NAT timeout, or + *

To ensure uninterrupted connectivity, the device must send keepalive packets before the + * timeout. Failure to do so may result in the mapping being cleared and connection termination. + * This value is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, + * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. + * + * @return the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping, or * VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET if not set. + * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int) + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public int getMinUdpPort4500NatTimeoutSeconds() { return mMinUdpPort4500NatTimeoutSeconds; } @@ -129,12 +149,21 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { && mMinUdpPort4500NatTimeoutSeconds == that.mMinUdpPort4500NatTimeoutSeconds; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public int describeContents() { return 0; } + /** @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override @NonNull public TransportInfo makeCopy(long redactions) { @@ -149,6 +178,9 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { mMinUdpPort4500NatTimeoutSeconds); } + /** @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public long getApplicableRedactions() { long redactions = REDACT_FOR_NETWORK_SETTINGS; @@ -161,7 +193,13 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return redactions; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mSubId); @@ -174,7 +212,13 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return "VcnTransportInfo { mWifiInfo = " + mWifiInfo + ", mSubId = " + mSubId + " }"; } - /** Implement the Parcelable interface */ + /** + * Implement the Parcelable interface + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final @NonNull Creator CREATOR = new Creator() { public VcnTransportInfo createFromParcel(Parcel in) { @@ -201,37 +245,63 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { } }; - /** This class can be used to construct a {@link VcnTransportInfo}. */ + /** + * This class can be used to construct a {@link VcnTransportInfo}. + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final class Builder { private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; - /** Construct Builder */ + /** + * Construct Builder + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public Builder() {} /** - * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout. + * Set the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping. * *

This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. * - * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN - * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN - * Gateway. + * @param minUdpPort4500NatTimeoutSeconds the minimum duration that the VCN Gateway + * guarantees to preserve a NAT mapping, or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET} + * to clear this value. To ensure uninterrupted connectivity, the device must send + * keepalive packets within this interval. Failure to do so may result in the mapping + * being cleared and connection termination. * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int) + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public Builder setMinUdpPort4500NatTimeoutSeconds( @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( - minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, - "Timeout must be at least 120s"); + minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + || minUdpPort4500NatTimeoutSeconds + >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return Builder.this; } - /** Build a VcnTransportInfo instance */ + /** + * Build a VcnTransportInfo instance + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public VcnTransportInfo build() { return new VcnTransportInfo( -- GitLab From 05d1144323b190c0946b663f415c84184a094e04 Mon Sep 17 00:00:00 2001 From: lijilou Date: Thu, 14 Nov 2024 15:06:16 +0800 Subject: [PATCH 022/656] BiometricScheduler: fix the NPE problem in startWatchdog method. Binder thread call the startWatchdog,but another put the mCurrentOperation to null when when assigning value to the operation object.This may cause NPE problems. Test: OEM monkey test Flag: EXEMPT bugfix Bug: 379003855 Change-Id: Ib44e3214c891dc4e975d34e1c4213faea14131c6 --- .../server/biometrics/sensors/BiometricScheduler.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 82d5d4d8141d..e8786be4d8e6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -677,10 +677,11 @@ public class BiometricScheduler { * Start the timeout for the watchdog. */ public void startWatchdog() { - if (mCurrentOperation == null) { + final BiometricSchedulerOperation operation = mCurrentOperation; + if (operation == null) { + Slog.e(TAG, "Current operation is null,no need to start watchdog"); return; } - final BiometricSchedulerOperation operation = mCurrentOperation; mHandler.postDelayed(() -> { if (operation == mCurrentOperation && !operation.isFinished()) { Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count"); -- GitLab From 214032f16b529fc2cba735705d459156cb1ca9c8 Mon Sep 17 00:00:00 2001 From: Angela Wang Date: Thu, 14 Nov 2024 11:05:25 +0000 Subject: [PATCH 023/656] Ignore ASHA hisyncId grouping if the device supports CSIP If a device is added as another device's sub device, it'll be removed from the cached device list. This makes the device can't be correctly grouped as a CSIP set when CSIP is connected. We should ignore ASHA hisyncId grouping and leave it to be handled as a member of CSIP set. Flag: EXEMPT bugfix Bug: 376022366 Test: atest HearingAidDeviceManagerTest Test: manually check the issue is resolved with real device Change-Id: I5897bcbd1915d03157743ba09e4092c6976736c3 --- .../settingslib/bluetooth/HearingAidDeviceManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index b3e48b26782e..fa28cf6c8a76 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -94,6 +94,14 @@ public class HearingAidDeviceManager { boolean setSubDeviceIfNeeded(CachedBluetoothDevice newDevice) { final long hiSyncId = newDevice.getHiSyncId(); if (isValidHiSyncId(hiSyncId)) { + // The remote device supports CSIP, the other ear should be processed as a member + // device. Ignore hiSyncId grouping from ASHA here. + if (newDevice.getProfiles().stream().anyMatch( + profile -> profile instanceof CsipSetCoordinatorProfile)) { + Log.w(TAG, "Skip ASHA grouping since this device supports CSIP"); + return false; + } + final CachedBluetoothDevice hearingAidDevice = getCachedDevice(hiSyncId); // Just add one of the hearing aids from a pair in the list that is shown in the UI. // Once there is another device with the same hiSyncId, to add new device as sub @@ -161,6 +169,7 @@ public class HearingAidDeviceManager { // device. Ignore hiSyncId grouping from ASHA here. if (cachedDevice.getProfiles().stream().anyMatch( profile -> profile instanceof CsipSetCoordinatorProfile)) { + Log.w(TAG, "Skip ASHA grouping since this device supports CSIP"); continue; } -- GitLab From a1d849fe651e5cd253d9ee777637aa40b1f798e8 Mon Sep 17 00:00:00 2001 From: Marvin Ramin Date: Thu, 14 Nov 2024 14:22:59 +0100 Subject: [PATCH 024/656] Use non-abbreviated units of time Bug: 377877538 Test: n/a Flag: EXEMPT refactor Change-Id: I173672c02c6ca5389eb297aca401e2d59e62d59f --- .../MediaProjectionManagerService.java | 16 ++++++++-------- .../MediaProjectionManagerServiceTest.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index fce008c23350..df5ecf872df4 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -1006,9 +1006,9 @@ public final class MediaProjectionManagerService extends SystemService // Host app has 5 minutes to begin using the token before it is invalid. // Some apps show a dialog for the user to interact with (selecting recording resolution) // before starting capture, but after requesting consent. - final long mDefaultTimeoutMs = Duration.ofMinutes(5).toMillis(); + final long mDefaultTimeoutMillis = Duration.ofMinutes(5).toMillis(); // The creation timestamp in milliseconds, measured by {@link SystemClock#uptimeMillis}. - private final long mCreateTimeMs; + private final long mCreateTimeMillis; public final int uid; public final String packageName; public final UserHandle userHandle; @@ -1017,7 +1017,7 @@ public final class MediaProjectionManagerService extends SystemService private final int mType; // Values for tracking token validity. // Timeout value to compare creation time against. - private final long mTimeoutMs = mDefaultTimeoutMs; + private final long mTimeoutMillis = mDefaultTimeoutMillis; private final int mDisplayId; private IMediaProjectionCallback mCallback; @@ -1048,7 +1048,7 @@ public final class MediaProjectionManagerService extends SystemService userHandle = new UserHandle(UserHandle.getUserId(uid)); mTargetSdkVersion = targetSdkVersion; mIsPrivileged = isPrivileged; - mCreateTimeMs = mClock.uptimeMillis(); + mCreateTimeMillis = mClock.uptimeMillis(); mActivityManagerInternal.notifyMediaProjectionEvent(uid, asBinder(), MEDIA_PROJECTION_TOKEN_EVENT_CREATED); mDisplayId = displayId; @@ -1209,7 +1209,7 @@ public final class MediaProjectionManagerService extends SystemService } } Slog.d(TAG, "Content Recording: handling stopping this projection token" - + " createTime= " + mCreateTimeMs + + " createTime= " + mCreateTimeMillis + " countStarts= " + mCountStarts); stopProjectionLocked(this); mToken.unlinkToDeath(mDeathEater, 0); @@ -1273,7 +1273,7 @@ public final class MediaProjectionManagerService extends SystemService } long getCreateTimeMillis() { - return mCreateTimeMs; + return mCreateTimeMillis; } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @@ -1281,8 +1281,8 @@ public final class MediaProjectionManagerService extends SystemService public boolean isValid() { isValid_enforcePermission(); synchronized (mLock) { - final long curMs = mClock.uptimeMillis(); - final boolean hasTimedOut = curMs - mCreateTimeMs > mTimeoutMs; + final long curMillis = mClock.uptimeMillis(); + final boolean hasTimedOut = curMillis - mCreateTimeMillis > mTimeoutMillis; final boolean virtualDisplayCreated = mVirtualDisplayId != INVALID_DISPLAY; final boolean isValid = !hasTimedOut && (mCountStarts <= 1) && !virtualDisplayCreated; diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index 510c2bcabad0..b2a7d20fb948 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -664,7 +664,7 @@ public class MediaProjectionManagerServiceTest { mClockInjector); MediaProjectionManagerService.MediaProjection projection = createProjectionPreconditions( service); - mClock.fastForward(projection.mDefaultTimeoutMs + 10); + mClock.fastForward(projection.mDefaultTimeoutMillis + 10); // Immediate timeout - so no longer valid. assertThat(projection.isValid()).isFalse(); -- GitLab From ffe15f981e08f388953c73d456961c894a0d9e06 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Tue, 1 Oct 2024 02:11:28 +0000 Subject: [PATCH 025/656] Expose VCN APIs for framework code As the first step of mainlining VCN, VCN will be moved from framework to its own java_sdk_library and hidden APIs will no longer be accessible to framework code. This CL exposes these classes, methods and constants as @SystemApi so that framework code can continue accessing them. Note, these APIs are temporarily exposed from framework.jar. Followup CLs will move them to the framework-connectivity-b.jar Bug: 376339506 Test: atest FrameworksVcnTests && atest CtsVcnTestCases Flag: android.net.vcn.mainline_vcn_module_api API-Coverage-Bug: 376553318 Change-Id: Ibf8a2d32ec3eae73cfb279dde2a0c74e4b4c5b33 Merged-In: Ibf8a2d32ec3eae73cfb279dde2a0c74e4b4c5b33 (cherry picked from commit 75fda3f4e8ba14107aef9152ed4c7693ccff422d) --- core/api/current.txt | 1 + core/api/module-lib-current.txt | 23 ++++ .../android/app/SystemServiceRegistry.java | 4 +- ...nectivityFrameworkInitializerBaklava.java} | 18 +++- .../net/vcn/VcnGatewayConnectionConfig.java | 19 +++- .../android/net/vcn/VcnTransportInfo.java | 100 +++++++++++++++--- 6 files changed, 140 insertions(+), 25 deletions(-) rename core/java/android/net/{vcn/VcnFrameworkInitializer.java => ConnectivityFrameworkInitializerBaklava.java} (87%) diff --git a/core/api/current.txt b/core/api/current.txt index f1db5eaa076f..b918d96b424d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -29564,6 +29564,7 @@ package android.net.vcn { method @NonNull public java.util.List getVcnUnderlyingNetworkPriorities(); method public boolean hasGatewayOption(int); method @FlaggedApi("android.net.vcn.safe_mode_config") public boolean isSafeModeEnabled(); + field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; // 0xffffffff field public static final int VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY = 0; // 0x0 } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index ae9f1faa288e..c5a7be313125 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -260,6 +260,10 @@ package android.media.session { package android.net { + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class ConnectivityFrameworkInitializerBaklava { + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static void registerServiceWrappers(); + } + public class LocalSocket implements java.io.Closeable { ctor public LocalSocket(@NonNull java.io.FileDescriptor); } @@ -319,6 +323,25 @@ package android.net.netstats { } +package android.net.vcn { + + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public final class VcnTransportInfo implements android.os.Parcelable android.net.TransportInfo { + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int describeContents(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public long getApplicableRedactions(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public int getMinUdpPort4500NatTimeoutSeconds(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.TransportInfo makeCopy(long); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public static final class VcnTransportInfo.Builder { + ctor @FlaggedApi("android.net.vcn.mainline_vcn_module_api") public VcnTransportInfo.Builder(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo build(); + method @FlaggedApi("android.net.vcn.mainline_vcn_module_api") @NonNull public android.net.vcn.VcnTransportInfo.Builder setMinUdpPort4500NatTimeoutSeconds(@IntRange(from=0x78) int); + } + +} + package android.net.wifi { public final class WifiMigration { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index d3d1477d3ffd..8f84ee879b1a 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -158,6 +158,7 @@ import android.media.tv.tunerresourcemanager.ITunerResourceManager; import android.media.tv.tunerresourcemanager.TunerResourceManager; import android.nearby.NearbyFrameworkInitializer; import android.net.ConnectivityFrameworkInitializer; +import android.net.ConnectivityFrameworkInitializerBaklava; import android.net.ConnectivityFrameworkInitializerTiramisu; import android.net.INetworkPolicyManager; import android.net.IPacProxyManager; @@ -168,7 +169,6 @@ import android.net.NetworkWatchlistManager; import android.net.PacProxyManager; import android.net.TetheringManager; import android.net.VpnManager; -import android.net.vcn.VcnFrameworkInitializer; import android.net.wifi.WifiFrameworkInitializer; import android.net.wifi.nl80211.WifiNl80211Manager; import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager; @@ -1754,7 +1754,7 @@ public final class SystemServiceRegistry { OnDevicePersonalizationFrameworkInitializer.registerServiceWrappers(); DeviceLockFrameworkInitializer.registerServiceWrappers(); VirtualizationFrameworkInitializer.registerServiceWrappers(); - VcnFrameworkInitializer.registerServiceWrappers(); + ConnectivityFrameworkInitializerBaklava.registerServiceWrappers(); if (com.android.server.telecom.flags.Flags.telecomMainlineBlockedNumbersManager()) { ProviderFrameworkInitializer.registerServiceWrappers(); diff --git a/core/java/android/net/vcn/VcnFrameworkInitializer.java b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java similarity index 87% rename from core/java/android/net/vcn/VcnFrameworkInitializer.java rename to core/java/android/net/ConnectivityFrameworkInitializerBaklava.java index 8cb213b306be..1f0fa92d7976 100644 --- a/core/java/android/net/vcn/VcnFrameworkInitializer.java +++ b/core/java/android/net/ConnectivityFrameworkInitializerBaklava.java @@ -14,15 +14,21 @@ * limitations under the License. */ -package android.net.vcn; +package android.net; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; + +import android.annotation.FlaggedApi; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.SystemServiceRegistry; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Context; import android.content.pm.PackageManager; +import android.net.vcn.IVcnManagementService; +import android.net.vcn.VcnManager; import android.os.Build; import android.os.SystemProperties; @@ -31,8 +37,9 @@ import android.os.SystemProperties; * * @hide */ -// TODO: Expose it as @SystemApi(client = MODULE_LIBRARIES) -public final class VcnFrameworkInitializer { +@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class ConnectivityFrameworkInitializerBaklava { /** * Starting with {@link VANILLA_ICE_CREAM}, Telephony feature flags (e.g. {@link * PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}) are being checked before returning managers @@ -55,7 +62,7 @@ public final class VcnFrameworkInitializer { */ private static final int VENDOR_API_FOR_ANDROID_V = 202404; - private VcnFrameworkInitializer() {} + private ConnectivityFrameworkInitializerBaklava() {} // Suppressing AndroidFrameworkCompatChange because we're querying vendor // partition SDK level, not application's target SDK version (which BTW we @@ -86,7 +93,10 @@ public final class VcnFrameworkInitializer { * * @throws IllegalStateException if this is called anywhere besides {@link * SystemServiceRegistry}. + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static void registerServiceWrappers() { SystemServiceRegistry.registerContextAwareService( VcnManager.VCN_MANAGEMENT_SERVICE_STRING, diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java index af93c964a8ba..3219ce81c256 100644 --- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java +++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java @@ -16,6 +16,7 @@ package android.net.vcn; import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; import static android.net.vcn.Flags.FLAG_SAFE_MODE_CONFIG; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; @@ -82,7 +83,15 @@ import java.util.concurrent.TimeUnit; * */ public final class VcnGatewayConnectionConfig { - /** @hide */ + /** + * Minimum NAT timeout not set. + * + *

When the timeout is not set, the device will automatically choose a keepalive interval and + * may reduce the keepalive frequency for power-optimization. + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + // This constant does not represent a minimum value. It indicates the value is not configured. + @SuppressLint("MinMaxConstant") public static final int MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET = -1; /** @hide */ @@ -773,7 +782,7 @@ public final class VcnGatewayConnectionConfig { * * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN - * Gateway. + * Gateway; or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET} to clear this value. * @return this {@link Builder} instance, for chaining */ @NonNull @@ -781,8 +790,10 @@ public final class VcnGatewayConnectionConfig { @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( - minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, - "Timeout must be at least 120s"); + minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + || minUdpPort4500NatTimeoutSeconds + >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return this; diff --git a/core/java/android/net/vcn/VcnTransportInfo.java b/core/java/android/net/vcn/VcnTransportInfo.java index 1fc91eea3138..3638429f33fb 100644 --- a/core/java/android/net/vcn/VcnTransportInfo.java +++ b/core/java/android/net/vcn/VcnTransportInfo.java @@ -17,13 +17,16 @@ package android.net.vcn; import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS; +import static android.net.vcn.Flags.FLAG_MAINLINE_VCN_MODULE_API; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS; import static android.net.vcn.VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.NetworkCapabilities; import android.net.TransportInfo; import android.net.wifi.WifiInfo; @@ -52,23 +55,29 @@ import java.util.Objects; * @hide */ // TODO: Do not store WifiInfo and subscription ID in VcnTransportInfo anymore -public class VcnTransportInfo implements TransportInfo, Parcelable { +@FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +public final class VcnTransportInfo implements TransportInfo, Parcelable { @Nullable private final WifiInfo mWifiInfo; private final int mSubId; private final int mMinUdpPort4500NatTimeoutSeconds; + /** @hide */ public VcnTransportInfo(@NonNull WifiInfo wifiInfo) { this(wifiInfo, INVALID_SUBSCRIPTION_ID, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET); } + /** @hide */ public VcnTransportInfo(@NonNull WifiInfo wifiInfo, int minUdpPort4500NatTimeoutSeconds) { this(wifiInfo, INVALID_SUBSCRIPTION_ID, minUdpPort4500NatTimeoutSeconds); } + /** @hide */ public VcnTransportInfo(int subId) { this(null /* wifiInfo */, subId, MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET); } + /** @hide */ public VcnTransportInfo(int subId, int minUdpPort4500NatTimeoutSeconds) { this(null /* wifiInfo */, subId, minUdpPort4500NatTimeoutSeconds); } @@ -86,6 +95,7 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { *

If the underlying Network for the associated VCN is Cellular, returns null. * * @return the WifiInfo if there is an underlying WiFi connection, else null. + * @hide */ @Nullable public WifiInfo getWifiInfo() { @@ -100,17 +110,27 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { * * @return the Subscription ID if a cellular underlying Network is present, else {@link * android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * @hide */ public int getSubId() { return mSubId; } /** - * Get the VCN provided UDP port 4500 NAT timeout + * Get the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping. * - * @return the UDP 4500 NAT timeout, or + *

To ensure uninterrupted connectivity, the device must send keepalive packets before the + * timeout. Failure to do so may result in the mapping being cleared and connection termination. + * This value is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, + * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. + * + * @return the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping, or * VcnGatewayConnectionConfig.MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET if not set. + * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int) + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public int getMinUdpPort4500NatTimeoutSeconds() { return mMinUdpPort4500NatTimeoutSeconds; } @@ -129,12 +149,21 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { && mMinUdpPort4500NatTimeoutSeconds == that.mMinUdpPort4500NatTimeoutSeconds; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public int describeContents() { return 0; } + /** @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override @NonNull public TransportInfo makeCopy(long redactions) { @@ -149,6 +178,9 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { mMinUdpPort4500NatTimeoutSeconds); } + /** @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public long getApplicableRedactions() { long redactions = REDACT_FOR_NETWORK_SETTINGS; @@ -161,7 +193,13 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return redactions; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mSubId); @@ -174,7 +212,13 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { return "VcnTransportInfo { mWifiInfo = " + mWifiInfo + ", mSubId = " + mSubId + " }"; } - /** Implement the Parcelable interface */ + /** + * Implement the Parcelable interface + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final @NonNull Creator CREATOR = new Creator() { public VcnTransportInfo createFromParcel(Parcel in) { @@ -201,37 +245,63 @@ public class VcnTransportInfo implements TransportInfo, Parcelable { } }; - /** This class can be used to construct a {@link VcnTransportInfo}. */ + /** + * This class can be used to construct a {@link VcnTransportInfo}. + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final class Builder { private int mMinUdpPort4500NatTimeoutSeconds = MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET; - /** Construct Builder */ + /** + * Construct Builder + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public Builder() {} /** - * Sets the maximum supported IKEv2/IPsec NATT keepalive timeout. + * Set the minimum duration that the VCN Gateway guarantees to preserve a NAT mapping. * *

This is used as a power-optimization hint for other IKEv2/IPsec use cases (e.g. VPNs, * or IWLAN) to reduce the necessary keepalive frequency, thus conserving power and data. * - * @param minUdpPort4500NatTimeoutSeconds the maximum keepalive timeout supported by the VCN - * Gateway Connection, generally the minimum duration a NAT mapping is cached on the VCN - * Gateway. + * @param minUdpPort4500NatTimeoutSeconds the minimum duration that the VCN Gateway + * guarantees to preserve a NAT mapping, or {@link MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET} + * to clear this value. To ensure uninterrupted connectivity, the device must send + * keepalive packets within this interval. Failure to do so may result in the mapping + * being cleared and connection termination. * @return this {@link Builder} instance, for chaining + * @see VcnGatewayConnectionConfig.Builder#setMinUdpPort4500NatTimeoutSeconds(int) + * @hide */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public Builder setMinUdpPort4500NatTimeoutSeconds( @IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS) int minUdpPort4500NatTimeoutSeconds) { Preconditions.checkArgument( - minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, - "Timeout must be at least 120s"); + minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET + || minUdpPort4500NatTimeoutSeconds + >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS, + "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET"); mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds; return Builder.this; } - /** Build a VcnTransportInfo instance */ + /** + * Build a VcnTransportInfo instance + * + * @hide + */ + @FlaggedApi(FLAG_MAINLINE_VCN_MODULE_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public VcnTransportInfo build() { return new VcnTransportInfo( -- GitLab From 3153a97dacb1a88282362e60d4dd94f1e4617d00 Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Wed, 6 Nov 2024 11:56:06 +0000 Subject: [PATCH 026/656] [AAPM] Rename permission Permission is used for more than setting the state Bug: 352420507 Test: AdvancedProtectionManagerTest Flag: android.security.aapm_api Change-Id: I3be84c469981aafe0ef232f75c216c30ee24a1d9 --- core/api/system-current.txt | 6 +++--- .../advancedprotection/AdvancedProtectionManager.java | 4 ++-- .../IAdvancedProtectionService.aidl | 4 ++-- core/res/AndroidManifest.xml | 11 ++++++----- data/etc/privapp-permissions-platform.xml | 2 +- packages/Shell/AndroidManifest.xml | 2 +- .../advancedprotection/AdvancedProtectionService.java | 4 ++-- .../AdvancedProtectionServiceTest.java | 4 ++-- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a46f872bc1d4..9e68e88bbd4f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -190,6 +190,7 @@ package android { field public static final String MANAGE_ACCESSIBILITY = "android.permission.MANAGE_ACCESSIBILITY"; field @Deprecated public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_ACTIVITY_TASKS = "android.permission.MANAGE_ACTIVITY_TASKS"; + field @FlaggedApi("android.security.aapm_api") public static final String MANAGE_ADVANCED_PROTECTION_MODE = "android.permission.MANAGE_ADVANCED_PROTECTION_MODE"; field public static final String MANAGE_APP_HIBERNATION = "android.permission.MANAGE_APP_HIBERNATION"; field public static final String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS"; field public static final String MANAGE_APP_PREDICTIONS = "android.permission.MANAGE_APP_PREDICTIONS"; @@ -370,7 +371,6 @@ package android { field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT"; field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY"; field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER"; - field @FlaggedApi("android.security.aapm_api") public static final String SET_ADVANCED_PROTECTION_MODE = "android.permission.SET_ADVANCED_PROTECTION_MODE"; field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE"; field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"; field public static final String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS"; @@ -12621,8 +12621,8 @@ package android.security.advancedprotection { @FlaggedApi("android.security.aapm_api") public final class AdvancedProtectionManager { method @NonNull public android.content.Intent createSupportIntent(@NonNull String, @Nullable String); - method @NonNull @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public java.util.List getAdvancedProtectionFeatures(); - method @RequiresPermission(android.Manifest.permission.SET_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public java.util.List getAdvancedProtectionFeatures(); + method @RequiresPermission(android.Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean); field @FlaggedApi("android.security.aapm_api") public static final String ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG = "android.security.advancedprotection.action.SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG"; field public static final String EXTRA_SUPPORT_DIALOG_FEATURE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_FEATURE"; field public static final String EXTRA_SUPPORT_DIALOG_TYPE = "android.security.advancedprotection.extra.SUPPORT_DIALOG_TYPE"; diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java index 9fe0dda136d1..0302fafd2f6c 100644 --- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java +++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java @@ -281,7 +281,7 @@ public final class AdvancedProtectionManager { * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean enabled) { try { mService.setAdvancedProtectionEnabled(enabled); @@ -297,7 +297,7 @@ public final class AdvancedProtectionManager { */ @SystemApi @NonNull - @RequiresPermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + @RequiresPermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public List getAdvancedProtectionFeatures() { try { return mService.getAdvancedProtectionFeatures(); diff --git a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl index 68307632027a..1939f829c700 100644 --- a/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl +++ b/core/java/android/security/advancedprotection/IAdvancedProtectionService.aidl @@ -31,8 +31,8 @@ interface IAdvancedProtectionService { void registerAdvancedProtectionCallback(IAdvancedProtectionCallback callback); @EnforcePermission("QUERY_ADVANCED_PROTECTION_MODE") void unregisterAdvancedProtectionCallback(IAdvancedProtectionCallback callback); - @EnforcePermission("SET_ADVANCED_PROTECTION_MODE") + @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE") void setAdvancedProtectionEnabled(boolean enabled); - @EnforcePermission("SET_ADVANCED_PROTECTION_MODE") + @EnforcePermission("MANAGE_ADVANCED_PROTECTION_MODE") List getAdvancedProtectionFeatures(); } \ No newline at end of file diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 95d07df388d0..79dd12a468f7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4172,18 +4172,19 @@ android:protectionLevel="signature|installer" /> - - - + @FlaggedApi(android.security.Flags.FLAG_AAPM_API) --> diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index fea7cb4b422c..0209afbc1287 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -598,7 +598,7 @@ applications that come with the platform - + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 526320debb1a..1333f0e81465 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -951,7 +951,7 @@ - diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java index e780be490181..e8723b91a541 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -141,7 +141,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub } @Override - @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public void setAdvancedProtectionEnabled(boolean enabled) { setAdvancedProtectionEnabled_enforcePermission(); final long identity = Binder.clearCallingIdentity(); @@ -159,7 +159,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub } @Override - @EnforcePermission(Manifest.permission.SET_ADVANCED_PROTECTION_MODE) + @EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE) public List getAdvancedProtectionFeatures() { getAdvancedProtectionFeatures_enforcePermission(); List features = new ArrayList<>(); diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java index 24bf6ca507e6..b1df0f1e9cce 100644 --- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java @@ -60,7 +60,7 @@ public class AdvancedProtectionServiceTest { public void setup() throws Settings.SettingNotFoundException { mContext = mock(Context.class); mPermissionEnforcer = new FakePermissionEnforcer(); - mPermissionEnforcer.grant(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); + mPermissionEnforcer.grant(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE); mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE); mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) { @@ -299,7 +299,7 @@ public class AdvancedProtectionServiceTest { @Test public void testSetProtection_withoutPermission() { - mPermissionEnforcer.revoke(Manifest.permission.SET_ADVANCED_PROTECTION_MODE); + mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE); assertThrows(SecurityException.class, () -> mService.setAdvancedProtectionEnabled(true)); } -- GitLab From e869daccb27ffe57e6e96661abfde4308336bd11 Mon Sep 17 00:00:00 2001 From: Rana Mouawi Date: Thu, 14 Nov 2024 16:50:14 +0000 Subject: [PATCH 027/656] Update bug number for handle_bugreports_for_wear flag. Bug: b/378060870 Flag: EXEMPT updating flag config Change-Id: I8caecede5f21af3142eb14da9d7d2ab10b22f566 --- packages/Shell/aconfig/wear.aconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/Shell/aconfig/wear.aconfig b/packages/Shell/aconfig/wear.aconfig index e07bd963a4aa..d88fd46cb68d 100644 --- a/packages/Shell/aconfig/wear.aconfig +++ b/packages/Shell/aconfig/wear.aconfig @@ -5,5 +5,5 @@ flag { name: "handle_bugreports_for_wear" namespace: "wear_services" description: "This flag enables Shell to propagate bugreport results to WearServices." - bug: "378060870" + bug: "338029043" } \ No newline at end of file -- GitLab From c864e8d19da539137c5a40e244544546f8cd80f8 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Wed, 13 Nov 2024 20:28:01 -0800 Subject: [PATCH 028/656] Clean up fix_config_garbage_collection To mainline VCN, we need to first change the VCN flag host container to be the tethering module and then use a buid flag to control whether to include VCN in the non-updatable platform or the module. After the container changes, when VCN is still in the platform, the VCN code can only access flags via "exported" mode, and only API flags are exportable and accessible. Thus the fix_config_garbage_collection flag needs to be handled It is safe to clean up this flag because it is a low risk bug fix and it has been in teamfood for a week with no issue being seen. Bug: 370862489 Test: FrameworksVcnTests; CtsVcnTestCases Flag: EXEMPT flag clean up Change-Id: If38b46735e7b20d918b7483c16b30101416b0a6c --- core/java/android/net/vcn/flags.aconfig | 10 ---------- .../android/server/VcnManagementService.java | 20 +++++-------------- .../server/VcnManagementServiceTest.java | 3 --- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 1b2c575917b2..dc87866edb6c 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -15,14 +15,4 @@ flag { description: "Expose APIs from VCN for mainline migration" is_exported: true bug: "376339506" -} - -flag { - name: "fix_config_garbage_collection" - namespace: "vcn" - description: "Handle race condition in subscription change" - bug: "370862489" - metadata { - purpose: PURPOSE_BUGFIX - } } \ No newline at end of file diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 06e6c8b1ec53..2012f5632a64 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -48,7 +48,6 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; -import android.net.vcn.Flags; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; @@ -890,20 +889,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { while (configsIterator.hasNext()) { final ParcelUuid subGrp = configsIterator.next(); - if (Flags.fixConfigGarbageCollection()) { - if (!subGroups.contains(subGrp)) { - // Trim subGrps with no more subscriptions; must have moved to another subGrp - logDbg("Garbage collect VcnConfig for group=" + subGrp); - configsIterator.remove(); - shouldWrite = true; - } - } else { - final List subscriptions = subMgr.getSubscriptionsInGroup(subGrp); - if (subscriptions == null || subscriptions.isEmpty()) { - // Trim subGrps with no more subscriptions; must have moved to another subGrp - configsIterator.remove(); - shouldWrite = true; - } + if (!subGroups.contains(subGrp)) { + // Trim subGrps with no more subscriptions; must have moved to another subGrp + logDbg("Garbage collect VcnConfig for group=" + subGrp); + configsIterator.remove(); + shouldWrite = true; } } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 3828a71d7b28..4ab8e6abbbef 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -70,7 +70,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.Uri; -import android.net.vcn.Flags; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; @@ -293,8 +292,6 @@ public class VcnManagementServiceTest { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any(), any()); - - mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CONFIG_GARBAGE_COLLECTION); } -- GitLab From c2bd3bd344f5ab8ed72aa4cb9b2a7010af9961a5 Mon Sep 17 00:00:00 2001 From: Yan Yan Date: Wed, 13 Nov 2024 16:37:03 +0000 Subject: [PATCH 029/656] Change VCN flag container to tethering VCN will be moved to Tetherig module eventually. This patch updates the VCN flag host container to the module and updates the framework.jar to access the flags via "exported" mode. Followup CLs will move VCN to a seperate java_sdk_library and the aconfig lib will be removed from framework-minus-apex-aconfig-declarations and moved to that java_sdk_library. A build system flag will control whether the VCN will be in platform or the tethering module. Bug: 376339506 Test: presubmit Flag: EXEMPT flag container value change Change-Id: I0434abce40dbadeec20f373f46484c41a08947ff --- AconfigFlags.bp | 11 ++++++++--- core/java/android/net/vcn/flags.aconfig | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AconfigFlags.bp b/AconfigFlags.bp index e71ded922ae6..8dc699b8d5db 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -49,7 +49,7 @@ aconfig_declarations_group { "android.media.tv.flags-aconfig-java", "android.multiuser.flags-aconfig-java", "android.net.platform.flags-aconfig-java", - "android.net.vcn.flags-aconfig-java", + "android.net.vcn.flags-aconfig-java-export", "android.net.wifi.flags-aconfig-java", "android.nfc.flags-aconfig-java", "android.os.flags-aconfig-java", @@ -1061,16 +1061,21 @@ java_aconfig_library { } // VCN +// TODO:376339506 Move the VCN code, the flag declaration and +// java_aconfig_library to framework-connectivity-b aconfig_declarations { name: "android.net.vcn.flags-aconfig", package: "android.net.vcn", - container: "system", + container: "com.android.tethering", + exportable: true, srcs: ["core/java/android/net/vcn/*.aconfig"], } java_aconfig_library { - name: "android.net.vcn.flags-aconfig-java", + name: "android.net.vcn.flags-aconfig-java-export", aconfig_declarations: "android.net.vcn.flags-aconfig", + mode: "exported", + min_sdk_version: "35", defaults: ["framework-minus-apex-aconfig-java-defaults"], } diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index dc87866edb6c..b461f95fec53 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -1,5 +1,5 @@ package: "android.net.vcn" -container: "system" +container: "com.android.tethering" flag { name: "safe_mode_config" -- GitLab From 6f0684a0a9facfc337ec757074b1c8bcf4573a42 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Thu, 14 Nov 2024 11:12:20 -0600 Subject: [PATCH 030/656] Remove secure_window_state flag Bug: 308662081 Flag: EXEMPT removing secure_window_state Test: presubmits Change-Id: I07fe8f314f6473fd95208734fa9bec6196e4ac81 --- .../window/flags/window_surfaces.aconfig | 8 -------- .../com/android/server/wm/WindowState.java | 19 ++++--------------- .../server/wm/WindowStateAnimator.java | 7 ------- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 392c307de7ba..13ded68f774e 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -27,14 +27,6 @@ flag { bug: "262477923" } -flag { - namespace: "window_surfaces" - name: "secure_window_state" - description: "Move SC secure flag to WindowState level" - is_fixed_read_only: true - bug: "308662081" -} - flag { namespace: "window_surfaces" name: "trusted_presentation_listener_for_window" diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 079170a70433..81e144b86a83 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -182,7 +182,6 @@ import static com.android.server.wm.WindowStateProto.UNRESTRICTED_KEEP_CLEAR_ARE import static com.android.server.wm.WindowStateProto.VIEW_VISIBILITY; import static com.android.server.wm.WindowStateProto.WINDOW_CONTAINER; import static com.android.server.wm.WindowStateProto.WINDOW_FRAMES; -import static com.android.window.flags.Flags.secureWindowState; import static com.android.window.flags.Flags.surfaceTrustedOverlay; import android.annotation.CallSuper; @@ -1187,9 +1186,7 @@ class WindowState extends WindowContainer implements WindowManagerP if (surfaceTrustedOverlay() && isWindowTrustedOverlay()) { getPendingTransaction().setTrustedOverlay(mSurfaceControl, true); } - if (secureWindowState()) { - getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked()); - } + getPendingTransaction().setSecure(mSurfaceControl, isSecureLocked()); // All apps should be considered as occluding when computing TrustedPresentation Thresholds. final boolean canOccludePresentation = !mSession.mCanAddInternalSystemWindow; getPendingTransaction().setCanOccludePresentation(mSurfaceControl, canOccludePresentation); @@ -6180,18 +6177,10 @@ class WindowState extends WindowContainer implements WindowManagerP void setSecureLocked(boolean isSecure) { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, getName()); - if (secureWindowState()) { - if (mSurfaceControl == null) { - return; - } - getPendingTransaction().setSecure(mSurfaceControl, isSecure); - } else { - if (mWinAnimator.mSurfaceControl == null) { - return; - } - getPendingTransaction().setSecure(mWinAnimator.mSurfaceControl, - isSecure); + if (mSurfaceControl == null) { + return; } + getPendingTransaction().setSecure(mSurfaceControl, isSecure); if (mDisplayContent != null) { mDisplayContent.refreshImeSecureFlag(getSyncTransaction()); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a934eea690f7..0154d95d888d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -49,7 +49,6 @@ import static com.android.server.wm.WindowStateAnimatorProto.DRAW_STATE; import static com.android.server.wm.WindowStateAnimatorProto.SURFACE; import static com.android.server.wm.WindowStateAnimatorProto.SYSTEM_DECOR_RECT; import static com.android.server.wm.WindowSurfaceControllerProto.SHOWN; -import static com.android.window.flags.Flags.secureWindowState; import static com.android.window.flags.Flags.setScPropertiesInClient; import android.content.Context; @@ -310,12 +309,6 @@ class WindowStateAnimator { int flags = SurfaceControl.HIDDEN; final WindowManager.LayoutParams attrs = w.mAttrs; - if (!secureWindowState()) { - if (w.isSecureLocked()) { - flags |= SurfaceControl.SECURE; - } - } - if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) { flags |= SurfaceControl.SKIP_SCREENSHOT; } -- GitLab From 1639f57b55426746d88fdaac25c71caacd787e36 Mon Sep 17 00:00:00 2001 From: Anton Potapov Date: Thu, 7 Nov 2024 18:08:11 +0000 Subject: [PATCH 031/656] Don't hide a slider if it's already being shown in the VolumeDialog As the VolumeDialogImpl we want to continue showing a slider if it has already being shown in the dialog. Flag: com.android.systemui.volume_redesign Bug: 369992924 Test: atest VolumeDialogSlidersInteractorTest Change-Id: I8a6c10f60fb2ac9bcb9d1e7688d4d233ef7d775f --- .../VolumeDialogSlidersInteractorTest.kt | 24 +++++++++++++++++++ .../VolumeDialogSlidersInteractor.kt | 17 ++++++++----- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt index 7c5a48728a11..3f995c69c32f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractorTest.kt @@ -155,6 +155,30 @@ class VolumeDialogSlidersInteractorTest : SysuiTestCase() { } } + @Test + fun activeStreamChanges_showBoth() { + with(kosmos) { + testScope.runTest { + runCurrent() + fakeVolumeDialogController.updateState { + activeStream = AudioManager.STREAM_SYSTEM + states.put(AudioManager.STREAM_MUSIC, buildStreamState()) + states.put(AudioManager.STREAM_SYSTEM, buildStreamState()) + } + val slidersModel by collectLastValue(underTest.sliders) + runCurrent() + + fakeVolumeDialogController.updateState { activeStream = AudioManager.STREAM_MUSIC } + runCurrent() + + assertThat(slidersModel!!.slider) + .isEqualTo(VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)) + assertThat(slidersModel!!.floatingSliders) + .containsExactly(VolumeDialogSliderType.Stream(AudioManager.STREAM_SYSTEM)) + } + } + } + private fun buildStreamState( build: VolumeDialogController.StreamState.() -> Unit = {} ): VolumeDialogController.StreamState { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt index 7af4258930cb..948d0fec16ae 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSlidersInteractor.kt @@ -35,6 +35,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.runningReduce import kotlinx.coroutines.flow.stateIn private const val DEFAULT_STREAM = AudioManager.STREAM_MUSIC @@ -54,13 +55,17 @@ constructor( volumeDialogStateInteractor.volumeDialogState .filter { it.streamModels.isNotEmpty() } .map { stateModel -> - stateModel.streamModels.values - .filter { streamModel -> shouldShowSliders(stateModel, streamModel) } - .sortedWith(streamsSorter) + val sliderTypes = + stateModel.streamModels.values + .filter { streamModel -> shouldShowSliders(stateModel, streamModel) } + .sortedWith(streamsSorter) + .map { model -> model.toType() } + LinkedHashSet(sliderTypes) } - .map { models -> - val sliderTypes: List = - models.map { model -> model.toType() } + .runningReduce { sliderTypes, newSliderTypes -> + newSliderTypes.apply { addAll(sliderTypes) } + } + .map { sliderTypes -> VolumeDialogSlidersModel( slider = sliderTypes.first(), floatingSliders = sliderTypes.drop(1), -- GitLab From e6e8999341d92bb955c8e69ec6a21d532071b419 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 14 Nov 2024 09:31:22 -0800 Subject: [PATCH 032/656] Log event when overflow is opened Bug: 349845968 Test: atest BubblesTest Test: manual, switch to overflow, check event is logged Flag: com.android.wm.shell.enable_bubble_bar Change-Id: I8e0c7493d4876c9acffe1a970bf8a264c46ed89b --- .../wm/shell/bubbles/BubbleController.java | 6 +----- .../android/systemui/wmshell/BubblesTest.java | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 39dc26797a81..451d6c3ec16f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1350,11 +1350,7 @@ public class BubbleController implements ConfigurationChangeListener, if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); mLayerView.showExpandedView(mBubbleData.getOverflow()); - if (wasExpanded) { - mLogger.log(BubbleLogger.Event.BUBBLE_BAR_BUBBLE_SWITCHED); - } else { - mLogger.log(BubbleLogger.Event.BUBBLE_BAR_EXPANDED); - } + mLogger.log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED); return; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 856333ea724e..7cf01000cbb7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -2707,6 +2707,22 @@ public class BubblesTest extends SysuiTestCase { eq(BubbleLogger.Event.BUBBLE_BAR_EXPANDED)); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_openOverflow() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + + clearInvocations(mBubbleLogger); + mBubbleController.expandStackAndSelectBubbleFromLauncher(BubbleOverflow.KEY, 0); + verify(mBubbleLogger).log(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_SELECTED); + verifyNoMoreInteractions(mBubbleLogger); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); -- GitLab From da5d3921e2b14f574acf9281501387bc625edba2 Mon Sep 17 00:00:00 2001 From: Shivangi Dubey Date: Thu, 14 Nov 2024 10:59:50 +0000 Subject: [PATCH 033/656] Fix split screen unfold animation Expectation is to not animate sides adjacent to split bar. There is a bug in identifying left-right split from top-bottom split. This change uses SplitScreenController#isLeftRightSplit instead of comparing width and height of root task. Bug: 377657152 Test: 1. Open two apps in splitscreen on folded screen 2. Unfold device and see if sides adjacent to split bar animate Flag: EXEMPT bugfix Change-Id: I5856ea458637e869d3080042cd23fa31fcaa766b --- .../wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java index d28287da83b6..32f3cd820421 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java @@ -334,8 +334,8 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator, // Sides adjacent to split bar or task bar are not be animated. Insets margins; - final boolean isLandscape = mRootStageBounds.width() > mRootStageBounds.height(); - if (isLandscape) { // Left and right splits. + final boolean isLeftRightSplit = mSplitScreenController.get().get().isLeftRightSplit(); + if (isLeftRightSplit) { margins = getLandscapeMargins(margin, taskbarExpanded); } else { // Top and bottom splits. margins = getPortraitMargins(margin, taskbarExpanded); -- GitLab From 8525faead5e2aa5d9f540bf29069e264aee0a0f9 Mon Sep 17 00:00:00 2001 From: Sally Date: Wed, 9 Oct 2024 23:16:49 +0000 Subject: [PATCH 034/656] Add IntRange to TtsSpan TimeBuilder APIs TimeBuilder is restricted to [0, 59] for seconds and minutes, and [0, 24] for hours. Flag: com.android.text.flags.tts_span_duration Test: builds Bug: 372323279 Change-Id: I71b06e8e9904e11d03552abfa5d7f0b9d5b44c5f --- core/api/current.txt | 6 +++--- core/java/android/text/style/TtsSpan.java | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 06cf9a5631ee..5df99cb834f9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -50062,9 +50062,9 @@ package android.text.style { public static class TtsSpan.TimeBuilder extends android.text.style.TtsSpan.SemioticClassBuilder { ctor public TtsSpan.TimeBuilder(); ctor public TtsSpan.TimeBuilder(int, int); - method public android.text.style.TtsSpan.TimeBuilder setHours(int); - method public android.text.style.TtsSpan.TimeBuilder setMinutes(int); - method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.TimeBuilder setSeconds(int); + method public android.text.style.TtsSpan.TimeBuilder setHours(@IntRange(from=0, to=24) int); + method public android.text.style.TtsSpan.TimeBuilder setMinutes(@IntRange(from=0, to=59) int); + method @FlaggedApi("com.android.text.flags.tts_span_duration") @NonNull public android.text.style.TtsSpan.TimeBuilder setSeconds(@IntRange(from=0, to=59) int); } public static class TtsSpan.VerbatimBuilder extends android.text.style.TtsSpan.SemioticClassBuilder { diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java index b7b8f0b1b5d8..a337ba2a57fb 100644 --- a/core/java/android/text/style/TtsSpan.java +++ b/core/java/android/text/style/TtsSpan.java @@ -19,6 +19,7 @@ package android.text.style; import static com.android.text.flags.Flags.FLAG_TTS_SPAN_DURATION; import android.annotation.FlaggedApi; +import android.annotation.IntRange; import android.annotation.NonNull; import android.os.Parcel; import android.os.PersistableBundle; @@ -324,7 +325,8 @@ public class TtsSpan implements ParcelableSpan { /** * Argument used to specify the seconds of a time or duration. The seconds should be - * provided as an integer in the range from 0 up to and including 59. + * provided as an integer in the range from 0 up to and including 59 for + * {@link #TYPE_TIME}. * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. */ @FlaggedApi(FLAG_TTS_SPAN_DURATION) @@ -1140,7 +1142,7 @@ public class TtsSpan implements ParcelableSpan { * @return This instance. * @see #ARG_HOURS */ - public TimeBuilder setHours(int hours) { + public TimeBuilder setHours(@IntRange(from = 0, to = 24) int hours) { return setIntArgument(TtsSpan.ARG_HOURS, hours); } @@ -1151,7 +1153,7 @@ public class TtsSpan implements ParcelableSpan { * @return This instance. * @see #ARG_MINUTES */ - public TimeBuilder setMinutes(int minutes) { + public TimeBuilder setMinutes(@IntRange(from = 0, to = 59) int minutes) { return setIntArgument(TtsSpan.ARG_MINUTES, minutes); } @@ -1162,7 +1164,7 @@ public class TtsSpan implements ParcelableSpan { */ @FlaggedApi(FLAG_TTS_SPAN_DURATION) @NonNull - public TimeBuilder setSeconds(int seconds) { + public TimeBuilder setSeconds(@IntRange(from = 0, to = 59) int seconds) { return setIntArgument(TtsSpan.ARG_SECONDS, seconds); } } -- GitLab From b2c257f46d6a596dbb67e507833cee3022567c8b Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 14 Nov 2024 10:28:50 -0800 Subject: [PATCH 035/656] Log event when bubble is opened from overflow Log when bubble bar bubble is added back to the bar from overflow. Bug: 349845968 Test: atest BubblesTest Test: manual, tap on a bubble in overflow, check logged events Flag: com.android.wm.shell.enable_bubble_bar Change-Id: Ie31f24ec3fc106bb30c0cf94f8eef6acf19b0e13 --- .../wm/shell/bubbles/BubbleController.java | 6 ++++- .../android/systemui/wmshell/BubblesTest.java | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 451d6c3ec16f..2dd4e23c8f44 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1327,7 +1327,11 @@ public class BubbleController implements ConfigurationChangeListener, /** Promote the provided bubble from the overflow view. */ public void promoteBubbleFromOverflow(Bubble bubble) { - mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); + if (isShowingAsBubbleBar()) { + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR); + } else { + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); + } ProtoLog.d(WM_SHELL_BUBBLES, "promoteBubbleFromOverflow=%s", bubble.getKey()); bubble.setInflateSynchronously(mInflateSynchronously); bubble.setShouldAutoExpand(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 7cf01000cbb7..aad8b4ba1191 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -2723,6 +2723,28 @@ public class BubblesTest extends SysuiTestCase { verifyNoMoreInteractions(mBubbleLogger); } + @EnableFlags(FLAG_ENABLE_BUBBLE_BAR) + @Test + public void testEventLogging_bubbleBar_fromOverflowToBar() { + mBubbleProperties.mIsBubbleBarEnabled = true; + mPositioner.setIsLargeScreen(true); + FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); + mBubbleController.registerBubbleStateListener(bubbleStateListener); + + mEntryListener.onEntryAdded(mRow); + + // Dismiss the bubble so it's in the overflow + mBubbleController.removeBubble( + mRow.getKey(), Bubbles.DISMISS_USER_GESTURE); + Bubble overflowBubble = mBubbleData.getOverflowBubbleWithKey(mRow.getKey()); + assertThat(overflowBubble).isNotNull(); + + // Promote overflow bubble and check that it is logged + mBubbleController.promoteBubbleFromOverflow(overflowBubble); + verify(mBubbleLogger).log(eqBubbleWithKey(overflowBubble.getKey()), + eq(BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_REMOVE_BACK_TO_BAR)); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); -- GitLab From 89143cda76a5a8cd5f3cf4f3118ad72398d643bc Mon Sep 17 00:00:00 2001 From: Maryam Dehaini Date: Wed, 13 Nov 2024 17:26:55 -0800 Subject: [PATCH 036/656] Add api to trigger App-to-web education Some applications will want to control when the App-to-web education should be shown based on the satte of the application and whether the web will provide the user will a better experience. This api allows activities to request for the education to be shown. Test: m Bug: 373680430 API-Coverage-Bug: 376903053 Flag: com.android.window.flags.enable_desktop_windowing_app_to_web_education Change-Id: Iba51662825b7272711792422c7cc5a002009915f --- core/api/current.txt | 2 +- core/java/android/app/Activity.java | 29 ++++++++----------- .../android/app/IActivityTaskManager.aidl | 4 +-- core/java/android/app/TaskInfo.java | 16 +++++----- .../com/android/server/wm/ActivityRecord.java | 9 +++--- .../server/wm/ActivityTaskManagerService.java | 5 ++-- .../core/java/com/android/server/wm/Task.java | 5 ++-- 7 files changed, 32 insertions(+), 38 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 59dc31492a0a..4b14624a3234 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4601,6 +4601,7 @@ package android.app { method public void reportFullyDrawn(); method public android.view.DragAndDropPermissions requestDragAndDropPermissions(android.view.DragEvent); method public void requestFullscreenMode(int, @Nullable android.os.OutcomeReceiver); + method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public final void requestOpenInBrowserEducation(); method public final void requestPermissions(@NonNull String[], int); method @FlaggedApi("android.permission.flags.device_aware_permission_apis_enabled") public final void requestPermissions(@NonNull String[], int, int); method public final void requestShowKeyboardShortcuts(); @@ -4626,7 +4627,6 @@ package android.app { method public void setInheritShowWhenLocked(boolean); method public void setIntent(android.content.Intent); method @FlaggedApi("android.security.content_uri_permission_apis") public void setIntent(@Nullable android.content.Intent, @Nullable android.app.ComponentCaller); - method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public final void setLimitSystemEducationDialogs(boolean); method public void setLocusContext(@Nullable android.content.LocusId, @Nullable android.os.Bundle); method public final void setMediaController(android.media.session.MediaController); method public void setPictureInPictureParams(@NonNull android.app.PictureInPictureParams); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 419eb7dac5f0..38aea64386a0 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1270,27 +1270,22 @@ public class Activity extends ContextThemeWrapper } /** - * To make users aware of system features such as the app header menu and its various - * functionalities, educational dialogs are shown to demonstrate how to find and utilize these - * features. Using this method, an activity can specify if it wants these educational dialogs to - * be shown. When set to {@code true}, these dialogs are not completely blocked; however, the - * system will be notified that they should not be shown unless necessary. If this API is not - * called, the system's educational dialogs are not limited by default. - * - *

This method can be utilized when activities have states where showing an - * educational dialog would be disruptive to the user. For example, if a game application is - * expecting prompt user input, this method can be used to limit educational dialogs such as the - * dialogs that showcase the app header's features which, in this instance, would disrupt the - * user's experience if shown.

- * - *

Note that educational dialogs may be shown soon after this activity is launched, so - * this method must be called early if the intent is to limit the dialogs from the start.

+ * Requests to show the “Open in browser” education. “Open in browser” is a feature + * within the app header that allows users to switch from an app to the web. The feature + * is made available when an application is opened by a user clicking a link or when a + * link is provided by an application. Links can be provided by utilizing + * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or + * {@link AssistContent#setWebUri}. + * + *

This method should be utilized when an activity wants to nudge the user to switch + * to the web application in cases where the web may provide the user with a better + * experience. Note that this method does not guarantee that the education will be shown.

*/ @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION) - public final void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) { + public final void requestOpenInBrowserEducation() { try { ActivityTaskManager - .getService().setLimitSystemEducationDialogs(mToken, limitSystemEducationDialogs); + .getService().requestOpenInBrowserEducation(mToken); } catch (RemoteException e) { // Empty } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index ec7b72ec677e..c6f62a21641d 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -242,8 +242,8 @@ interface IActivityTaskManager { boolean supportsLocalVoiceInteraction(); - // Sets whether system educational dialogs should be limited - void setLimitSystemEducationDialogs(IBinder appToken, boolean limitSystemEducationDialogs); + // Requests the "Open in browser" education to be shown + void requestOpenInBrowserEducation(IBinder appToken); // Get device configuration ConfigurationInfo getDeviceConfigurationInfo(); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index aac963ade726..01cc9d82d56d 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -340,10 +340,10 @@ public class TaskInfo { public int requestedVisibleTypes; /** - * Whether the top activity has requested to limit educational dialogs shown by the system. + * The timestamp of the top activity's last request to show the "Open in Browser" education. * @hide */ - public boolean isTopActivityLimitSystemEducationDialogs; + public long topActivityRequestOpenInBrowserEducationTimestamp; /** * Encapsulate specific App Compat information. @@ -493,8 +493,8 @@ public class TaskInfo { && Objects.equals(capturedLink, that.capturedLink) && capturedLinkTimestamp == that.capturedLinkTimestamp && requestedVisibleTypes == that.requestedVisibleTypes - && isTopActivityLimitSystemEducationDialogs - == that.isTopActivityLimitSystemEducationDialogs + && topActivityRequestOpenInBrowserEducationTimestamp + == that.topActivityRequestOpenInBrowserEducationTimestamp && appCompatTaskInfo.equalsForTaskOrganizer(that.appCompatTaskInfo) && Objects.equals(topActivityMainWindowFrame, that.topActivityMainWindowFrame); } @@ -571,7 +571,7 @@ public class TaskInfo { capturedLink = source.readTypedObject(Uri.CREATOR); capturedLinkTimestamp = source.readLong(); requestedVisibleTypes = source.readInt(); - isTopActivityLimitSystemEducationDialogs = source.readBoolean(); + topActivityRequestOpenInBrowserEducationTimestamp = source.readLong(); appCompatTaskInfo = source.readTypedObject(AppCompatTaskInfo.CREATOR); topActivityMainWindowFrame = source.readTypedObject(Rect.CREATOR); } @@ -627,7 +627,7 @@ public class TaskInfo { dest.writeTypedObject(capturedLink, flags); dest.writeLong(capturedLinkTimestamp); dest.writeInt(requestedVisibleTypes); - dest.writeBoolean(isTopActivityLimitSystemEducationDialogs); + dest.writeLong(topActivityRequestOpenInBrowserEducationTimestamp); dest.writeTypedObject(appCompatTaskInfo, flags); dest.writeTypedObject(topActivityMainWindowFrame, flags); } @@ -672,8 +672,8 @@ public class TaskInfo { + " capturedLink=" + capturedLink + " capturedLinkTimestamp=" + capturedLinkTimestamp + " requestedVisibleTypes=" + requestedVisibleTypes - + " isTopActivityLimitSystemEducationDialogs=" - + isTopActivityLimitSystemEducationDialogs + + " topActivityRequestOpenInBrowserEducationTimestamp=" + + topActivityRequestOpenInBrowserEducationTimestamp + " appCompatTaskInfo=" + appCompatTaskInfo + " topActivityMainWindowFrame=" + topActivityMainWindowFrame + "}"; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index c6e6e761c0bc..6f773c43b08e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -630,8 +630,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The locusId associated with this activity, if set. private LocusId mLocusId; - // Whether the activity is requesting to limit the system's educational dialogs - public boolean mShouldLimitSystemEducationDialogs; + // The timestamp of the last request to show the "Open in browser" education + public long mRequestOpenInBrowserEducationTimestamp; // Whether the activity was launched from a bubble. private boolean mLaunchedFromBubble; @@ -7326,9 +7326,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLocusId; } - void setLimitSystemEducationDialogs(boolean limitSystemEducationDialogs) { - if (mShouldLimitSystemEducationDialogs == limitSystemEducationDialogs) return; - mShouldLimitSystemEducationDialogs = limitSystemEducationDialogs; + void requestOpenInBrowserEducation() { + mRequestOpenInBrowserEducationTimestamp = System.currentTimeMillis(); final Task task = getTask(); if (task != null) { final boolean force = isVisibleRequested() && this == task.getTopNonFinishingActivity(); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 198e14a16500..8ff08187c698 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -3915,12 +3915,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setLimitSystemEducationDialogs( - IBinder appToken, boolean limitSystemEducationDialogs) { + public void requestOpenInBrowserEducation(IBinder appToken) { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(appToken); if (r != null) { - r.setLimitSystemEducationDialogs(limitSystemEducationDialogs); + r.requestOpenInBrowserEducation(); } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index dbc3b76c22a1..e8ae28008c89 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3423,8 +3423,8 @@ class Task extends TaskFragment { ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID; info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays; info.mTopActivityLocusId = top != null ? top.getLocusId() : null; - info.isTopActivityLimitSystemEducationDialogs = top != null - ? top.mShouldLimitSystemEducationDialogs : false; + info.topActivityRequestOpenInBrowserEducationTimestamp = top != null + ? top.mRequestOpenInBrowserEducationTimestamp : 0; final Task parentTask = getParent() != null ? getParent().asTask() : null; info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer ? parentTask.mTaskId @@ -3523,6 +3523,7 @@ class Task extends TaskFragment { info.capturedLink = null; info.capturedLinkTimestamp = 0; + info.topActivityRequestOpenInBrowserEducationTimestamp = 0; } @Nullable PictureInPictureParams getPictureInPictureParams() { -- GitLab From 1ef797766904e32acc48a3ca775147123b855e81 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Thu, 14 Nov 2024 10:18:04 -0800 Subject: [PATCH 037/656] package: Add a new BLE channel sounding feature flag Rationale for adding a new feature flag: Channel sounding is a new ranging technology supported by Bluetooth spec. Ranging apps might want to filter based on device's support for this new hardware feature. This is an optional BT feature, so not all devices supporting Bluetooth may support this particular feature. So a feature flag will be useful for apps to query/filter to understand if ranging is possible via channel sounding or not. Bug: 371095923 Test: Compiles Change-Id: I364685fd25ac1069390c07faa59b9b95e3b25e44 --- core/api/current.txt | 1 + core/java/android/content/pm/PackageManager.java | 10 ++++++++++ services/java/com/android/server/SystemServer.java | 5 ++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/core/api/current.txt b/core/api/current.txt index 6869d93dfdfb..d9452911d2be 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13109,6 +13109,7 @@ package android.content.pm { field public static final String FEATURE_BACKUP = "android.software.backup"; field public static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth"; field public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le"; + field @FlaggedApi("com.android.ranging.flags.ranging_cs_enabled") public static final String FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING = "android.hardware.bluetooth_le.channel_sounding"; field public static final String FEATURE_CAMERA = "android.hardware.camera"; field public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; field public static final String FEATURE_CAMERA_AR = "android.hardware.camera.ar"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2df187bc3122..0ed9c87e3d51 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3162,6 +3162,16 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le"; + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device is capable of ranging with + * other devices using channel sounding via Bluetooth Low Energy radio. + */ + @FlaggedApi(com.android.ranging.flags.Flags.FLAG_RANGING_CS_ENABLED) + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING = + "android.hardware.bluetooth_le.channel_sounding"; + /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has a camera facing away diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 52a2fd6e4955..688312e7e004 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -2968,7 +2968,10 @@ public final class SystemServer implements Dumpable { if (com.android.ranging.flags.Flags.rangingStackEnabled()) { if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB) || context.getPackageManager().hasSystemFeature( - PackageManager.FEATURE_WIFI_RTT)) { + PackageManager.FEATURE_WIFI_RTT) + || (com.android.ranging.flags.Flags.rangingCsEnabled() + && context.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_BLUETOOTH_LE_CHANNEL_SOUNDING))) { t.traceBegin("RangingService"); // TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next. try { -- GitLab From eb09f976e48d196db79d96aa48f15b6ff84fc15c Mon Sep 17 00:00:00 2001 From: Chris Sabotta Date: Thu, 14 Nov 2024 18:35:35 +0000 Subject: [PATCH 038/656] Print NanoappIDs as hex This improves debug readability. Flag: EXEMPT log only update Bug: 378588134 Change-Id: I9e2ab999410d37049ec01611a5c641d48c66c375 Test: presubmits --- .../location/contexthub/ContextHubClientManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java index 0fdd0ae641df..5248a051404d 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubClientManager.java @@ -237,15 +237,16 @@ import java.util.function.Consumer; if (message.isBroadcastMessage()) { if (message.isReliable()) { - Log.e(TAG, "Received reliable broadcast message from " + message.getNanoAppId()); + Log.e(TAG, "Received reliable broadcast message from 0x" + + Long.toHexString(message.getNanoAppId())); return ErrorCode.PERMANENT_ERROR; } // Broadcast messages shouldn't be sent with any permissions tagged per CHRE API // requirements. if (!messagePermissions.isEmpty()) { - Log.e(TAG, "Received broadcast message with permissions from " - + message.getNanoAppId()); + Log.e(TAG, "Received broadcast message with permissions from 0x" + + Long.toHexString(message.getNanoAppId())); return ErrorCode.PERMANENT_ERROR; } -- GitLab From 1ba0be0c7a755dc016fa2bd95be2c0473dfac983 Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Thu, 14 Nov 2024 18:44:31 +0000 Subject: [PATCH 039/656] media: Hide *_TRUSTED_CONTENT_ONLY constants Bug: 297922713 Test: builds Flag: android.media.codec.in_process_sw_audio_codec Change-Id: I3e5ccdb311465ed0701b0184e869d627634473ea --- core/api/current.txt | 2 -- media/java/android/media/MediaCodecInfo.java | 2 ++ media/java/android/media/MediaFormat.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index d2e89b4420c4..3e02e8aa8fb7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -22797,7 +22797,6 @@ package android.media { method public boolean isVendor(); field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_MEMORY_SAFE = 1; // 0x1 field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_SANDBOXED = 0; // 0x0 - field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; // 0x2 } public static final class MediaCodecInfo.AudioCapabilities { @@ -23688,7 +23687,6 @@ package android.media { field public static final int COLOR_TRANSFER_ST2084 = 6; // 0x6 field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_MEMORY_SAFE = 2; // 0x2 field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_SANDBOXED = 1; // 0x1 - field @FlaggedApi("android.media.codec.in_process_sw_audio_codec") public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 4; // 0x4 field public static final String KEY_AAC_DRC_ALBUM_MODE = "aac-drc-album-mode"; field public static final String KEY_AAC_DRC_ATTENUATION_FACTOR = "aac-drc-cut-level"; field public static final String KEY_AAC_DRC_BOOST_FACTOR = "aac-drc-boost-level"; diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 96edd63a9b12..782db358bf9f 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -1876,6 +1876,8 @@ public final class MediaCodecInfo { * Codecs with this security model is not included in * {@link MediaCodecList#REGULAR_CODECS}, but included in * {@link MediaCodecList#ALL_CODECS}. + * + * @hide */ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) public static final int SECURITY_MODEL_TRUSTED_CONTENT_ONLY = 2; diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index bd65b2ecb76a..bc09aee9ac11 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -1748,6 +1748,7 @@ public final class MediaFormat { (1 << MediaCodecInfo.SECURITY_MODEL_MEMORY_SAFE); /** * Flag for {@link MediaCodecInfo#SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. + * @hide */ @FlaggedApi(FLAG_IN_PROCESS_SW_AUDIO_CODEC) public static final int FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY = @@ -1759,8 +1760,7 @@ public final class MediaFormat { * The associated value is a flag of the following values: * {@link FLAG_SECURITY_MODEL_SANDBOXED}, * {@link FLAG_SECURITY_MODEL_MEMORY_SAFE}, - * {@link FLAG_SECURITY_MODEL_TRUSTED_CONTENT_ONLY}. The default value is - * {@link FLAG_SECURITY_MODEL_SANDBOXED}. + * The default value is {@link FLAG_SECURITY_MODEL_SANDBOXED}. *

* When passed to {@link MediaCodecList#findDecoderForFormat} or * {@link MediaCodecList#findEncoderForFormat}, MediaCodecList filters -- GitLab From 751b5ed7b2ef4f90bdcaa61df4606fcc323a3b10 Mon Sep 17 00:00:00 2001 From: Ben Lin Date: Fri, 8 Nov 2024 22:23:53 +0000 Subject: [PATCH 040/656] Update WMLockscreenVisibilityManagerTest with flag handling. Test: WindowManagerLockscreenVisibilityManagerTest Bug: 377812323 Flag: com.android.window.flags.ensure_keyguard_does_transition_starting Change-Id: Id6485c8c86474f08222fe43cd9f55be4ace467f6 --- ...wManagerLockscreenVisibilityManagerTest.kt | 103 ++++++++++++++++-- 1 file changed, 94 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt index 92764ae94271..74a0bafda931 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt @@ -17,6 +17,10 @@ package com.android.systemui.keyguard.ui.binder import android.app.IActivityTaskManager +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.CheckFlagsRule +import android.platform.test.flag.junit.DeviceFlagsValueProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -25,8 +29,10 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardDismissTransition import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock +import com.android.window.flags.Flags import com.android.wm.shell.keyguard.KeyguardTransitions import org.junit.Before +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq @@ -41,6 +47,9 @@ import org.mockito.kotlin.any @RunWith(AndroidJUnit4::class) @kotlinx.coroutines.ExperimentalCoroutinesApi class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { + + @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + private lateinit var underTest: WindowManagerLockscreenVisibilityManager private lateinit var executor: FakeExecutor @@ -68,32 +77,62 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun testLockscreenVisible_andAodVisible() { + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testLockscreenVisible_andAodVisible_without_keyguard_shell_transitions() { underTest.setLockscreenShown(true) - underTest.setAodVisible(true) - verify(activityTaskManagerService).setLockScreenShown(true, false) + underTest.setAodVisible(true) verify(activityTaskManagerService).setLockScreenShown(true, true) + verifyNoMoreInteractions(activityTaskManagerService) } @Test - fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible() { + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testLockscreenVisible_andAodVisible_with_keyguard_shell_transitions() { underTest.setLockscreenShown(true) + verify(keyguardTransitions).startKeyguardTransition(true, false) underTest.setAodVisible(true) + verify(keyguardTransitions).startKeyguardTransition(true, true) + verifyNoMoreInteractions(keyguardTransitions) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible_without_keyguard_shell_transitions() { + underTest.setLockscreenShown(true) verify(activityTaskManagerService).setLockScreenShown(true, false) + underTest.setAodVisible(true) verify(activityTaskManagerService).setLockScreenShown(true, true) + verifyNoMoreInteractions(activityTaskManagerService) underTest.setSurfaceBehindVisibility(true) - verify(activityTaskManagerService).keyguardGoingAway(anyInt()) + verifyNoMoreInteractions(activityTaskManagerService) } @Test - fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway() { + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testGoingAway_whenLockscreenVisible_thenSurfaceMadeVisible_with_keyguard_shell_transitions() { + underTest.setLockscreenShown(true) + verify(keyguardTransitions).startKeyguardTransition(true, false) + underTest.setAodVisible(true) + verify(keyguardTransitions).startKeyguardTransition(true, true) + + verifyNoMoreInteractions(keyguardTransitions) + + underTest.setSurfaceBehindVisibility(true) + verify(keyguardTransitions).startKeyguardTransition(false, false) + + verifyNoMoreInteractions(keyguardTransitions) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway_without_keyguard_shell_transitions() { underTest.setLockscreenShown(false) underTest.setAodVisible(false) @@ -106,7 +145,22 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater() { + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testSurfaceVisible_whenLockscreenNotShowing_doesNotTriggerGoingAway_with_keyguard_shell_transitions() { + underTest.setLockscreenShown(false) + underTest.setAodVisible(false) + + verify(keyguardTransitions).startKeyguardTransition(false, false) + verifyNoMoreInteractions(keyguardTransitions) + + underTest.setSurfaceBehindVisibility(true) + + verifyNoMoreInteractions(keyguardTransitions) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater_without_keyguard_shell_transitions() { underTest.setAodVisible(false) verifyNoMoreInteractions(activityTaskManagerService) @@ -116,7 +170,19 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall() { + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun testAodVisible_noLockscreenShownCallYet_doesNotShowLockscreenUntilLater_with_keyguard_shell_transitions() { + underTest.setAodVisible(false) + verifyNoMoreInteractions(keyguardTransitions) + + underTest.setLockscreenShown(true) + verify(keyguardTransitions).startKeyguardTransition(true, false) + verifyNoMoreInteractions(activityTaskManagerService) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall_without_keyguard_shell_transitions() { underTest.setLockscreenShown(true) underTest.setSurfaceBehindVisibility(true) verify(activityTaskManagerService).keyguardGoingAway(0) @@ -126,8 +192,27 @@ class WindowManagerLockscreenVisibilityManagerTest : SysuiTestCase() { } @Test - fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility() { + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun setSurfaceBehindVisibility_goesAwayFirst_andIgnoresSecondCall_with_keyguard_shell_transitions() { + underTest.setLockscreenShown(true) + underTest.setSurfaceBehindVisibility(true) + verify(keyguardTransitions).startKeyguardTransition(false, false) + + underTest.setSurfaceBehindVisibility(true) + verifyNoMoreInteractions(keyguardTransitions) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_without_keyguard_shell_transitions() { underTest.setSurfaceBehindVisibility(false) verify(activityTaskManagerService).setLockScreenShown(eq(true), any()) } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ENSURE_KEYGUARD_DOES_TRANSITION_STARTING) + fun setSurfaceBehindVisibility_falseSetsLockscreenVisibility_with_keyguard_shell_transitions() { + underTest.setSurfaceBehindVisibility(false) + verify(keyguardTransitions).startKeyguardTransition(eq(true), any()) + } } -- GitLab From 851b5adee77d0a13db0e4f750d2ba3e4a674935f Mon Sep 17 00:00:00 2001 From: Yu-Ting Tseng Date: Wed, 23 Oct 2024 13:58:24 -0700 Subject: [PATCH 041/656] Address frozen notification API feedback Add an API to accept an Executor. Improve Javadoc. Flag: DOCS_ONLY Bug: 368312382 Test: none Change-Id: Ifcb2f139dc8df9f487453361927a118b2ec65607 --- core/api/current.txt | 4 +- core/java/android/os/BinderProxy.java | 34 +++++++++----- core/java/android/os/IBinder.java | 39 +++++++++++++++- core/java/android/os/RemoteCallbackList.java | 46 +++++++++++++++++-- .../bfscctestapp/BfsccTestAppCmdService.java | 3 ++ ...nderFrozenStateChangeNotificationTest.java | 4 +- .../os/BinderDeathDispatcherTest.java | 3 +- 7 files changed, 112 insertions(+), 21 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 519deac25177..bc2a25dbc877 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33261,7 +33261,7 @@ package android.os { } public interface IBinder { - method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException; @@ -33885,6 +33885,7 @@ package android.os { method public void finishBroadcast(); method public Object getBroadcastCookie(int); method public E getBroadcastItem(int); + method @FlaggedApi("android.os.binder_frozen_state_change_callback") @Nullable public java.util.concurrent.Executor getExecutor(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize(); method public Object getRegisteredCallbackCookie(int); @@ -33905,6 +33906,7 @@ package android.os { @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder { ctor public RemoteCallbackList.Builder(int); method @NonNull public android.os.RemoteCallbackList build(); + method @NonNull public android.os.RemoteCallbackList.Builder setExecutor(@NonNull java.util.concurrent.Executor); method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback); method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int); } diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 3b5a99ed089a..01222cdd38b3 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -651,28 +652,39 @@ public final class BinderProxy implements IBinder { private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags); /** - * This list is to hold strong reference to the frozen state callbacks. The callbacks are only - * weakly referenced by JNI so the strong references here are needed to keep the callbacks - * around until the proxy is GC'ed. + * This map is to hold strong reference to the frozen state callbacks. + * + * The callbacks are only weakly referenced by JNI so the strong references here are needed to + * keep the callbacks around until the proxy is GC'ed. + * + * The key is the original callback passed into {@link #addFrozenStateChangeCallback}. The value + * is the wrapped callback created in {@link #addFrozenStateChangeCallback} to dispatch the + * calls on the desired executor. */ - private List mFrozenStateChangeCallbacks = - Collections.synchronizedList(new ArrayList<>()); + private Map mFrozenStateChangeCallbacks = + Collections.synchronizedMap(new HashMap<>()); /** * See {@link IBinder#addFrozenStateChangeCallback(FrozenStateChangeCallback)} */ - public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(Executor executor, FrozenStateChangeCallback callback) throws RemoteException { - addFrozenStateChangeCallbackNative(callback); - mFrozenStateChangeCallbacks.add(callback); + FrozenStateChangeCallback wrappedCallback = (who, state) -> + executor.execute(() -> callback.onFrozenStateChanged(who, state)); + addFrozenStateChangeCallbackNative(wrappedCallback); + mFrozenStateChangeCallbacks.put(callback, wrappedCallback); } /** * See {@link IBinder#removeFrozenStateChangeCallback} */ - public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) { - mFrozenStateChangeCallbacks.remove(callback); - return removeFrozenStateChangeCallbackNative(callback); + public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) + throws IllegalArgumentException { + FrozenStateChangeCallback wrappedCallback = mFrozenStateChangeCallbacks.remove(callback); + if (wrappedCallback == null) { + throw new IllegalArgumentException("callback not found"); + } + return removeFrozenStateChangeCallbackNative(wrappedCallback); } private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback) diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index a997f4c86704..8cfd32449537 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Base interface for a remotable object, the core part of a lightweight @@ -397,12 +399,31 @@ public interface IBinder { @interface State { } + /** + * Represents the frozen state of the remote process. + * + * While in this state, the remote process won't be able to receive and handle a + * transaction. Therefore, any asynchronous transactions will be buffered and delivered when + * the process is unfrozen, and any synchronous transactions will result in an error. + * + * Buffered transactions may be stale by the time that the process is unfrozen and handles + * them. To avoid overwhelming the remote process with stale events or overflowing their + * buffers, it's best to avoid sending binder transactions to a frozen process. + */ int STATE_FROZEN = 0; + + /** + * Represents the unfrozen state of the remote process. + * + * In this state, the process hosting the object can execute and is not restricted + * by the freezer from using the CPU or responding to binder transactions. + */ int STATE_UNFROZEN = 1; /** * Interface for receiving a callback when the process hosting an IBinder * has changed its frozen state. + * * @param who The IBinder whose hosting process has changed state. * @param state The latest state. */ @@ -427,15 +448,31 @@ public interface IBinder { *

You will only receive state change notifications for remote binders, as local binders by * definition can't be frozen without you being frozen too.

* + * @param executor The executor on which to run the callback. + * @param callback The callback used to deliver state change notifications. + * *

@throws {@link UnsupportedOperationException} if the kernel binder driver does not support * this feature. */ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) - default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) + default void addFrozenStateChangeCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull FrozenStateChangeCallback callback) throws RemoteException { throw new UnsupportedOperationException(); } + /** + * Same as {@link #addFrozenStateChangeCallback(Executor, FrozenStateChangeCallback)} except + * that callbacks are invoked on a binder thread. + * + * @hide + */ + default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) + throws RemoteException { + addFrozenStateChangeCallback(Runnable::run, callback); + } + /** * Unregister a {@link FrozenStateChangeCallback}. The callback will no longer be invoked when * the hosting process changes its frozen state. diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 91c482faf7d7..d5630fd46eb4 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -29,6 +30,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -134,6 +136,7 @@ public class RemoteCallbackList { private final @FrozenCalleePolicy int mFrozenCalleePolicy; private final int mMaxQueueSize; + private final Executor mExecutor; private final class Interface implements IBinder.DeathRecipient, IBinder.FrozenStateChangeCallback { @@ -197,7 +200,7 @@ public class RemoteCallbackList { void maybeSubscribeToFrozenCallback() throws RemoteException { if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { try { - mBinder.addFrozenStateChangeCallback(this); + mBinder.addFrozenStateChangeCallback(mExecutor, this); } catch (UnsupportedOperationException e) { // The kernel does not support frozen notifications. In this case we want to // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply @@ -237,6 +240,7 @@ public class RemoteCallbackList { private @FrozenCalleePolicy int mFrozenCalleePolicy; private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE; private InterfaceDiedCallback mInterfaceDiedCallback; + private Executor mExecutor; /** * Creates a Builder for {@link RemoteCallbackList}. @@ -284,6 +288,18 @@ public class RemoteCallbackList { return this; } + /** + * Sets the executor to be used when invoking callbacks asynchronously. + * + * This is only used when callbacks need to be invoked asynchronously, e.g. when the process + * hosting a callback becomes unfrozen. Callbacks that can be invoked immediately run on the + * same thread that calls {@link #broadcast} synchronously. + */ + public @NonNull Builder setExecutor(@NonNull @CallbackExecutor Executor executor) { + mExecutor = executor; + return this; + } + /** * For notifying when the process hosting a callback interface has died. * @@ -308,15 +324,21 @@ public class RemoteCallbackList { * @return The built {@link RemoteCallbackList} object. */ public @NonNull RemoteCallbackList build() { + Executor executor = mExecutor; + if (executor == null && mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { + // TODO Throw an exception here once the existing API caller is updated to provide + // an executor. + executor = new HandlerExecutor(Handler.getMain()); + } if (mInterfaceDiedCallback != null) { - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize) { + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor) { @Override public void onCallbackDied(E deadInterface, Object cookie) { mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie); } }; } - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize); + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor); } } @@ -340,6 +362,16 @@ public class RemoteCallbackList { return mMaxQueueSize; } + /** + * Returns the executor used when invoking callbacks asynchronously. + * + * @return The executor. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public @Nullable Executor getExecutor() { + return mExecutor; + } + /** * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to *

@@ -347,7 +379,7 @@ public class RemoteCallbackList {
      * 
*/ public RemoteCallbackList() { - this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE); + this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE, null); } /** @@ -362,10 +394,14 @@ public class RemoteCallbackList { * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be * dropped to keep the size under limit. Ignored except for * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. + * + * @param executor The executor used when invoking callbacks asynchronously. */ - private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) { + private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize, + @CallbackExecutor Executor executor) { mFrozenCalleePolicy = frozenCalleePolicy; mMaxQueueSize = maxQueueSize; + mExecutor = executor; } /** diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java index fe54aa8d87f0..945147db1ef5 100644 --- a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java +++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java @@ -18,6 +18,8 @@ package com.android.frameworks.coretests.bfscctestapp; import android.app.Service; import android.content.Intent; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; @@ -36,6 +38,7 @@ public class BfsccTestAppCmdService extends Service { @Override public void listenTo(IBinder binder) throws RemoteException { binder.addFrozenStateChangeCallback( + new HandlerExecutor(Handler.getMain()), (IBinder who, int state) -> mNotifications.offer(state)); } diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java index 195a18a5f521..523fe1a8aa5d 100644 --- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java +++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java @@ -200,7 +200,7 @@ public class BinderFrozenStateChangeNotificationTest { IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> results.offer(who); try { - binder.addFrozenStateChangeCallback(callback); + binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); } catch (UnsupportedOperationException e) { return; } @@ -227,7 +227,7 @@ public class BinderFrozenStateChangeNotificationTest { final IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> queue.offer(state == IBinder.FrozenStateChangeCallback.STATE_FROZEN); - binder.addFrozenStateChangeCallback(callback); + binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); return callback; } catch (UnsupportedOperationException e) { return null; diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java index d0070678d4fa..f888c9ba93a9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java @@ -42,6 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; +import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) @@ -125,7 +126,7 @@ public class BinderDeathDispatcherTest { } @Override - public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(Executor e, FrozenStateChangeCallback callback) throws RemoteException { } -- GitLab From 0813431e4b7b5024bb80632ee58ee90c7c422643 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 14:50:23 -0800 Subject: [PATCH 042/656] TIS: Standardize TIS Scan Background Service Extensions API Define standardized AIDL interfaces for scan background extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I497a2ecedb1a5bd8cf6d99d484ace841c8a9c740 --- .../scanbsu/IScanBackgroundServiceUpdate.aidl | 32 +++++++++++++++++++ .../IScanBackgroundServiceUpdateListener.aidl | 31 ++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl create mode 100644 media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl new file mode 100644 index 000000000000..bda60edc48c1 --- /dev/null +++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdate.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.scanbsu; + +import android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdateListener; + +/** + * @hide + */ +interface IScanBackgroundServiceUpdate { + // Set the listener for background service update + // receives notifications for svl/tsl/nwl update during background service update. + void addBackgroundServiceUpdateListener(String clientToken, + in IScanBackgroundServiceUpdateListener listener); + // Remove the listener for background service update to stop receiving notifications + // for svl/tsl/nwl update during background service update. + void removeBackgroundServiceUpdateListener(in IScanBackgroundServiceUpdateListener listener); +} diff --git a/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl new file mode 100644 index 000000000000..d9bcbedb234a --- /dev/null +++ b/media/java/android/media/tv/extension/scanbsu/IScanBackgroundServiceUpdateListener.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.scanbsu; + +import android.os.Bundle; + +/** + * @hide + */ +interface IScanBackgroundServiceUpdateListener { + // On background service update add/delete/update svl records. + void onChannelListUpdate(String sessionToken, out Bundle[] updateInfos); + // On background service update add/delete/update nwl records. + void onNetworkListUpdate(String sessionToken, out Bundle[] updateInfos); + // On background service update add/delete/update tsl records. + void onTransportStreamingListUpdate(String sessionToken, out Bundle[] updateInfos); +} -- GitLab From 2dc2dfadd68e9b96c8e360f9ade25d36b0cac0d2 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 14:57:09 -0800 Subject: [PATCH 043/656] TIS: Standardize TIS Client Token Extensions API Define standardized AIDL interfaces for client token extention package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: Ie28201b52b43a39076148c988d9b32403cdbf30e --- .../extension/clienttoken/IClientToken.aidl | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 media/java/android/media/tv/extension/clienttoken/IClientToken.aidl diff --git a/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl b/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl new file mode 100644 index 000000000000..fa701b3c01ad --- /dev/null +++ b/media/java/android/media/tv/extension/clienttoken/IClientToken.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.clienttoken; + +/** + * @hide + */ +interface IClientToken { + String generateClientToken(); +} -- GitLab From 2458b9d2adec6ee9b80e9bc87a39206f80dc0968 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 15:15:50 -0800 Subject: [PATCH 044/656] TIS: Standardize TIS Screen Mode Setting Extensions API Define standardized AIDL interfaces for screen mode setting extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: Ib38880de9cd7bf8874fba6b27e078f1864161cc6 --- .../screenmode/IScreenModeSettings.aidl | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl diff --git a/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl new file mode 100644 index 000000000000..57f3b4ab5017 --- /dev/null +++ b/media/java/android/media/tv/extension/screenmode/IScreenModeSettings.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.screenmode; + +/** + * @hide + */ +interface IScreenModeSettings { + // Set screen mode information using a JSON string. + void setScreenModeSettings(String sessionToken, String setting); + // Get the overscan index which TIS session is applied. + int getOverScanIndex(String sessionToken); + // Get status that TIS session is support overscan or not. + boolean getSupportApplyOverScan(String sessionToken); +} -- GitLab From f93e213c4085844d43420892bdf9102cec40458b Mon Sep 17 00:00:00 2001 From: Toshiki Kikuchi Date: Fri, 15 Nov 2024 00:27:48 +0000 Subject: [PATCH 045/656] Reland "Add a functional test for no-desktop-task-limit" This reverts commit aaa17a0071ba3e61c2f8283a8d68941684f1045a. Reason for revert: reland after fixing the compat issue with ag/30336343 Change-Id: Ief255f9d56dd091861818dffd81d2a3618ca9c13 --- .../shell/functional/OpenUnlimitedAppsTest.kt | 27 +++++++ .../wm/shell/scenarios/OpenUnlimitedApps.kt | 73 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt create mode 100644 libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt new file mode 100644 index 000000000000..9462f15335de --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenUnlimitedAppsTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.OpenUnlimitedApps +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [OpenUnlimitedApps]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class OpenUnlimitedAppsTest : OpenUnlimitedApps() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt new file mode 100644 index 000000000000..367c4a437018 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenUnlimitedApps.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.scenarios + +import android.app.Instrumentation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.MailAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Test +/** + * Base scenario test for opening many apps on the device without the window limit. + */ +@Ignore("Test Base Class") +abstract class OpenUnlimitedApps() +{ + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + + private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation)) + + private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + Assume.assumeTrue(maxNum == 0) + testApp.enterDesktopMode(wmHelper, device) + } + + @Test + open fun openUnlimitedApps() { + // The maximum number of active tasks is infinite. We here use 12 as a large enough number. + val openTaskNum = 12 + + // Launch new [openTaskNum] tasks. + for (i in 1..openTaskNum) { + mailApp.launchViaIntent(wmHelper) + } + } + + @After + fun teardown() { + testApp.exit(wmHelper) + mailApp.exit(wmHelper) + } +} -- GitLab From cc191c3cadc4faa23faaa1dcadb867bb685dba1a Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 16:26:41 -0800 Subject: [PATCH 046/656] TIS: Standardize TIS Service Database Extensions API Define standardized AIDL interfaces for service database extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I418cdee4d467c0da0e06aee17964b00f6c6ae83a --- .../servicedb/IChannelListTransfer.aidl | 29 +++++++ .../tv/extension/servicedb/IServiceList.aidl | 30 +++++++ .../extension/servicedb/IServiceListEdit.aidl | 81 +++++++++++++++++++ .../servicedb/IServiceListEditListener.aidl | 24 ++++++ .../servicedb/IServiceListExportListener.aidl | 24 ++++++ .../servicedb/IServiceListExportSession.aidl | 30 +++++++ .../servicedb/IServiceListImportListener.aidl | 25 ++++++ .../servicedb/IServiceListImportSession.aidl | 32 ++++++++ .../IServiceListSetChannelListListener.aidl | 24 ++++++ .../IServiceListSetChannelListSession.aidl | 29 +++++++ .../IServiceListTransferInterface.aidl | 31 +++++++ 11 files changed, 359 insertions(+) create mode 100644 media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceList.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl create mode 100644 media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl diff --git a/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl new file mode 100644 index 000000000000..cb6aeccb5b57 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IChannelListTransfer.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IChannelListTransfer { + // Parse XML file and import Channels information. + void importChannelList(in ParcelFileDescriptor pfd); + // Get Channels information for export and create XML file. + void exportChannelList(in ParcelFileDescriptor pfd); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceList.aidl b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl new file mode 100644 index 000000000000..51daa80ccbd6 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceList.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceList { + // Get a list of the Service list IDs quivalent to COLUMN_CHANNEL_LIST_ID + // in the Channels table of TvProvider. + String[] getServiceListIds(); + // Get the information associated with the Service list. + Bundle getServiceListInfo(String serviceListId, in String[] keys); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl new file mode 100644 index 000000000000..1b1577ffc015 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListEdit.aidl @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.media.tv.extension.servicedb.IServiceListEditListener; +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceListEdit { + // Open in edit mode. Must call close() after edit is done. + int open(IServiceListEditListener listener); + // Method to close in edit mode. + int close(); + // Method to commit changes made to service database. + int commit(); + // Method to commit and close the changes. + int userEditCommit(); + + // Get a service/transportStream/Network/Satellite record information specified by + // serviceInfoId and keys from tvdb. + Bundle getServiceInfoFromDatabase(String serviceInfoId, in String[] keys); + // Get a list of all service records' information specified by serviceListId and keys from tvdb. + Bundle getServiceInfoListFromDatabase(String serviceListId, in String[] keys); + // Get a list of all service info IDs in the service list of serviceListId from tvdb. + String[] getServiceInfoIdsFromDatabase(String inServiceListId); + // Update a service information by the contents of serviceInfo; + int updateServiceInfoFromDatabase(in Bundle updateServiceInfo); + // Update all service information by the contents of serviceInfoList. + int updateServiceInfoByListFromDatabase(in Bundle[] updateServiceInfoList); + // Remove a service information of the serviceInfoId from the service list. + int removeServiceInfoFromDatabase(String serviceInfoId); + // Remove all service information of the serviceInfoId from the service list. + int removeServiceInfoByListFromDatabase(in String[] serviceInfoIdList); + // Get a list of the Service list IDs which is equivalent to COLUMN_CHANNEL_LIST_ID + // in Channels table from tv db. + String[] getServiceListChannelIds(); + // Get the information associated with the Service list Channel id. + Bundle getServiceListInfoByChannelId(String serviceListChannelId, in String[] keys); + + // Get a list of transportStream records' information specified by serviceListId and keys. + Bundle getTransportStreamInfoList(String serviceListId, in String[] keys); + // Get a list of transportStream records' information specified by serviceListId and keys + // from work db. + Bundle getTransportStreamInfoListForce(String serviceListId, in String[] keys); + + // Get a list of network records' information specified by serviceListId and keys. + Bundle getNetworkInfoList(String serviceListId, in String[] keys); + // Get a list of satellite records' information specified by serviceListId and keys. + Bundle getSatelliteInfoList(String serviceListId, in String[] keys); + + // Decompress whole bundle value of single service/transportStream/Network/Satellite record. + // RecordInfoBundle:a single record got from database by getServiceInfoFromDatabase() + String toRecordInfoByType(in Bundle recordInfoBundle, String recordType); + // Set channels(tv.db) modified result to middleware database(SVL/TSL/NWL/SATL). + int putRecordIdList(String serviceListId, in Bundle recordIdListBundle, int optType); + + // Add predefined ServiceListInfo of Hotbird 13E in scan two satellite scene EU region + // following by commit(). + String addPredefinedServiceListInfo(int broadcastType, String serviceListType, + String serviceListPrefix, String countryCode, int operatorId); + // Add predefined channels of Hotbird 13E in scan two satellite scene EU region. + int addPredefinedChannelList(String serviceListId, in Bundle[] predefinedListBundle); + // Add predefined satellite info of Hotbird 13E in scan two satellite scene EU region. + int addPredefinedSatInfo(String serviceListId, in Bundle predefinedSatInfoBundle); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl new file mode 100644 index 000000000000..e227eda47d4f --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListEditListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListEditListener { + void onCompleted(int requestId, int result); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl new file mode 100644 index 000000000000..c57e8f97ed4c --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListExportListener { + void onExported(int exportResult); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl new file mode 100644 index 000000000000..fcde581548f6 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListExportSession.aidl @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IServiceListExportSession { + // Start export service list with reserved parameters. + int exportServiceList(in ParcelFileDescriptor pfd, in Bundle exportParams); + // Release export resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl new file mode 100644 index 000000000000..abd8320df11d --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportListener.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +/** + * @hide + */ +interface IServiceListImportListener { + void onImported(int importResult); + void onPreloaded(int preloadResult); +} \ No newline at end of file diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl new file mode 100644 index 000000000000..1f1ae010a444 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListImportSession.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +/** + * @hide + */ +interface IServiceListImportSession { + // Start import service list. Should call after preload and before release. + int importServiceList(in ParcelFileDescriptor pfd, in Bundle importParams); + // Preparing for import. + int preload(in ParcelFileDescriptor pfd); + // Release import resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl new file mode 100644 index 000000000000..7c9c5c8a8048 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +/** + * @hide + */ +oneway interface IServiceListSetChannelListListener { + void onCompleted(int setChannelListResult); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl new file mode 100644 index 000000000000..b0527b3709b7 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListSetChannelListSession.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.os.Bundle; + +/** + * @hide + */ +interface IServiceListSetChannelListSession { + // Set channelList with channelinfo bundles, serviceListInfo, and operation type. + int setChannelList(in Bundle[] channelsInfo, in Bundle ServiceListInfoBundle, int optType); + // Release set channellist resources. + int release(); +} diff --git a/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl new file mode 100644 index 000000000000..91fb15728b06 --- /dev/null +++ b/media/java/android/media/tv/extension/servicedb/IServiceListTransferInterface.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.servicedb; + +import android.media.tv.extension.servicedb.IServiceListExportListener; +import android.media.tv.extension.servicedb.IServiceListImportListener; +import android.media.tv.extension.servicedb.IServiceListSetChannelListListener; +import android.os.IBinder; + +/** + * @hide + */ +interface IServiceListTransferInterface { + IBinder createExportSession(in IServiceListExportListener listener); + IBinder createImportSession(in IServiceListImportListener listener); + IBinder createSetChannelListSession(in IServiceListSetChannelListListener listener); +} -- GitLab From d098844c439c5ba55e5919bfc770dc450058d910 Mon Sep 17 00:00:00 2001 From: Toshiki Kikuchi Date: Fri, 15 Nov 2024 10:46:23 +0900 Subject: [PATCH 047/656] Activate DesktopWallpaper when fullscreen task is forced to be freeform This CL lets the controller launch the desktop wallpaper when an opening fullscreen task is forced to be freeform. Otherwise, we can unexpectedly see the Launcher activity even after the desktop task is opened. Bug: 375370653 Bug: 375375114 Flag: EXEMPT bug fix Test: DesktopTasksControllerTest Change-Id: I3df6ef1d0ca8d7e1c796adfac52d8e4bab938bec --- .../wm/shell/desktopmode/DesktopTasksController.kt | 7 +++++-- .../wm/shell/desktopmode/DesktopTasksControllerTest.kt | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 4db0be5c9025..982056bbcded 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -1598,8 +1598,11 @@ class DesktopTasksController( return WindowContainerTransaction().also { wct -> addMoveToDesktopChanges(wct, task) // In some launches home task is moved behind new task being launched. Make sure - // that's not the case for launches in desktop. - if (task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0) { + // that's not the case for launches in desktop. Also, if this launch is the first + // one to trigger the desktop mode (e.g., when [forceEnterDesktop()]), activate the + // desktop mode here. + if (task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0 + || !isDesktopModeShowing(task.displayId)) { bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId) wct.reorder(task.token, true) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 2319716617bf..a9f49e48ffb8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -1951,6 +1951,7 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() { whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true) val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! @@ -1962,8 +1963,13 @@ class DesktopTasksControllerTest : ShellTestCase() { assertNotNull(wct, "should handle request") assertThat(wct.changes[fullscreenTask.token.asBinder()]?.windowingMode) .isEqualTo(WINDOWING_MODE_UNDEFINED) - assertThat(wct.hierarchyOps).hasSize(1) - wct.assertReorderAt(0, fullscreenTask, toTop = true) + assertThat(wct.hierarchyOps).hasSize(3) + // There are 3 hops that are happening in this case: + // 1. Moving the fullscreen task to top as we add moveToDesktop() changes + // 2. Pending intent for the wallpaper + // 3. Bringing the fullscreen task back at the top + wct.assertPendingIntentAt(1, desktopWallpaperIntent) + wct.assertReorderAt(2, fullscreenTask, toTop = true) } @Test -- GitLab From 416ee1d47e3e91d7051760308a00bcb4be0c90a4 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 17:50:38 -0800 Subject: [PATCH 048/656] TIS: Standardize TIS Analog Extensions API Define standardized AIDL interfaces for analog extention package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I4d22310b1c5bbd21a88ddca66469cf15e6272ce1 --- .../analog/IAnalogAttributeInterface.aidl | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl diff --git a/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl new file mode 100644 index 000000000000..550acba8ed65 --- /dev/null +++ b/media/java/android/media/tv/extension/analog/IAnalogAttributeInterface.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.analog; + +/** + * @hide + */ +interface IAnalogAttributeInterface { + int getVersion(); + void setColorSystemCapability(in String[] list); + String[] getColorSystemCapability(); +} -- GitLab From 43fba9a0fce262214531bd9aee9c5c88f5e187e0 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Thu, 14 Nov 2024 16:52:02 -0800 Subject: [PATCH 049/656] TIS: Standardize TIS Event Extensions API Define standardized AIDL interfaces for event extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I1b89d2e3f93ff41adf5e5ab6a363b63de3628701 --- .../tv/extension/event/IEventDownload.aidl | 29 +++++++++++++ .../event/IEventDownloadListener.aidl | 26 +++++++++++ .../event/IEventDownloadSession.aidl | 38 ++++++++++++++++ .../tv/extension/event/IEventMonitor.aidl | 43 +++++++++++++++++++ .../event/IEventMonitorListener.aidl | 26 +++++++++++ 5 files changed, 162 insertions(+) create mode 100644 media/java/android/media/tv/extension/event/IEventDownload.aidl create mode 100644 media/java/android/media/tv/extension/event/IEventDownloadListener.aidl create mode 100644 media/java/android/media/tv/extension/event/IEventDownloadSession.aidl create mode 100644 media/java/android/media/tv/extension/event/IEventMonitor.aidl create mode 100644 media/java/android/media/tv/extension/event/IEventMonitorListener.aidl diff --git a/media/java/android/media/tv/extension/event/IEventDownload.aidl b/media/java/android/media/tv/extension/event/IEventDownload.aidl new file mode 100644 index 000000000000..29c7553e1014 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownload.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.event; + +import android.media.tv.extension.event.IEventDownloadListener; +import android.os.Bundle; +import android.os.IBinder; + +/** + * @hide + */ +interface IEventDownload { + // Create an event download session and return it as a Ibinder for DVB/DTMB + IBinder createSession(in Bundle eventDownloadParams, in IEventDownloadListener listener); +} diff --git a/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl new file mode 100644 index 000000000000..6d7d61f15d27 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownloadListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.event; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IEventDownloadListener { + void onCompleted(in Bundle status); +} diff --git a/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl new file mode 100644 index 000000000000..fe7ee37b324b --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventDownloadSession.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.event; + +import android.net.Uri; +import android.os.Bundle; + +/** + * @hide + */ +interface IEventDownloadSession { + // Determine to execute barker channel or silent tune flow for related service type + int isBarkerOrSequentialDownloadByServiceType(in Bundle eventDownloadParams); + // Determine whether to start barker channel or silent tune flow. + int isBarkerOrSequentialDownloadByServiceRecord(in Bundle eventDownloadParams); + // Start event download. + void startTuningMultiplex(in Uri channelUri); + // Set active window channels. + void setActiveWindowChannelInfo(in Uri[] activeWinChannelInfos); + // Cancel barker channel or silent tune flow. + void cancel(); + // Release barker channel or silent tune flow. + void release(); +} diff --git a/media/java/android/media/tv/extension/event/IEventMonitor.aidl b/media/java/android/media/tv/extension/event/IEventMonitor.aidl new file mode 100644 index 000000000000..f6e7bd134e78 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventMonitor.aidl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.event; + +import android.media.tv.extension.event.IEventMonitorListener; +import android.net.Uri; +import android.os.Bundle; + +/** + * @hide + */ +interface IEventMonitor { + // Get present event information. + Bundle getPresentEventInfo(long channelDbId); + // Add present event information listener. + void addPresentEventInfoListener(in IEventMonitorListener listener); + // Remove present event information listener. + void removePresentEventInfoListener(in IEventMonitorListener listener); + // Get following event information. + Bundle getFollowingEventInfo(long channelDbId); + // Add following event information listener. + void addFollowingEventInfoListener(in IEventMonitorListener listener); + // Remove following event information listener. + void removeFollowingEventInfoListener(in IEventMonitorListener listener); + // Get SDT guidance information. + Bundle getSdtGuidanceInfo(long channelDbId); + // Set Event Background channel list info. + void setBgmTuneChannelInfo(in Uri[] tuneChannelInfos); +} diff --git a/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl new file mode 100644 index 000000000000..a00e5422a7e0 --- /dev/null +++ b/media/java/android/media/tv/extension/event/IEventMonitorListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.event; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IEventMonitorListener { + void onInfoChanged(long channelDbId, in Bundle eventinfo); +} -- GitLab From 4a44565881b174f094d37b7879e24efe79820a95 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Thu, 14 Nov 2024 17:38:37 +0800 Subject: [PATCH 050/656] Defer orientation for transient launch with immersive app policy Because com.android.quickstep.util.RecentsOrientedState doesn't support well for rotated app on large screen (the TaskView will rotate unexpectedly), make the transition becomes 2 steps. e.g. swiping an auto pip from landscape to portrait, it will play pip animation in landscape. And then play a screen rotation from landscape to portrait. This also fixes suddenly orientation change if immersive rotation policy is enabled when clicking navigation bar that launches recents. Bug: 378422563 Flag: EXEMPT bugfix Test: atest DisplayRotationImmersiveAppCompatPolicyTests# \ testDeferOrientationUpdate Test: Enable large screen env: config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled adb shell cmd window set-ignore-orientation-request 1 adb shell wm size 2200x1600 Enable auto-rotation Launch a fixed orientation fullscreen landscape app (hide bars). Rotate device 90 degree. Swipe up navigation bar or press navigation button. The orientation should not be changed until the transition of navigation is done. Change-Id: I26db2ff64d2464b0ec9559f0416e86ab5d09f3d4 --- .../android/server/wm/DisplayRotation.java | 3 ++ ...splayRotationImmersiveAppCompatPolicy.java | 34 +++++++++++++++++++ ...RotationImmersiveAppCompatPolicyTests.java | 23 +++++++++++++ 3 files changed, 60 insertions(+) diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 4cf1fb400fe7..df209ff4cf50 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -485,6 +485,9 @@ public class DisplayRotation { if (isDefaultDisplay) { updateOrientationListenerLw(); } + } else if (mCompatPolicyForImmersiveApps != null + && mCompatPolicyForImmersiveApps.deferOrientationUpdate()) { + return false; } return updateRotationUnchecked(forceUpdate); } diff --git a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java index 094434d07cfe..046ed614dc19 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java @@ -17,10 +17,13 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; +import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Configuration.Orientation; @@ -65,6 +68,37 @@ final class DisplayRotationImmersiveAppCompatPolicy { mDisplayContent = displayContent; } + /** + * Returns {@code true} if the orientation update should be skipped and it will update when + * transition is done. This is to keep the orientation which was preserved by + * {@link #isRotationLockEnforced} from being changed by a transient launch (i.e. recents). + */ + boolean deferOrientationUpdate() { + if (mDisplayRotation.getUserRotation() != USER_ROTATION_FREE + || mDisplayRotation.getLastOrientation() != SCREEN_ORIENTATION_UNSPECIFIED) { + return false; + } + final WindowOrientationListener orientationListener = + mDisplayRotation.getOrientationListener(); + if (orientationListener == null + || orientationListener.getProposedRotation() == mDisplayRotation.getRotation()) { + return false; + } + // The above conditions mean that isRotationLockEnforced might have taken effect: + // Auto-rotation is enabled and the proposed rotation is not applied. + // Then the update should defer until the transition idle to avoid disturbing animation. + if (!mDisplayContent.mTransitionController.hasTransientLaunch(mDisplayContent)) { + return false; + } + mDisplayContent.mTransitionController.mStateValidators.add(() -> { + if (!isRotationLockEnforcedLocked(orientationListener.getProposedRotation())) { + mDisplayContent.mWmService.updateRotation(false /* alwaysSendConfiguration */, + false /* forceRelayout */); + } + }); + return true; + } + /** * Decides whether it is necessary to lock screen rotation, preventing auto rotation, based on * the top activity configuration and proposed screen rotation. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java index c8fc4822259e..63973345b5fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java @@ -20,16 +20,19 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -54,6 +57,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas private DisplayRotationImmersiveAppCompatPolicy mPolicy; + private DisplayRotation mMockDisplayRotation; private AppCompatConfiguration mMockAppCompatConfiguration; private ActivityRecord mMockActivityRecord; private Task mMockTask; @@ -98,6 +102,7 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_90)).thenReturn(true); when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_180)).thenReturn(false); when(mockDisplayRotation.isLandscapeOrSeascape(Surface.ROTATION_270)).thenReturn(true); + mMockDisplayRotation = mockDisplayRotation; return mockDisplayRotation; } @@ -195,6 +200,24 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas assertIsRotationLockEnforcedReturnsFalseForAllRotations(); } + @Test + public void testDeferOrientationUpdate() { + assertFalse(mPolicy.deferOrientationUpdate()); + + doReturn(SCREEN_ORIENTATION_UNSPECIFIED).when(mMockDisplayRotation).getLastOrientation(); + final WindowOrientationListener orientationListener = mock(WindowOrientationListener.class); + doReturn(Surface.ROTATION_90).when(orientationListener).getProposedRotation(); + doReturn(orientationListener).when(mMockDisplayRotation).getOrientationListener(); + spyOn(mDisplayContent.mTransitionController); + doReturn(true).when(mDisplayContent.mTransitionController) + .hasTransientLaunch(mDisplayContent); + + assertTrue(mPolicy.deferOrientationUpdate()); + mDisplayContent.mTransitionController.mStateValidators.getFirst().run(); + + verify(mWm).updateRotation(false, false); + } + @Test public void testRotationChoiceEnforcedOnly_nullTopRunningActivity_lockNotEnforced() { when(mDisplayContent.topRunningActivity()).thenReturn(null); -- GitLab From 095c6db7b5251941c99cf42aa26b28ccd1d6778f Mon Sep 17 00:00:00 2001 From: Rambo Wang Date: Fri, 15 Nov 2024 02:37:23 +0000 Subject: [PATCH 051/656] Clean up TODO comment for TelephonyManager.getIccAuthentication This change removes the redundant TODO comment since he API has been protected with settled-down permission USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER. Bug: 73660190 Change-Id: Ic419dc6da464e257e64fcb02ad2e5db5f54450b1 Test: m -j Flag: EXEMPT clean up --- telephony/java/android/telephony/TelephonyManager.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6f2c8623fd71..1a45701428e2 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -8825,9 +8825,6 @@ public class TelephonyManager { * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION} or doesn't support given * authType. */ - // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not - // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since - // it's not public API. @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) public String getIccAuthentication(int appType, @AuthType int authType, String data) { return getIccAuthentication(getSubId(), appType, authType, data); -- GitLab From 6b4d6ed074fd7c19a6f9074e36a84db26fefe28f Mon Sep 17 00:00:00 2001 From: mattsziklay Date: Thu, 14 Nov 2024 22:10:40 -0800 Subject: [PATCH 052/656] Use addMoveToFullscreenChanges on fullscreen launch. When a desktop is minimized and a task contained inside it is then launched in fullscreen, the windowing mode is always set to WINDOWING_MODE_FULLSCREEN. However, when the display windowing is fullscreen, the task should be set to WINDOWING_MODE_UNDEFINED to prevent issues with future split screen transitions (see ag/23624076 for more details). This is already done for other freeform -> fullscreen transitions, this CL just applies the logic to task launches. Bug: 372809656 Test: Manual Flag: EXEMPT bugfix Change-Id: Ib05f7b9912d3cc3b834b9e3217a56337de3c163d --- .../android/wm/shell/desktopmode/DesktopTasksController.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 223038f84418..7112de33a180 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -1603,6 +1603,13 @@ class DesktopTasksController( transition, wct, task.displayId ) } + } else if (taskRepository.isActiveTask(task.taskId)) { + // If a freeform task receives a request for a fullscreen launch, apply the same + // changes we do for similar transitions. The task not having WINDOWING_MODE_UNDEFINED + // set when needed can interfere with future split / multi-instance transitions. + return WindowContainerTransaction().also { wct -> + addMoveToFullscreenChanges(wct, task) + } } return null } -- GitLab From 6e7ebccd4138036190a17ca862955f7f3d11bada Mon Sep 17 00:00:00 2001 From: George Chang Date: Mon, 28 Oct 2024 07:47:02 +0000 Subject: [PATCH 053/656] Add a flag to enable IsTagIntentAllowed API Flag: android.nfc.nfc_check_tag_intent_preference Bug: 335916336 Test: device_config put nfc android.nfc.nfc_check_tag_intent_preference true Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAllowed Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAppPreferenceSupported Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAllowedWhenNotSupported Change-Id: Ifa65d2fe627bc7fa61acc8b806d3daf204a563fb --- nfc/java/android/nfc/flags.aconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig index 8a37aa28cf9d..ee287aba709f 100644 --- a/nfc/java/android/nfc/flags.aconfig +++ b/nfc/java/android/nfc/flags.aconfig @@ -181,3 +181,11 @@ flag { description: "Enable set service enabled for category other" bug: "338157113" } + +flag { + name: "nfc_check_tag_intent_preference" + is_exported: true + namespace: "nfc" + description: "App can check its tag intent preference status" + bug: "335916336" +} -- GitLab From e504d3d4786357ffecebafc98f964ab0904ebf98 Mon Sep 17 00:00:00 2001 From: George Chang Date: Mon, 28 Oct 2024 09:38:14 +0000 Subject: [PATCH 054/656] Update isTagIntentAllowed and isTagIntentAppPreferenceSupported Add new API isTagIntentAllowed and expose isTagIntentAppPreferenceSupported as public API. Flag: android.nfc.nfc_check_tag_intent_preference Bug: 335916336 Test: m framework-nfc.stubs.source.system-update-current-api Test: m framework-nfc.stubs.source-update-current-api Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAllowed Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAppPreferenceSupported Test: atest android.nfc.cts.NfcAdapterTest#testIsTagIntentAllowedWhenNotSupported Change-Id: I502c31ec8ba876d9a0c35a8d5447343d27ef678d --- nfc/api/current.txt | 3 ++ nfc/api/system-current.txt | 1 - nfc/java/android/nfc/INfcAdapter.aidl | 1 + nfc/java/android/nfc/NfcAdapter.java | 51 +++++++++++++++++++++++---- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 008120429c40..9a7a39f213ee 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -81,11 +81,14 @@ package android.nfc { method @FlaggedApi("android.nfc.enable_nfc_reader_option") public boolean isReaderOptionSupported(); method public boolean isSecureNfcEnabled(); method public boolean isSecureNfcSupported(); + method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAllowed(); + method @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public boolean isTagIntentAppPreferenceSupported(); method @FlaggedApi("android.nfc.enable_nfc_charging") public boolean isWlcEnabled(); method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void resetDiscoveryTechnology(@NonNull android.app.Activity); method @FlaggedApi("android.nfc.enable_nfc_set_discovery_tech") public void setDiscoveryTechnology(@NonNull android.app.Activity, int, int); method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setObserveModeEnabled(boolean); field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED"; + field @FlaggedApi("android.nfc.nfc_check_tag_intent_preference") public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE = "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE"; field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED"; field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED"; field public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED"; diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index f587660cae5b..72447b1033d3 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -11,7 +11,6 @@ package android.nfc { method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map getTagIntentAppPreferenceForUser(int); method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn(); method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOnSupported(); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagIntentAppPreferenceSupported(); method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener); method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerNfcVendorNciCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.NfcVendorNciCallback); method @FlaggedApi("android.nfc.enable_nfc_charging") public void registerWlcStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.WlcStateListener); diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 31514a09adad..a08b55fe86b8 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -121,4 +121,5 @@ interface INfcAdapter List getRoutingTableEntryList(); void indicateDataMigration(boolean inProgress, String pkg); int commitRouting(); + boolean isTagIntentAllowed(in String pkg, in int Userid); } diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java index c5d8191b22e6..056844f38f3c 100644 --- a/nfc/java/android/nfc/NfcAdapter.java +++ b/nfc/java/android/nfc/NfcAdapter.java @@ -49,6 +49,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; import java.io.IOException; @@ -2505,22 +2506,22 @@ public final class NfcAdapter { } /** - * Checks if the device supports Tag application preference. + * Checks if the device supports Tag Intent App Preference functionality. + * + * When supported, {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED} or + * {@link #ACTION_TAG_DISCOVERED} will not be dispatched to an Activity if + * {@link isTagIntentAllowed} returns {@code false}. * * @return {@code true} if the device supports Tag application preference, {@code false} * otherwise * @throws UnsupportedOperationException if FEATURE_NFC is unavailable - * - * @hide */ - @SystemApi - @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) public boolean isTagIntentAppPreferenceSupported() { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } return callServiceReturn(() -> sService.isTagIntentAppPreferenceSupported(), false); - } /** @@ -2895,4 +2896,42 @@ public final class NfcAdapter { } return mNfcOemExtension; } + + /** + * Activity action: Bring up the settings page that allows the user to enable or disable tag + * intent reception for apps. + * + *

This will direct user to the settings page shows a list that asks users whether + * they want to allow or disallow the package to start an activity when a tag is discovered. + * + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) + public static final String ACTION_CHANGE_TAG_INTENT_PREFERENCE = + "android.nfc.action.CHANGE_TAG_INTENT_PREFERENCE"; + + /** + * Checks whether the user has disabled the calling app from receiving NFC tag intents. + * + *

This method checks whether the caller package name is either not present in the user + * disabled list or is explicitly allowed by the user. + * + * @return {@code true} if an app is either not present in the list or is added to the list + * with the flag set to {@code true}. Otherwise, it returns {@code false}. + * It also returns {@code true} if {@link isTagIntentAppPreferenceSupported} returns + * {@code false}. + * + * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. + */ + @FlaggedApi(Flags.FLAG_NFC_CHECK_TAG_INTENT_PREFERENCE) + public boolean isTagIntentAllowed() { + if (!sHasNfcFeature) { + throw new UnsupportedOperationException(); + } + if (!isTagIntentAppPreferenceSupported()) { + return true; + } + return callServiceReturn(() -> sService.isTagIntentAllowed(mContext.getPackageName(), + UserHandle.myUserId()), false); + } } -- GitLab From 737b91f90cf593104213f31fa1f60b55414bcd9a Mon Sep 17 00:00:00 2001 From: Sanjana Sunil Date: Wed, 6 Nov 2024 16:23:11 +0000 Subject: [PATCH 055/656] Allow dependency installation if caller has suitable permission If the calling app has INSTALL_DEPENDENCY_SHARED_LIBRARIES permission and the package being installed is a dependency type, allow installation to proceed with no further action. Otherwise, procees with the normal flow of using user action. Bug: 372861622 Test: atest PackageManagerShellCommandInstallTest Flag: android.content.pm.sdk_dependency_installer Change-Id: I3ebe5d4c7850cc249cd25522b422f3c678c8c110 --- .../android/content/pm/parsing/ApkLite.java | 21 ++++++++++++++++--- .../content/pm/parsing/ApkLiteParseUtils.java | 4 +++- .../content/pm/parsing/PackageLite.java | 17 +++++++++++++-- packages/Shell/AndroidManifest.xml | 1 + .../server/pm/PackageInstallerSession.java | 16 +++++++++++++- 5 files changed, 52 insertions(+), 7 deletions(-) diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java index c83cf9601cd7..1d8209da6559 100644 --- a/core/java/android/content/pm/parsing/ApkLite.java +++ b/core/java/android/content/pm/parsing/ApkLite.java @@ -141,6 +141,11 @@ public class ApkLite { */ private final boolean mIsSdkLibrary; + /** + * Indicates if this apk is a static library. + */ + private final boolean mIsStaticLibrary; + /** * List of SDK names used by this apk. */ @@ -191,7 +196,7 @@ public class ApkLite { Set requiredSplitTypes, Set splitTypes, boolean hasDeviceAdminReceiver, boolean isSdkLibrary, List usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor, - String[][] usesSdkLibrariesCertDigests, + String[][] usesSdkLibrariesCertDigests, boolean isStaticLibrary, List usesStaticLibraries, long[] usesStaticLibrariesVersionsMajor, String[][] usesStaticLibrariesCertDigests, boolean updatableSystem, @@ -229,6 +234,7 @@ public class ApkLite { mRollbackDataPolicy = rollbackDataPolicy; mHasDeviceAdminReceiver = hasDeviceAdminReceiver; mIsSdkLibrary = isSdkLibrary; + mIsStaticLibrary = isStaticLibrary; mUsesSdkLibraries = usesSdkLibraries; mUsesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor; mUsesSdkLibrariesCertDigests = usesSdkLibrariesCertDigests; @@ -275,6 +281,7 @@ public class ApkLite { mRollbackDataPolicy = 0; mHasDeviceAdminReceiver = false; mIsSdkLibrary = false; + mIsStaticLibrary = false; mUsesSdkLibraries = Collections.emptyList(); mUsesSdkLibrariesVersionsMajor = null; mUsesSdkLibrariesCertDigests = null; @@ -593,6 +600,14 @@ public class ApkLite { return mIsSdkLibrary; } + /** + * Indicates if this apk is a static library. + */ + @DataClass.Generated.Member + public boolean isIsStaticLibrary() { + return mIsStaticLibrary; + } + /** * List of SDK names used by this apk. */ @@ -662,10 +677,10 @@ public class ApkLite { } @DataClass.Generated( - time = 1730202160705L, + time = 1731589363302L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ApkLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.annotation.Nullable java.lang.String mUsesSplitName\nprivate final @android.annotation.Nullable java.lang.String mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mRevisionCode\nprivate final int mInstallLocation\nprivate final int mMinSdkVersion\nprivate final int mTargetSdkVersion\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final boolean mFeatureSplit\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mProfileableByShell\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mUseEmbeddedDex\nprivate final @android.annotation.Nullable java.lang.String mTargetPackageName\nprivate final boolean mOverlayIsStatic\nprivate final int mOverlayPriority\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyName\nprivate final @android.annotation.Nullable java.lang.String mRequiredSystemPropertyValue\nprivate final int mRollbackDataPolicy\nprivate final boolean mHasDeviceAdminReceiver\nprivate final boolean mIsSdkLibrary\nprivate final boolean mIsStaticLibrary\nprivate final @android.annotation.NonNull java.util.List mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final boolean mUpdatableSystem\nprivate final @android.annotation.Nullable java.lang.String mEmergencyInstaller\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass ApkLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index e9e8578af787..bd2adb7cc821 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -465,6 +465,7 @@ public class ApkLiteParseUtils { boolean hasDeviceAdminReceiver = false; boolean isSdkLibrary = false; + boolean isStaticLibrary = false; List usesSdkLibraries = new ArrayList<>(); long[] usesSdkLibrariesVersionsMajor = new long[0]; String[][] usesSdkLibrariesCertDigests = new String[0][0]; @@ -653,6 +654,7 @@ public class ApkLiteParseUtils { SharedLibraryInfo.TYPE_SDK_PACKAGE)); break; case TAG_STATIC_LIBRARY: + isSdkLibrary = true; // Mirrors ParsingPackageUtils#parseStaticLibrary until lite and full // parsing are combined String staticLibName = parser.getAttributeValue( @@ -806,7 +808,7 @@ public class ApkLiteParseUtils { requiredSystemPropertyValue, minSdkVersion, targetSdkVersion, rollbackDataPolicy, requiredSplitTypes.first, requiredSplitTypes.second, hasDeviceAdminReceiver, isSdkLibrary, usesSdkLibraries, - usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests, + usesSdkLibrariesVersionsMajor, usesSdkLibrariesCertDigests, isStaticLibrary, usesStaticLibraries, usesStaticLibrariesVersions, usesStaticLibrariesCertDigests, updatableSystem, emergencyInstaller, declaredLibraries)); diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java index 76b25fed2ac2..0e11eecfc7ec 100644 --- a/core/java/android/content/pm/parsing/PackageLite.java +++ b/core/java/android/content/pm/parsing/PackageLite.java @@ -114,6 +114,10 @@ public class PackageLite { * Indicates if this package is a sdk. */ private final boolean mIsSdkLibrary; + /** + * Indicates if this package is a static library. + */ + private final boolean mIsStaticLibrary; private final @NonNull List mUsesSdkLibraries; @@ -164,6 +168,7 @@ public class PackageLite { mUsesSdkLibraries = baseApk.getUsesSdkLibraries(); mUsesSdkLibrariesVersionsMajor = baseApk.getUsesSdkLibrariesVersionsMajor(); mUsesSdkLibrariesCertDigests = baseApk.getUsesSdkLibrariesCertDigests(); + mIsStaticLibrary = baseApk.isIsStaticLibrary(); mUsesStaticLibraries = baseApk.getUsesStaticLibraries(); mUsesStaticLibrariesVersions = baseApk.getUsesStaticLibrariesVersions(); mUsesStaticLibrariesCertDigests = baseApk.getUsesStaticLibrariesCertDigests(); @@ -455,6 +460,14 @@ public class PackageLite { return mIsSdkLibrary; } + /** + * Indicates if this package is a static library. + */ + @DataClass.Generated.Member + public boolean isIsStaticLibrary() { + return mIsStaticLibrary; + } + @DataClass.Generated.Member public @NonNull List getUsesSdkLibraries() { return mUsesSdkLibraries; @@ -499,10 +512,10 @@ public class PackageLite { } @DataClass.Generated( - time = 1730203707341L, + time = 1731591578587L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java", - inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final @android.annotation.NonNull java.util.List mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") + inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\nprivate final boolean mIsStaticLibrary\nprivate final @android.annotation.NonNull java.util.List mUsesSdkLibraries\nprivate final @android.annotation.Nullable long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesSdkLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mUsesStaticLibraries\nprivate final @android.annotation.Nullable long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.Nullable java.lang.String[][] mUsesStaticLibrariesCertDigests\nprivate final @android.annotation.NonNull java.util.List mDeclaredLibraries\nprivate final @android.annotation.Nullable android.content.pm.ArchivedPackageParcel mArchivedPackage\npublic java.util.List getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)") @Deprecated private void __metadata() {} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 526320debb1a..27553bfe96dc 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -179,6 +179,7 @@ + diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 505b7e6205df..58986dcda46f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1092,6 +1092,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isInstallDpcPackagesPermissionGranted = (snapshot.checkUidPermission( android.Manifest.permission.INSTALL_DPC_PACKAGES, mInstallerUid) == PackageManager.PERMISSION_GRANTED); + boolean isInstallDependencyPackagesPermissionGranted = false; + if (Flags.sdkDependencyInstaller()) { + isInstallDependencyPackagesPermissionGranted = (snapshot.checkUidPermission( + android.Manifest.permission.INSTALL_DEPENDENCY_SHARED_LIBRARIES, mInstallerUid) + == PackageManager.PERMISSION_GRANTED); + } // Also query the package uid for archived packages, so that the user confirmation // dialog can be displayed for updating archived apps. final int targetPackageUid = snapshot.getPackageUid(packageName, @@ -1113,10 +1119,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final boolean isSelfUpdate = targetPackageUid == mInstallerUid; final boolean isEmergencyInstall = isEmergencyInstallerEnabled(packageName, snapshot, userId, mInstallerUid); + boolean isSdkOrStaticLibraryInstall = false; + synchronized (mLock) { + if (mPackageLite != null) { + isSdkOrStaticLibraryInstall = + mPackageLite.isIsSdkLibrary() || mPackageLite.isIsStaticLibrary(); + } + } final boolean isPermissionGranted = isInstallPermissionGranted || (isUpdatePermissionGranted && isUpdate) || (isSelfUpdatePermissionGranted && isSelfUpdate) - || (isInstallDpcPackagesPermissionGranted && hasDeviceAdminReceiver); + || (isInstallDpcPackagesPermissionGranted && hasDeviceAdminReceiver) + || (isInstallDependencyPackagesPermissionGranted && isSdkOrStaticLibraryInstall); final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID); final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID); final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID); -- GitLab From 3fad2a3bca69c9ab9eb2cd2d81392280d22e4ab2 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Fri, 15 Nov 2024 12:31:42 +0800 Subject: [PATCH 056/656] Log if orientation is ignored by universal resizable This logs for two cases for ignored fixed orientation: 1. The activity is first launched with declared android:screenOrientation in the manifest. 2. The activity calls Activity#setRequestedOrientation at runtime. This makes it easier to see which activity's requested orientation is ignored. Bug: 377771481 Flag: com.android.window.flags.universal_resizable_by_default Test: atest SizeCompatTests#testUniversalResizeable Change-Id: I850b4b9143fc645ee410f52026047b9294aa77e0 --- .../com/android/server/wm/ActivityRecord.java | 28 ++++++++++++-- .../com/android/server/wm/DisplayArea.java | 2 - .../android/server/wm/WindowContainer.java | 38 +++++++++++-------- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 73ae51c6e64a..738ed1d37a64 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1622,6 +1622,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A newParent.setResumedActivity(this, "onParentChanged"); } mAppCompatController.getTransparentPolicy().start(); + if (mState == INITIALIZING && isRestrictedFixedOrientation(info.screenOrientation)) { + Slog.i(TAG, "Ignoring manifest-declared fixed orientation " + + ActivityInfo.screenOrientationToString(info.screenOrientation) + + " of " + this + " since target sdk 36"); + } } if (rootTask != null && rootTask.topRunningActivity() == this) { @@ -3177,6 +3182,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return getWindowConfiguration().canReceiveKeys() && !mWaitForEnteringPinnedMode; } + /** + * Returns {@code true} if the orientation will be ignored for {@link #isUniversalResizeable()}. + */ + private boolean isRestrictedFixedOrientation( + @ActivityInfo.ScreenOrientation int orientation) { + // Exclude "locked" because it is not explicit portrait or landscape. + return orientation != ActivityInfo.SCREEN_ORIENTATION_LOCKED + && ActivityInfo.isFixedOrientation(orientation) + && isUniversalResizeable(); + } + /** * Returns {@code true} if the fixed orientation, aspect ratio, resizability of this activity * will be ignored. @@ -8107,7 +8123,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_ORIENTATION, "Setting requested orientation %s for %s", ActivityInfo.screenOrientationToString(requestedOrientation), this); - setOrientation(requestedOrientation, this); + final int resolvedOrientation = setOrientation(requestedOrientation, this); + if (resolvedOrientation != requestedOrientation + && isRestrictedFixedOrientation(requestedOrientation)) { + Slog.i(TAG, "Ignoring requested fixed orientation " + + ActivityInfo.screenOrientationToString(requestedOrientation) + + " of " + this + " since target sdk 36"); + } // Push the new configuration to the requested app in case where it's not pushed, e.g. when // the request is handled at task level with letterbox. @@ -8198,9 +8220,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @ActivityInfo.ScreenOrientation protected int getOverrideOrientation() { int candidateOrientation = super.getOverrideOrientation(); - if (candidateOrientation != ActivityInfo.SCREEN_ORIENTATION_LOCKED - && ActivityInfo.isFixedOrientation(candidateOrientation) - && isUniversalResizeable()) { + if (isRestrictedFixedOrientation(candidateOrientation)) { candidateOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } return mAppCompatController.getOrientationPolicy() diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 3b2479885f57..f40d636b522a 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -101,8 +101,6 @@ public class DisplayArea extends WindowContainer { DisplayArea(WindowManagerService wms, Type type, String name, int featureId) { super(wms); - // TODO(display-area): move this up to ConfigurationContainer - setOverrideOrientation(SCREEN_ORIENTATION_UNSET); mType = type; mName = name; mFeatureId = featureId; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index e0c473de0f33..e5f6860241be 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1676,30 +1676,36 @@ class WindowContainer extends ConfigurationContainer< * @param orientation the specified orientation. Needs to be one of {@link ScreenOrientation}. * @param requestingContainer the container which orientation request has changed. Mostly used * to ensure it gets correct configuration. + * @return the resolved override orientation of this window container. */ - void setOrientation(@ScreenOrientation int orientation, + @ScreenOrientation + int setOrientation(@ScreenOrientation int orientation, @Nullable WindowContainer requestingContainer) { if (getOverrideOrientation() == orientation) { - return; + return orientation; } - setOverrideOrientation(orientation); final WindowContainer parent = getParent(); - if (parent != null) { - if (getConfiguration().orientation != getRequestedConfigurationOrientation() - // Update configuration directly only if the change won't be dispatched from - // ancestor. This prevents from computing intermediate configuration when the - // parent also needs to be updated from the ancestor. E.g. the app requests - // portrait but the task is still in landscape. While updating from display, - // the task can be updated to portrait first so the configuration can be - // computed in a consistent environment. - && (inMultiWindowMode() + if (parent == null) { + return orientation; + } + // The derived class can return a result that is different from the given orientation. + final int resolvedOrientation = getOverrideOrientation(); + if (getConfiguration().orientation != getRequestedConfigurationOrientation( + false /* forDisplay */, resolvedOrientation) + // Update configuration directly only if the change won't be dispatched from + // ancestor. This prevents from computing intermediate configuration when the + // parent also needs to be updated from the ancestor. E.g. the app requests + // portrait but the task is still in landscape. While updating from display, + // the task can be updated to portrait first so the configuration can be + // computed in a consistent environment. + && (inMultiWindowMode() || !handlesOrientationChangeFromDescendant(orientation))) { - // Resolve the requested orientation. - onConfigurationChanged(parent.getConfiguration()); - } - onDescendantOrientationChanged(requestingContainer); + // Resolve the requested orientation. + onConfigurationChanged(parent.getConfiguration()); } + onDescendantOrientationChanged(requestingContainer); + return resolvedOrientation; } @ScreenOrientation -- GitLab From 4faf0ca3bb80f104614e6280f1933d8ff1ce689c Mon Sep 17 00:00:00 2001 From: Yisroel Forta Date: Thu, 14 Nov 2024 12:31:36 +0000 Subject: [PATCH 057/656] Fix start info handling of isolated processes Currently isolated process uids are stored in their own container. This results in storing a large number of records that are not reasonable accessible. As an initial remedy, simply discard the records for isolated processes. This is done both when added, and when restored from disk so that old ones don't linger. In the future, we may want to store these under the parent UID. Bug: 374032823 Test: trigger isolated process start, confirm no record. Flag: com.android.server.am.app_start_info_isolated_process Change-Id: Icaeec18fb679c1e71ec9a0b250af7f5f4e18d87e --- .../server/am/AppStartInfoTracker.java | 25 ++++++++++++++++--- .../java/com/android/server/am/flags.aconfig | 10 ++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index 3913d2f2ea92..961022b7231b 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -634,12 +634,20 @@ public final class AppStartInfoTracker { } final ApplicationStartInfo info = new ApplicationStartInfo(raw); + int uid = raw.getRealUid(); - AppStartInfoContainer container = mData.get(raw.getPackageName(), raw.getRealUid()); + // Isolated process starts won't be reasonably accessible if stored by their uid, don't + // store them. + if (com.android.server.am.Flags.appStartInfoIsolatedProcess() + && UserHandle.isIsolated(uid)) { + return null; + } + + AppStartInfoContainer container = mData.get(raw.getPackageName(), uid); if (container == null) { container = new AppStartInfoContainer(mAppStartInfoHistoryListSize); - container.mUid = info.getRealUid(); - mData.put(raw.getPackageName(), raw.getRealUid(), container); + container.mUid = uid; + mData.put(raw.getPackageName(), uid, container); } container.addStartInfoLocked(info); @@ -1010,6 +1018,17 @@ public final class AppStartInfoTracker { new AppStartInfoContainer(mAppStartInfoHistoryListSize); int uid = container.readFromProto(proto, AppsStartInfoProto.Package.USERS, pkgName); + + // If the isolated process flag is enabled and the uid is that of an isolated + // process, then break early so that the container will not be added to mData. + // This is expected only as a one time mitigation, records added after this flag + // is enabled should always return false for isIsolated and thereby always + // continue on. + if (com.android.server.am.Flags.appStartInfoIsolatedProcess() + && UserHandle.isIsolated(uid)) { + break; + } + synchronized (mLock) { mData.put(pkgName, uid, container); } diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index c59c40fc9cd8..6d247d227774 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -270,3 +270,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "app_start_info_isolated_process" + namespace: "system_performance" + description: "Adjust handling of isolated process records to be discarded." + bug: "374032823" + metadata { + purpose: PURPOSE_BUGFIX + } +} -- GitLab From 941d05e69435fe2121bfa7b8eca673fdba669a99 Mon Sep 17 00:00:00 2001 From: Mina Granic Date: Fri, 15 Nov 2024 11:10:03 +0000 Subject: [PATCH 058/656] Simplify the camera compat state. Due to previously integrating camera-compat-triggered recalculations in `cameraActivity.recomputeConfiguration()`, there is no need to track the camera compat state: it can always be calculated and up-to-date. Flag: com.android.window.flags.enable_camera_compat_for_desktop_windowing Fixes: 347864073 Test: atest WmTests:CameraCompatFreeformPolicyTests Test: atest WmTests:AppCompatCameraOverridesTest Change-Id: I2074fdd473ad73c22bd807a6f7cc8e573b0c3bd0 --- .../server/wm/AppCompatCameraOverrides.java | 14 -------------- .../server/wm/CameraCompatFreeformPolicy.java | 6 ------ .../server/wm/CameraCompatFreeformPolicyTests.java | 6 ++---- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java index fbf9478b4fd9..5373476beacc 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; @@ -33,7 +32,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.NonNull; -import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode; import com.android.server.wm.utils.OptPropFactory; import com.android.window.flags.Flags; @@ -202,22 +200,10 @@ class AppCompatCameraOverrides { && !mActivityRecord.shouldCreateAppCompatDisplayInsets(); } - @FreeformCameraCompatMode - int getFreeformCameraCompatMode() { - return mAppCompatCameraOverridesState.mFreeformCameraCompatMode; - } - - void setFreeformCameraCompatMode(@FreeformCameraCompatMode int freeformCameraCompatMode) { - mAppCompatCameraOverridesState.mFreeformCameraCompatMode = freeformCameraCompatMode; - } - static class AppCompatCameraOverridesState { // Whether activity "refresh" was requested but not finished in // ActivityRecord#activityResumedLocked following the camera compat force rotation in // DisplayRotationCompatPolicy. private boolean mIsRefreshRequested; - - @FreeformCameraCompatMode - private int mFreeformCameraCompatMode = CAMERA_COMPAT_FREEFORM_NONE; } } diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java index 2a0252ab06fd..14d90fd542f8 100644 --- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java @@ -159,16 +159,10 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa } cameraActivity.recomputeConfiguration(); - updateCameraCompatMode(cameraActivity); cameraActivity.getTask().dispatchTaskInfoChangedIfNeeded(/* force= */ true); cameraActivity.ensureActivityConfiguration(/* ignoreVisibility= */ false); } - private void updateCameraCompatMode(@NonNull ActivityRecord cameraActivity) { - cameraActivity.mAppCompatController.getAppCompatCameraOverrides() - .setFreeformCameraCompatMode(getCameraCompatMode(cameraActivity)); - } - @Override public boolean onCameraClosed(@NonNull String cameraId) { // Top activity in the same task as the camera activity, or `null` if the task is diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java index e447565a55bd..17c5ac23c8f2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java @@ -429,13 +429,11 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { } private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) { - assertEquals(mode, mActivity.mAppCompatController.getAppCompatCameraOverrides() - .getFreeformCameraCompatMode()); + assertEquals(mode, mCameraCompatFreeformPolicy.getCameraCompatMode(mActivity)); } private void assertNotInCameraCompatMode() { - assertEquals(CAMERA_COMPAT_FREEFORM_NONE, mActivity.mAppCompatController - .getAppCompatCameraOverrides().getFreeformCameraCompatMode()); + assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_NONE); } private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception { -- GitLab From 93c8bea10960c2fb1c6a4a6cd9bbc677b2134618 Mon Sep 17 00:00:00 2001 From: Mina Granic Date: Fri, 15 Nov 2024 12:31:04 +0000 Subject: [PATCH 059/656] Set the wmtest targetSDK to 31. This is to avoid the VerifyApps Google Play Protect popups when running tests. Tests can set this value to lower if they need it. Flag: TEST_ONLY Test: ran a few tests to make sure the popup doesn't show Fixes: 379261543 Change-Id: I76410173e4229dfd1e019bdb75d6ff65f0891a3b --- services/tests/wmtests/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 6e6b70d319ab..5f2f3ed67432 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -17,10 +17,10 @@ - + + android:targetSdkVersion="31" /> -- GitLab From a8ad2ceda7c83a0717b9109b9456a591a1333744 Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Fri, 15 Nov 2024 12:32:13 +0000 Subject: [PATCH 060/656] Remove TODO(b/347269120)s as they will no longer be done Change https://r.android.com/3139200 added the TODOs as the intent was to add `@Nullable` back on the methods. However, doing so would break too many applications so it is not considered worth the effort so this change is cleaning up the unnecessary TODOs. Bug: 347269120 Test: m checkapi Change-Id: Ibeb0380f8d14ebb9a4c05f7b9a26632e38affd2f --- core/java/android/content/Context.java | 2 -- core/java/android/content/ContextWrapper.java | 1 - 2 files changed, 3 deletions(-) diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0b3331382413..7e65439bb3c7 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4499,7 +4499,6 @@ public abstract class Context { * @see #DISPLAY_HASH_SERVICE * @see android.view.displayhash.DisplayHashManager */ - // TODO(b/347269120): Re-add @Nullable public abstract Object getSystemService(@ServiceName @NonNull String name); /** @@ -4545,7 +4544,6 @@ public abstract class Context { */ @SuppressWarnings("unchecked") @RavenwoodKeep - // TODO(b/347269120): Re-add @Nullable public final T getSystemService(@NonNull Class serviceClass) { // Because subclasses may override getSystemService(String) we cannot // perform a lookup by class alone. We must first map the class to its diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 79fa6ea4d157..77aa62886399 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -953,7 +953,6 @@ public class ContextWrapper extends Context { } @Override - // TODO(b/347269120): Re-add @Nullable public Object getSystemService(String name) { return mBase.getSystemService(name); } -- GitLab From a1cd51e67f0e4985c229057b78974b1a4da8a576 Mon Sep 17 00:00:00 2001 From: Felix Stern Date: Fri, 15 Nov 2024 13:50:14 +0000 Subject: [PATCH 061/656] Fix InputMethodServiceTest#testHideImeWithWindowInsetsController The IME visibility is only sent at the end of the animation. Therefore, we have to wait until the visibility was sent to the server and the IME window hidden. Change-Id: Ied0943e1937e9173365a62c9e5ccf5d8fae2dd56 Test: InputMethodServiceTest#testHideImeWithWindowInsetsController Fix: 377004461 Flag: EXEMPT test fix --- .../inputmethodservice/InputMethodServiceTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java index 5bb6b19cd63e..d08715586580 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java @@ -194,7 +194,13 @@ public class InputMethodServiceTest { () -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(), true /* expected */, false /* inputViewStarted */); - assertThat(mInputMethodService.isInputViewShown()).isFalse(); + if (mFlagsValueProvider.getBoolean(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)) { + // The IME visibility is only sent at the end of the animation. Therefore, we have to + // wait until the visibility was sent to the server and the IME window hidden. + eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isFalse()); + } else { + assertThat(mInputMethodService.isInputViewShown()).isFalse(); + } } /** -- GitLab From f04796f3e9f3104eaf146c7b48b718051a7dbc0a Mon Sep 17 00:00:00 2001 From: Mina Granic Date: Fri, 15 Nov 2024 10:50:44 +0000 Subject: [PATCH 062/656] Remove a duplicate method in camera compat. Also do a few smaller automatic cleanups. Flag: com.android.window.flags.enable_camera_compat_for_desktop_windowing Fixes: 371176245 Test: atest WmTests:CameraCompatFreeformPolicyTests Change-Id: I9890ffdb6e8dbb87d099c95e1a3842c31dc18a8b --- .../server/wm/AppCompatCameraOverrides.java | 2 +- .../server/wm/CameraCompatFreeformPolicy.java | 25 +--------- .../wm/CameraCompatFreeformPolicyTests.java | 50 +++++++++---------- 3 files changed, 27 insertions(+), 50 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java index fbf9478b4fd9..d76e3ec9ea92 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java @@ -165,7 +165,7 @@ class AppCompatCameraOverrides { * *

The treatment is enabled when the following conditions are met: *

    - *
  • Property gating the camera compatibility free-form treatment is enabled. + *
  • Feature flag gating the camera compatibility free-form treatment is enabled. *
  • Activity isn't opted out by the device manufacturer with override. *
*/ diff --git a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java index 2a0252ab06fd..6c5e6b9cece7 100644 --- a/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java +++ b/services/core/java/com/android/server/wm/CameraCompatFreeformPolicy.java @@ -38,7 +38,6 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.CameraCompatTaskInfo; -import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.view.DisplayInfo; import android.view.Surface; @@ -46,7 +45,6 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.ProtoLog; import com.android.internal.protolog.WmProtoLogGroups; -import com.android.window.flags.Flags; /** * Policy for camera compatibility freeform treatment. @@ -124,26 +122,6 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa return appBoundsChanged || displayRotationChanged; } - /** - * Whether activity is eligible for camera compatibility free-form treatment. - * - *

The treatment is applied to a fixed-orientation camera activity in free-form windowing - * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and - * provides changes to the camera and display orientation signals to match those expected on a - * portrait device in that orientation (for example, on a standard phone). - * - *

The treatment is enabled when the following conditions are met: - *

    - *
  • Property gating the camera compatibility free-form treatment is enabled. - *
  • Activity isn't opted out by the device manufacturer with override. - *
- */ - @VisibleForTesting - boolean isCameraCompatForFreeformEnabledForActivity(@NonNull ActivityRecord activity) { - return Flags.enableCameraCompatForDesktopWindowing() && !activity.info.isChangeEnabled( - ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT); - } - @Override public void onCameraOpened(@NonNull ActivityRecord cameraActivity, @NonNull String cameraId) { @@ -277,7 +255,8 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa boolean isTreatmentEnabledForActivity(@NonNull ActivityRecord activity, boolean checkOrientation) { int orientation = activity.getRequestedConfigurationOrientation(); - return isCameraCompatForFreeformEnabledForActivity(activity) + return activity.mAppCompatController.getAppCompatCameraOverrides() + .shouldApplyFreeformTreatmentForCameraCompat() && mCameraStateMonitor.isCameraRunningForActivity(activity) && (!checkOrientation || orientation != ORIENTATION_UNDEFINED) && activity.inFreeformWindowingMode() diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java index e447565a55bd..d6a4880945e1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java @@ -96,53 +96,46 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { private static final String TEST_PACKAGE_1 = "com.android.frameworks.wmtests"; private static final String TEST_PACKAGE_2 = "com.test.package.two"; private static final String CAMERA_ID_1 = "camera-1"; - private static final String CAMERA_ID_2 = "camera-2"; - private CameraManager mMockCameraManager; - private Handler mMockHandler; private AppCompatConfiguration mAppCompatConfiguration; private CameraManager.AvailabilityCallback mCameraAvailabilityCallback; private CameraCompatFreeformPolicy mCameraCompatFreeformPolicy; private ActivityRecord mActivity; - private Task mTask; private ActivityRefresher mActivityRefresher; @Before public void setUp() throws Exception { mAppCompatConfiguration = mDisplayContent.mWmService.mAppCompatConfiguration; spyOn(mAppCompatConfiguration); - when(mAppCompatConfiguration.isCameraCompatTreatmentEnabled()) - .thenReturn(true); - when(mAppCompatConfiguration.isCameraCompatRefreshEnabled()) - .thenReturn(true); + when(mAppCompatConfiguration.isCameraCompatTreatmentEnabled()).thenReturn(true); + when(mAppCompatConfiguration.isCameraCompatRefreshEnabled()).thenReturn(true); when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled()) .thenReturn(true); - mMockCameraManager = mock(CameraManager.class); + final CameraManager mockCameraManager = mock(CameraManager.class); doAnswer(invocation -> { mCameraAvailabilityCallback = invocation.getArgument(1); return null; - }).when(mMockCameraManager).registerAvailabilityCallback( + }).when(mockCameraManager).registerAvailabilityCallback( any(Executor.class), any(CameraManager.AvailabilityCallback.class)); - when(mContext.getSystemService(CameraManager.class)).thenReturn(mMockCameraManager); + when(mContext.getSystemService(CameraManager.class)).thenReturn(mockCameraManager); mDisplayContent.setIgnoreOrientationRequest(true); - mMockHandler = mock(Handler.class); + final Handler mockHandler = mock(Handler.class); - when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer( + when(mockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer( invocation -> { ((Runnable) invocation.getArgument(0)).run(); return null; }); - mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler); - CameraStateMonitor cameraStateMonitor = - new CameraStateMonitor(mDisplayContent, mMockHandler); - mCameraCompatFreeformPolicy = - new CameraCompatFreeformPolicy(mDisplayContent, cameraStateMonitor, - mActivityRefresher); + mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mockHandler); + final CameraStateMonitor cameraStateMonitor = new CameraStateMonitor(mDisplayContent, + mockHandler); + mCameraCompatFreeformPolicy = new CameraCompatFreeformPolicy(mDisplayContent, + cameraStateMonitor, mActivityRefresher); setDisplayRotation(Surface.ROTATION_90); mCameraCompatFreeformPolicy.start(); @@ -250,10 +243,12 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + assertTrue(mActivity.info .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT)); - assertFalse(mCameraCompatFreeformPolicy.isCameraCompatForFreeformEnabledForActivity( - mActivity)); + assertFalse(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity( + mActivity, /* checkOrientation= */ true)); } @Test @@ -261,8 +256,10 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); - assertTrue(mCameraCompatFreeformPolicy.isCameraCompatForFreeformEnabledForActivity( - mActivity)); + mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); + + assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity( + mActivity, /* checkOrientation= */ true)); } @Test @@ -406,7 +403,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { private void configureActivityAndDisplay(@ScreenOrientation int activityOrientation, @Orientation int naturalOrientation, @WindowingMode int windowingMode) { - mTask = new TaskBuilder(mSupervisor) + final Task task = new TaskBuilder(mSupervisor) .setDisplay(mDisplayContent) .setWindowingMode(windowingMode) .build(); @@ -416,7 +413,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { .setComponent(ComponentName.createRelative(mContext, com.android.server.wm.CameraCompatFreeformPolicyTests.class.getName())) .setScreenOrientation(activityOrientation) - .setTask(mTask) + .setTask(task) .build(); spyOn(mActivity.mAppCompatController.getAppCompatCameraOverrides()); @@ -471,7 +468,8 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { private Configuration createConfiguration(boolean letterbox) { final Configuration configuration = new Configuration(); - Rect bounds = letterbox ? new Rect(300, 0, 700, 600) : new Rect(0, 0, 1000, 600); + Rect bounds = letterbox ? new Rect(/*left*/ 300, /*top*/ 0, /*right*/ 700, /*bottom*/ 600) + : new Rect(/*left*/ 0, /*top*/ 0, /*right*/ 1000, /*bottom*/ 600); configuration.windowConfiguration.setAppBounds(bounds); return configuration; } -- GitLab From 74ef84689ffa65988d3aa9c5713c048b285e86a8 Mon Sep 17 00:00:00 2001 From: Ebru Kurnaz Date: Fri, 15 Nov 2024 14:19:57 +0000 Subject: [PATCH 063/656] Notes QS Tile: Flag the notes tile viewmodel with QsNewTilesFuture Test: Build Bug: 357863750 Flag: com.android.systemui.notes_role_qs_tile Change-Id: I7635767ac61e4223ac4f2289680d3b22c8e343ec --- .../systemui/notetask/NoteTaskModule.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index a1c5c9c682c3..5d5465633f9f 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -41,6 +41,7 @@ import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel import com.android.systemui.res.R import dagger.Binds import dagger.Module @@ -106,12 +107,14 @@ interface NoteTaskModule { stateInteractor: NotesTileDataInteractor, userActionInteractor: NotesTileUserActionInteractor, ): QSTileViewModel = - factory.create( - TileSpec.create(NOTES_TILE_SPEC), - userActionInteractor, - stateInteractor, - mapper, - ) + if (com.android.systemui.Flags.qsNewTilesFuture()) + factory.create( + TileSpec.create(NOTES_TILE_SPEC), + userActionInteractor, + stateInteractor, + mapper, + ) + else StubQSTileViewModel @Provides @IntoMap @@ -120,10 +123,10 @@ interface NoteTaskModule { QSTileConfig( tileSpec = TileSpec.create(NOTES_TILE_SPEC), uiConfig = - QSTileUIConfig.Resource( - iconRes = R.drawable.ic_qs_notes, - labelRes = R.string.quick_settings_notes_label, - ), + QSTileUIConfig.Resource( + iconRes = R.drawable.ic_qs_notes, + labelRes = R.string.quick_settings_notes_label, + ), instanceId = uiEventLogger.getNewInstanceId(), category = TileCategory.UTILITIES, ) -- GitLab From 336c0f5cb5d3dbaf313b5f01aa8b8c189bd55c91 Mon Sep 17 00:00:00 2001 From: Mina Granic Date: Thu, 14 Nov 2024 18:07:47 +0000 Subject: [PATCH 064/656] Make a opt-out per-app override opt-in for camera compat freeform. This override is not currently used, so it should be safe to keep the same ID. Flag: com.android.window.flags.enable_camera_compat_for_desktop_windowing Fixes: 371176245 Test: atest WmTests:CameraCompatFreeformPolicyTests Test: atest WmTests:AppCompatCameraOverridesTest Change-Id: I39dfe2bbeaeb0a6375200d440a6ab2f830562b6d --- .../java/android/content/pm/ActivityInfo.java | 10 ++-- .../server/wm/AppCompatCameraOverrides.java | 8 +-- .../wm/AppCompatCameraOverridesTest.java | 24 +++------ .../wm/CameraCompatFreeformPolicyTests.java | 52 +++++++++++++------ 4 files changed, 53 insertions(+), 41 deletions(-) diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index ce52825ddb73..b10f5e4fe66c 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1320,23 +1320,23 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { 264301586L; // buganizer id /** - * Excludes the packages the override is applied to from the camera compatibility treatment - * in free-form windowing mode for fixed-orientation apps. + * Includes the packages the override is applied to in the camera compatibility treatment in + * free-form windowing mode for fixed-orientation apps. * *

In free-form windowing mode, the compatibility treatment emulates running on a portrait * device by letterboxing the app window and changing the camera characteristics to what apps * commonly expect in a portrait device: 90 and 270 degree sensor rotation for back and front * cameras, respectively, and setting display rotation to 0. * - *

Use this flag to disable the compatibility treatment for apps that do not respond well to - * the treatment. + *

Use this flag to enable the compatibility treatment for apps in which camera doesn't work + * well in freeform windowing. * * @hide */ @ChangeId @Overridable @Disabled - public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT = + public static final long OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT = 314961188L; /** diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java index d76e3ec9ea92..5c87782bb6cb 100644 --- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java @@ -18,8 +18,8 @@ package com.android.server.wm; import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION; -import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; +import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; @@ -166,12 +166,12 @@ class AppCompatCameraOverrides { *

The treatment is enabled when the following conditions are met: *

    *
  • Feature flag gating the camera compatibility free-form treatment is enabled. - *
  • Activity isn't opted out by the device manufacturer with override. + *
  • Activity is opted in by the device manufacturer with override. *
*/ boolean shouldApplyFreeformTreatmentForCameraCompat() { - return Flags.enableCameraCompatForDesktopWindowing() && !isChangeEnabled(mActivityRecord, - OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT); + return Flags.enableCameraCompatForDesktopWindowing() && isChangeEnabled(mActivityRecord, + OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT); } boolean isOverrideOrientationOnlyForCameraEnabled() { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java index b91a5b7afe26..d5ed048032e7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java @@ -17,8 +17,8 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION; -import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH; +import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA; import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA; @@ -228,9 +228,8 @@ public class AppCompatCameraOverridesTest extends WindowTestsBase { } @Test - @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT}) @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() { + public void testShouldApplyCameraCompatFreeformTreatment_notEnabledByOverride_returnsFalse() { runTestScenario((robot) -> { robot.activity().createActivityWithComponentInNewTask(); @@ -239,19 +238,9 @@ public class AppCompatCameraOverridesTest extends WindowTestsBase { } @Test - @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT}) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse() { - runTestScenario((robot) -> { - robot.activity().createActivityWithComponentInNewTask(); - - robot.checkShouldApplyFreeformTreatmentForCameraCompat(false); - }); - } - - @Test - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() { + public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() { runTestScenario((robot) -> { robot.activity().createActivityWithComponentInNewTask(); @@ -261,8 +250,9 @@ public class AppCompatCameraOverridesTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA, - OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA}) - public void testShouldRecomputeConfigurationForCameraCompat() { + OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA, + OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) + public void testShouldRecomputeConfigurationForFreeformTreatment() { runTestScenario((robot) -> { robot.conf().enableCameraCompatSplitScreenAspectRatio(true); robot.applyOnActivity((a) -> { diff --git a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java index d6a4880945e1..d4c242734a1b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/CameraCompatFreeformPolicyTests.java @@ -25,7 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE; import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP; -import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT; +import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; @@ -142,8 +142,9 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { cameraStateMonitor.startListeningToCameraState(); } - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testFullscreen_doesNotActivateCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_PORTRAIT, WINDOWING_MODE_FULLSCREEN); doReturn(false).when(mActivity).inFreeformWindowingMode(); @@ -153,23 +154,26 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { assertNotInCameraCompatMode(); } - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testOrientationUnspecified_doesNotActivateCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_UNSPECIFIED); assertNotInCameraCompatMode(); } - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testNoCameraConnection_doesNotActivateCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); assertNotInCameraCompatMode(); } - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); setDisplayRotation(Surface.ROTATION_0); @@ -180,6 +184,8 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { } @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); setDisplayRotation(Surface.ROTATION_270); @@ -190,6 +196,8 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { } @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception { configureActivity(SCREEN_ORIENTATION_LANDSCAPE); setDisplayRotation(Surface.ROTATION_0); @@ -200,6 +208,8 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { } @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception { configureActivity(SCREEN_ORIENTATION_LANDSCAPE); setDisplayRotation(Surface.ROTATION_270); @@ -209,8 +219,9 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { assertActivityRefreshRequested(/* refreshRequested */ false); } - @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); setDisplayRotation(Surface.ROTATION_270); @@ -229,6 +240,8 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { } @Test + @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testCameraOpenedForDifferentPackage_notInCameraCompatMode() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); @@ -239,31 +252,32 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT}) - public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() { + public void testShouldApplyCameraCompatFreeformTreatment_overrideNotEnabled_returnsFalse() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); - assertTrue(mActivity.info - .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT)); - assertFalse(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity( - mActivity, /* checkOrientation= */ true)); + assertFalse(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity, + /* checkOrientation */ true)); } @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) - public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue() { + @EnableCompatChanges(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT) + public void testShouldApplyCameraCompatFreeformTreatment_enabledByOverride_returnsTrue() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1); - assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity( - mActivity, /* checkOrientation= */ true)); + assertTrue(mActivity.info + .isChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)); + assertTrue(mCameraCompatFreeformPolicy.isTreatmentEnabledForActivity(mActivity, + /* checkOrientation */ true)); } @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testShouldRefreshActivity_appBoundsChanged_returnsTrue() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); Configuration oldConfiguration = createConfiguration(/* letterbox= */ false); @@ -276,6 +290,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testShouldRefreshActivity_displayRotationChanged_returnsTrue() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); Configuration oldConfiguration = createConfiguration(/* letterbox= */ true); @@ -291,6 +306,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testShouldRefreshActivity_appBoundsNorDisplayChanged_returnsFalse() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); Configuration oldConfiguration = createConfiguration(/* letterbox= */ true); @@ -306,6 +322,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testOnActivityConfigurationChanging_refreshDisabledViaFlag_noRefresh() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); @@ -321,6 +338,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testOnActivityConfigurationChanging_cycleThroughStopDisabled() throws Exception { when(mAppCompatConfiguration.isCameraCompatRefreshCycleThroughStopEnabled()) .thenReturn(false); @@ -335,6 +353,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testOnActivityConfigurationChanging_cycleThroughStopDisabledForApp() throws Exception { configureActivity(SCREEN_ORIENTATION_PORTRAIT); @@ -349,6 +368,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testGetCameraCompatAspectRatio_activityNotInCameraCompat_returnsDefaultAspRatio() { configureActivity(SCREEN_ORIENTATION_FULL_USER); @@ -362,6 +382,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testGetCameraCompatAspectRatio_activityInCameraCompat_returnsConfigAspectRatio() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); final float configAspectRatio = 1.5f; @@ -377,6 +398,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase { @Test @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING) + @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT}) public void testGetCameraCompatAspectRatio_inCameraCompatPerAppOverride_returnDefAspectRatio() { configureActivity(SCREEN_ORIENTATION_PORTRAIT); final float configAspectRatio = 1.5f; -- GitLab From 0a8e8588ef103d8925b6babf8ba40c3e47ce16da Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Fri, 15 Nov 2024 14:48:00 +0000 Subject: [PATCH 065/656] Set TalkBack focus to last selected screen on OOBE settings page. Fix: 377647325 Test: Manual. TalkBack. Flag: EXEMPT Bugfix. Change-Id: I7e61a5b3ce47d51b6a32c5752a75181590ac45dc --- .../ui/composable/TutorialSelectionScreen.kt | 31 +++++++++++++++++-- .../ui/view/TouchpadTutorialActivity.kt | 20 ++++++++++-- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt index d371acf86a28..66a900bd72d8 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt @@ -18,6 +18,7 @@ package com.android.systemui.touchpad.tutorial.ui.composable import android.content.res.Configuration import androidx.compose.foundation.background +import androidx.compose.foundation.focusable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -36,8 +37,12 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.input.pointer.pointerInteropFilter @@ -49,6 +54,7 @@ import com.android.systemui.inputdevice.tutorial.ui.composable.DoneButton import com.android.systemui.res.R import com.android.systemui.touchpad.tutorial.ui.gesture.isFourFingerTouchpadSwipe import com.android.systemui.touchpad.tutorial.ui.gesture.isThreeFingerTouchpadSwipe +import com.android.systemui.touchpad.tutorial.ui.viewmodel.Screen @Composable fun TutorialSelectionScreen( @@ -56,6 +62,7 @@ fun TutorialSelectionScreen( onHomeTutorialClicked: () -> Unit, onRecentAppsTutorialClicked: () -> Unit, onDoneButtonClicked: () -> Unit, + lastSelectedScreen: Screen, ) { Column( verticalArrangement = Arrangement.Center, @@ -80,6 +87,7 @@ fun TutorialSelectionScreen( onHomeTutorialClicked = onHomeTutorialClicked, onRecentAppsTutorialClicked = onRecentAppsTutorialClicked, modifier = Modifier.weight(1f).padding(60.dp), + lastSelectedScreen, ) } else -> { @@ -88,6 +96,7 @@ fun TutorialSelectionScreen( onHomeTutorialClicked = onHomeTutorialClicked, onRecentAppsTutorialClicked = onRecentAppsTutorialClicked, modifier = Modifier.weight(1f).padding(60.dp), + lastSelectedScreen, ) } } @@ -105,6 +114,7 @@ private fun HorizontalSelectionButtons( onHomeTutorialClicked: () -> Unit, onRecentAppsTutorialClicked: () -> Unit, modifier: Modifier = Modifier, + lastSelectedScreen: Screen, ) { Row( horizontalArrangement = Arrangement.spacedBy(20.dp), @@ -116,6 +126,7 @@ private fun HorizontalSelectionButtons( onHomeTutorialClicked, onRecentAppsTutorialClicked, modifier = Modifier.weight(1f).fillMaxSize(), + lastSelectedScreen, ) } } @@ -126,6 +137,7 @@ private fun VerticalSelectionButtons( onHomeTutorialClicked: () -> Unit, onRecentAppsTutorialClicked: () -> Unit, modifier: Modifier = Modifier, + lastSelectedScreen: Screen, ) { Column( verticalArrangement = Arrangement.spacedBy(20.dp), @@ -137,6 +149,7 @@ private fun VerticalSelectionButtons( onHomeTutorialClicked, onRecentAppsTutorialClicked, modifier = Modifier.weight(1f).fillMaxSize(), + lastSelectedScreen, ) } } @@ -147,14 +160,26 @@ private fun ThreeTutorialButtons( onHomeTutorialClicked: () -> Unit, onRecentAppsTutorialClicked: () -> Unit, modifier: Modifier = Modifier, + lastSelectedScreen: Screen, ) { + val homeFocusRequester = remember { FocusRequester() } + val backFocusRequester = remember { FocusRequester() } + val recentAppsFocusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { + when (lastSelectedScreen) { + Screen.HOME_GESTURE -> homeFocusRequester.requestFocus() + Screen.BACK_GESTURE -> backFocusRequester.requestFocus() + Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus() + else -> {} // No-Op. + } + } TutorialButton( text = stringResource(R.string.touchpad_tutorial_home_gesture_button), icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon), iconColor = MaterialTheme.colorScheme.onPrimary, onClick = onHomeTutorialClicked, backgroundColor = MaterialTheme.colorScheme.primary, - modifier = modifier, + modifier = modifier.focusRequester(homeFocusRequester).focusable(), ) TutorialButton( text = stringResource(R.string.touchpad_tutorial_back_gesture_button), @@ -162,7 +187,7 @@ private fun ThreeTutorialButtons( iconColor = MaterialTheme.colorScheme.onTertiary, onClick = onBackTutorialClicked, backgroundColor = MaterialTheme.colorScheme.tertiary, - modifier = modifier, + modifier = modifier.focusRequester(backFocusRequester).focusable(), ) TutorialButton( text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button), @@ -170,7 +195,7 @@ private fun ThreeTutorialButtons( iconColor = MaterialTheme.colorScheme.onSecondary, onClick = onRecentAppsTutorialClicked, backgroundColor = MaterialTheme.colorScheme.secondary, - modifier = modifier, + modifier = modifier.focusRequester(recentAppsFocusRequester).focusable(), ) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt index e1f7bd59005c..272fae27c7d7 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt @@ -24,6 +24,9 @@ import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.lifecycle.Lifecycle.State.STARTED import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme @@ -82,13 +85,24 @@ constructor( @Composable fun TouchpadTutorialScreen(vm: TouchpadTutorialViewModel, closeTutorial: () -> Unit) { val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) + var lastSelectedScreen by remember { mutableStateOf(TUTORIAL_SELECTION) } when (activeScreen) { TUTORIAL_SELECTION -> TutorialSelectionScreen( - onBackTutorialClicked = { vm.goTo(BACK_GESTURE) }, - onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) }, - onRecentAppsTutorialClicked = { vm.goTo(RECENT_APPS_GESTURE) }, + onBackTutorialClicked = { + lastSelectedScreen = BACK_GESTURE + vm.goTo(BACK_GESTURE) + }, + onHomeTutorialClicked = { + lastSelectedScreen = HOME_GESTURE + vm.goTo(HOME_GESTURE) + }, + onRecentAppsTutorialClicked = { + lastSelectedScreen = RECENT_APPS_GESTURE + vm.goTo(RECENT_APPS_GESTURE) + }, onDoneButtonClicked = closeTutorial, + lastSelectedScreen, ) BACK_GESTURE -> BackGestureTutorialScreen( -- GitLab From 00f1127e7006f675702605bc49320b1228cd54be Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 12 Nov 2024 16:31:59 +0000 Subject: [PATCH 066/656] Connected data layer and UI layer for custom shortcuts retrieval Test: ShortcutHelperInteractorTest Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer Bug: 373620793 Bug: 373631223 Change-Id: Ib7d26551a1d309c8192fb9aa38c9e513d1b6d79b --- .../CustomShortcutCategoriesRepositoryTest.kt | 184 +---------- .../shortcut/data/source/TestShortcuts.kt | 286 ++++++++++++++++-- .../ShortcutHelperCategoriesInteractorTest.kt | 99 +++++- .../ui/ShortcutHelperDialogStarterTest.kt | 12 +- .../keyboard/shortcut/ShortcutHelperModule.kt | 17 ++ .../repository/ShortcutCategoriesUtils.kt | 4 +- .../ShortcutHelperCategoriesInteractor.kt | 61 +++- .../qualifiers/CustomShortcutCategories.kt | 21 ++ .../qualifiers/DefaultShortcutCategories.kt | 21 ++ .../ui/viewmodel/ShortcutHelperViewModel.kt | 6 +- .../viewmodel/ShortcutHelperViewModelTest.kt | 15 +- .../shortcut/KeyboardShortcutHelperKosmos.kt | 6 +- 12 files changed, 487 insertions(+), 245 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt index cc4c7c419a11..af8341b8e0b9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt @@ -18,13 +18,9 @@ package com.android.systemui.keyboard.shortcut.data.repository import android.content.Context import android.content.Context.INPUT_SERVICE -import android.hardware.input.InputGestureData -import android.hardware.input.InputGestureData.createKeyTrigger -import android.hardware.input.KeyGestureEvent import android.hardware.input.fakeInputManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import android.view.KeyEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES @@ -32,15 +28,9 @@ import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository -import com.android.systemui.keyboard.shortcut.shared.model.Shortcut -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey -import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.kosmos.testScope import com.android.systemui.settings.FakeUserTracker @@ -83,7 +73,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { whenever( fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) ) - .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations) + .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations) helper.toggle(deviceId = 123) val categories by collectLastValue(repo.categories) @@ -100,7 +90,7 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { whenever( fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull()) ) - .thenReturn(customizableInputGesturesWithSimpleShortcutCombinations) + .thenReturn(allCustomizableInputGesturesWithSimpleShortcutCombinations) helper.toggle(deviceId = 123) val categories by collectLastValue(repo.categories) @@ -124,168 +114,4 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { assertThat(categories).isEmpty() } } - - private fun simpleInputGestureData( - keyCode: Int = KeyEvent.KEYCODE_A, - modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON, - keyGestureType: Int, - ): InputGestureData { - val builder = InputGestureData.Builder() - builder.setKeyGestureType(keyGestureType) - builder.setTrigger(createKeyTrigger(keyCode, modifiers)) - return builder.build() - } - - private fun simpleShortcutCategory( - category: ShortcutCategoryType, - subcategoryLabel: String, - shortcutLabel: String, - ): ShortcutCategory { - return ShortcutCategory( - type = category, - subCategories = - listOf( - ShortcutSubCategory( - label = subcategoryLabel, - shortcuts = listOf(simpleShortcut(shortcutLabel)), - ) - ), - ) - } - - private fun simpleShortcut(label: String) = - Shortcut( - label = label, - commands = - listOf( - ShortcutCommand( - isCustom = true, - keys = - listOf( - ShortcutKey.Text("Ctrl"), - ShortcutKey.Text("Alt"), - ShortcutKey.Text("A"), - ), - ) - ), - ) - - private val customizableInputGestureWithUnknownKeyGestureType = - // These key gesture events are currently not supported by shortcut helper customizer - listOf( - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY), - ) - - private val expectedShortcutCategoriesWithSimpleShortcutCombination = - listOf( - simpleShortcutCategory(System, "System apps", "Open assistant"), - simpleShortcutCategory(System, "System controls", "Go to home screen"), - simpleShortcutCategory(System, "System apps", "Open settings"), - simpleShortcutCategory(System, "System controls", "Lock screen"), - simpleShortcutCategory(System, "System controls", "View notifications"), - simpleShortcutCategory(System, "System apps", "Take a note"), - simpleShortcutCategory(System, "System controls", "Take screenshot"), - simpleShortcutCategory(System, "System controls", "Go back"), - simpleShortcutCategory( - MultiTasking, - "Split screen", - "Switch from split screen to full screen", - ), - simpleShortcutCategory( - MultiTasking, - "Split screen", - "Use split screen with current app on the left", - ), - simpleShortcutCategory( - MultiTasking, - "Split screen", - "Switch to app on left or above while using split screen", - ), - simpleShortcutCategory( - MultiTasking, - "Split screen", - "Use split screen with current app on the right", - ), - simpleShortcutCategory( - MultiTasking, - "Split screen", - "Switch to app on right or below while using split screen", - ), - simpleShortcutCategory(System, "System controls", "Show shortcuts"), - simpleShortcutCategory(System, "System controls", "View recent apps"), - simpleShortcutCategory(AppCategories, "Applications", "Calculator"), - simpleShortcutCategory(AppCategories, "Applications", "Calendar"), - simpleShortcutCategory(AppCategories, "Applications", "Browser"), - simpleShortcutCategory(AppCategories, "Applications", "Contacts"), - simpleShortcutCategory(AppCategories, "Applications", "Email"), - simpleShortcutCategory(AppCategories, "Applications", "Maps"), - simpleShortcutCategory(AppCategories, "Applications", "SMS"), - simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"), - ) - - private val customizableInputGesturesWithSimpleShortcutCombinations = - listOf( - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER - ), - simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING - ), - simpleInputGestureData( - keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER - ), - ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt index c9c39b3ebf66..a1e7ef4ac5a3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt @@ -16,11 +16,20 @@ package com.android.systemui.keyboard.shortcut.data.source +import android.hardware.input.InputGestureData +import android.hardware.input.InputGestureData.createKeyTrigger +import android.hardware.input.KeyGestureEvent import android.view.KeyEvent import android.view.KeyboardShortcutGroup import android.view.KeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.System +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory import com.android.systemui.keyboard.shortcut.shared.model.shortcut import com.android.systemui.res.R @@ -64,6 +73,13 @@ object TestShortcuts { } } + private val goHomeShortcutInfo = + KeyboardShortcutInfo( + /* label = */ "Go to home screen", + /* keycode = */ KeyEvent.KEYCODE_B, + /* modifiers = */ KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON, + ) + private val standardShortcutInfo1 = KeyboardShortcutInfo( /* label = */ "Standard shortcut 1", @@ -79,6 +95,16 @@ object TestShortcuts { } } + private val customGoHomeShortcut = + shortcut("Go to home screen") { + command { + key("Ctrl") + key("Alt") + key("A") + isCustom(true) + } + } + private val standardShortcutInfo2 = KeyboardShortcutInfo( /* label = */ "Standard shortcut 2", @@ -123,35 +149,38 @@ object TestShortcuts { listOf( shortcutInfoWithRepeatedLabel, shortcutInfoWithRepeatedLabelAlternate, - shortcutInfoWithRepeatedLabelSecondAlternate - ) + shortcutInfoWithRepeatedLabelSecondAlternate, + ), ) private val subCategoryWithGroupedRepeatedShortcutLabels = ShortcutSubCategory( label = groupWithRepeatedShortcutLabels.label!!.toString(), - shortcuts = listOf(shortcutWithGroupedRepeatedLabel) + shortcuts = listOf(shortcutWithGroupedRepeatedLabel), ) private val groupWithStandardShortcutInfo = KeyboardShortcutGroup("Standard group", listOf(standardShortcutInfo1)) + val groupWithGoHomeShortcutInfo = + KeyboardShortcutGroup("System controls", listOf(goHomeShortcutInfo)) + private val subCategoryWithStandardShortcut = ShortcutSubCategory( label = groupWithStandardShortcutInfo.label!!.toString(), - shortcuts = listOf(standardShortcut1) + shortcuts = listOf(standardShortcut1), ) private val groupWithOnlyUnsupportedModifierShortcut = KeyboardShortcutGroup( "Group with unsupported modifiers", - listOf(shortcutInfoWithUnsupportedModifiers) + listOf(shortcutInfoWithUnsupportedModifiers), ) private val groupWithSupportedAndUnsupportedModifierShortcut = KeyboardShortcutGroup( "Group with mix of supported and not supported modifiers", - listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers) + listOf(standardShortcutInfo3, shortcutInfoWithUnsupportedModifiers), ) private val switchToNextLanguageShortcut = @@ -174,19 +203,19 @@ object TestShortcuts { private val subCategoryForInputLanguageSwitchShortcuts = ShortcutSubCategory( "Input", - listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut) + listOf(switchToNextLanguageShortcut, switchToPreviousLanguageShortcut), ) private val subCategoryWithUnsupportedShortcutsRemoved = ShortcutSubCategory( groupWithSupportedAndUnsupportedModifierShortcut.label!!.toString(), - listOf(standardShortcut3) + listOf(standardShortcut3), ) private val standardGroup1 = KeyboardShortcutGroup( "Standard group 1", - listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3) + listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3), ) private val standardPackageName1 = "standard.app.group1" @@ -194,38 +223,63 @@ object TestShortcuts { private val standardAppGroup1 = KeyboardShortcutGroup( "Standard app group 1", - listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3) + listOf(standardShortcutInfo1, standardShortcutInfo2, standardShortcutInfo3), ) .apply { packageName = standardPackageName1 } + private val standardSystemAppSubcategoryWithCustomHomeShortcut = + ShortcutSubCategory("System controls", listOf(customGoHomeShortcut)) + private val standardSubCategory1 = ShortcutSubCategory( standardGroup1.label!!.toString(), - listOf(standardShortcut1, standardShortcut2, standardShortcut3) + listOf(standardShortcut1, standardShortcut2, standardShortcut3), ) private val standardGroup2 = KeyboardShortcutGroup( "Standard group 2", - listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1) + listOf(standardShortcutInfo3, standardShortcutInfo2, standardShortcutInfo1), ) private val standardSubCategory2 = ShortcutSubCategory( standardGroup2.label!!.toString(), - listOf(standardShortcut3, standardShortcut2, standardShortcut1) + listOf(standardShortcut3, standardShortcut2, standardShortcut1), ) private val standardGroup3 = KeyboardShortcutGroup( "Standard group 3", - listOf(standardShortcutInfo2, standardShortcutInfo1) + listOf(standardShortcutInfo2, standardShortcutInfo1), ) private val standardSubCategory3 = ShortcutSubCategory( standardGroup3.label!!.toString(), - listOf(standardShortcut2, standardShortcut1) + listOf(standardShortcut2, standardShortcut1), + ) + + private val systemSubCategoryWithGoHomeShortcuts = + ShortcutSubCategory( + label = "System controls", + shortcuts = + listOf( + shortcut("Go to home screen") { + command { + key("Ctrl") + key("Alt") + key("B") + } + command { + key("Ctrl") + key("Alt") + key("A") + isCustom(true) + } + } + ), ) + val imeGroups = listOf(standardGroup1, standardGroup2, standardGroup3) val imeCategory = ShortcutCategory( @@ -235,8 +289,8 @@ object TestShortcuts { subCategoryForInputLanguageSwitchShortcuts, standardSubCategory1, standardSubCategory2, - standardSubCategory3 - ) + standardSubCategory3, + ), ) val currentAppGroups = listOf(standardAppGroup1) @@ -245,15 +299,33 @@ object TestShortcuts { val systemGroups = listOf(standardGroup3, standardGroup2, standardGroup1) val systemCategory = ShortcutCategory( - type = ShortcutCategoryType.System, - subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1) + type = System, + subCategories = listOf(standardSubCategory3, standardSubCategory2, standardSubCategory1), + ) + + val systemCategoryWithMergedGoHomeShortcut = + ShortcutCategory( + type = System, + subCategories = listOf(systemSubCategoryWithGoHomeShortcuts), + ) + + val systemCategoryWithCustomHomeShortcut = + ShortcutCategory( + type = System, + subCategories = + listOf( + standardSubCategory3, + standardSubCategory2, + standardSubCategory1, + standardSystemAppSubcategoryWithCustomHomeShortcut, + ), ) val multitaskingGroups = listOf(standardGroup2, standardGroup1) val multitaskingCategory = ShortcutCategory( - type = ShortcutCategoryType.MultiTasking, - subCategories = listOf(standardSubCategory2, standardSubCategory1) + type = MultiTasking, + subCategories = listOf(standardSubCategory2, standardSubCategory1), ) val groupsWithDuplicateShortcutLabels = @@ -266,14 +338,14 @@ object TestShortcuts { listOf( subCategoryForInputLanguageSwitchShortcuts, subCategoryWithGroupedRepeatedShortcutLabels, - subCategoryWithStandardShortcut + subCategoryWithStandardShortcut, ) val groupsWithUnsupportedModifier = listOf( groupWithStandardShortcutInfo, groupWithOnlyUnsupportedModifierShortcut, - groupWithSupportedAndUnsupportedModifierShortcut + groupWithSupportedAndUnsupportedModifierShortcut, ) val subCategoriesWithUnsupportedModifiersRemoved = @@ -283,8 +355,174 @@ object TestShortcuts { listOf( subCategoryForInputLanguageSwitchShortcuts, subCategoryWithStandardShortcut, - subCategoryWithUnsupportedShortcutsRemoved + subCategoryWithUnsupportedShortcutsRemoved, ) val groupsWithOnlyUnsupportedModifiers = listOf(groupWithOnlyUnsupportedModifierShortcut) + + private fun simpleInputGestureData( + keyCode: Int = KeyEvent.KEYCODE_A, + modifiers: Int = KeyEvent.META_CTRL_ON or KeyEvent.META_ALT_ON, + keyGestureType: Int, + ): InputGestureData { + val builder = InputGestureData.Builder() + builder.setKeyGestureType(keyGestureType) + builder.setTrigger(createKeyTrigger(keyCode, modifiers)) + return builder.build() + } + + private fun simpleShortcutCategory( + category: ShortcutCategoryType, + subcategoryLabel: String, + shortcutLabel: String, + ): ShortcutCategory { + return ShortcutCategory( + type = category, + subCategories = + listOf( + ShortcutSubCategory( + label = subcategoryLabel, + shortcuts = listOf(simpleShortcut(shortcutLabel)), + ) + ), + ) + } + + private fun simpleShortcut(label: String) = + Shortcut( + label = label, + commands = + listOf( + ShortcutCommand( + isCustom = true, + keys = + listOf( + ShortcutKey.Text("Ctrl"), + ShortcutKey.Text("Alt"), + ShortcutKey.Text("A"), + ), + ) + ), + ) + + val customizableInputGestureWithUnknownKeyGestureType = + // These key gesture events are currently not supported by shortcut helper customizer + listOf( + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MEDIA_KEY), + ) + + val expectedShortcutCategoriesWithSimpleShortcutCombination = + listOf( + simpleShortcutCategory(System, "System apps", "Open assistant"), + simpleShortcutCategory(System, "System controls", "Go to home screen"), + simpleShortcutCategory(System, "System apps", "Open settings"), + simpleShortcutCategory(System, "System controls", "Lock screen"), + simpleShortcutCategory(System, "System controls", "View notifications"), + simpleShortcutCategory(System, "System apps", "Take a note"), + simpleShortcutCategory(System, "System controls", "Take screenshot"), + simpleShortcutCategory(System, "System controls", "Go back"), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch from split screen to full screen", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Use split screen with current app on the left", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch to app on left or above while using split screen", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Use split screen with current app on the right", + ), + simpleShortcutCategory( + MultiTasking, + "Split screen", + "Switch to app on right or below while using split screen", + ), + simpleShortcutCategory(System, "System controls", "Show shortcuts"), + simpleShortcutCategory(System, "System controls", "View recent apps"), + simpleShortcutCategory(AppCategories, "Applications", "Calculator"), + simpleShortcutCategory(AppCategories, "Applications", "Calendar"), + simpleShortcutCategory(AppCategories, "Applications", "Browser"), + simpleShortcutCategory(AppCategories, "Applications", "Contacts"), + simpleShortcutCategory(AppCategories, "Applications", "Email"), + simpleShortcutCategory(AppCategories, "Applications", "Maps"), + simpleShortcutCategory(AppCategories, "Applications", "SMS"), + simpleShortcutCategory(MultiTasking, "Recent apps", "Cycle forward through recent apps"), + ) + val customInputGestureTypeHome = + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME) + + val allCustomizableInputGesturesWithSimpleShortcutCombinations = + listOf( + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER + ), + simpleInputGestureData(keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING + ), + simpleInputGestureData( + keyGestureType = KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER + ), + ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt index 57c8b444b922..f7c77017e239 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractorTest.kt @@ -16,12 +16,24 @@ package com.android.systemui.keyboard.shortcut.domain.interactor +import android.content.Context +import android.content.Context.INPUT_SERVICE +import android.hardware.input.InputGestureData +import android.hardware.input.fakeInputManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyboard.shortcut.data.source.FakeKeyboardShortcutGroupsSource import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customInputGestureTypeHome +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.groupWithGoHomeShortcutInfo +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithCustomHomeShortcut +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.systemCategoryWithMergedGoHomeShortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.InputMethodEditor import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking @@ -34,6 +46,8 @@ import com.android.systemui.keyboard.shortcut.shortcutHelperSystemShortcutsSourc import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.userTracker import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,13 +56,18 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { + private val mockUserContext: Context = mock() private val systemShortcutsSource = FakeKeyboardShortcutGroupsSource() private val multitaskingShortcutsSource = FakeKeyboardShortcutGroupsSource() + @OptIn(ExperimentalCoroutinesApi::class) private val kosmos = testKosmos().also { @@ -57,17 +76,23 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { it.shortcutHelperMultiTaskingShortcutsSource = multitaskingShortcutsSource it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource() it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource() + it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext }) } + private val fakeInputManager = kosmos.fakeInputManager private val testScope = kosmos.testScope - private val interactor = kosmos.shortcutHelperCategoriesInteractor + private lateinit var interactor: ShortcutHelperCategoriesInteractor private val helper = kosmos.shortcutHelperTestHelper + private val inter by lazy { kosmos.shortcutHelperCategoriesInteractor } @Before fun setShortcuts() { + interactor = kosmos.shortcutHelperCategoriesInteractor helper.setImeShortcuts(TestShortcuts.imeGroups) systemShortcutsSource.setGroups(TestShortcuts.systemGroups) multitaskingShortcutsSource.setGroups(TestShortcuts.multitaskingGroups) + whenever(mockUserContext.getSystemService(INPUT_SERVICE)) + .thenReturn(fakeInputManager.inputManager) } @Test @@ -120,7 +145,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { ShortcutCategory( type = InputMethodEditor, subCategories = - TestShortcuts.imeSubCategoriesWithGroupedDuplicatedShortcutLabels + TestShortcuts.imeSubCategoriesWithGroupedDuplicatedShortcutLabels, ), ) .inOrder() @@ -140,7 +165,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { ShortcutCategory( type = System, subCategories = - TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels + TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels, ), TestShortcuts.multitaskingCategory, TestShortcuts.imeCategory, @@ -163,7 +188,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { ShortcutCategory( type = MultiTasking, subCategories = - TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels + TestShortcuts.subCategoriesWithGroupedDuplicatedShortcutLabels, ), TestShortcuts.imeCategory, ) @@ -185,7 +210,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { ShortcutCategory( type = InputMethodEditor, subCategories = - TestShortcuts.imeSubCategoriesWithUnsupportedModifiersRemoved + TestShortcuts.imeSubCategoriesWithUnsupportedModifiersRemoved, ), ) .inOrder() @@ -203,7 +228,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { .containsExactly( ShortcutCategory( type = System, - subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved + subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved, ), TestShortcuts.multitaskingCategory, TestShortcuts.imeCategory, @@ -224,7 +249,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { TestShortcuts.systemCategory, ShortcutCategory( type = MultiTasking, - subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved + subCategories = TestShortcuts.subCategoriesWithUnsupportedModifiersRemoved, ), TestShortcuts.imeCategory, ) @@ -240,10 +265,7 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { helper.showFromActivity() assertThat(categories) - .containsExactly( - TestShortcuts.multitaskingCategory, - TestShortcuts.imeCategory, - ) + .containsExactly(TestShortcuts.multitaskingCategory, TestShortcuts.imeCategory) .inOrder() } @@ -255,11 +277,64 @@ class ShortcutHelperCategoriesInteractorTest : SysuiTestCase() { helper.showFromActivity() + assertThat(categories) + .containsExactly(TestShortcuts.systemCategory, TestShortcuts.imeCategory) + .inOrder() + } + + @Test + @DisableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER) + fun categories_excludesCustomShortcutsWhenFlagIsOff() { + testScope.runTest { + setCustomInputGestures(allCustomizableInputGesturesWithSimpleShortcutCombinations) + helper.showFromActivity() + val categories by collectLastValue(interactor.shortcutCategories) assertThat(categories) .containsExactly( TestShortcuts.systemCategory, + TestShortcuts.multitaskingCategory, + TestShortcuts.imeCategory, + ) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER) + fun categories_includesCustomShortcutsWhenFlagIsOn() { + testScope.runTest { + setCustomInputGestures(listOf(customInputGestureTypeHome)) + helper.showFromActivity() + val categories by collectLastValue(interactor.shortcutCategories) + assertThat(categories) + .containsExactly( + systemCategoryWithCustomHomeShortcut, + TestShortcuts.multitaskingCategory, + TestShortcuts.imeCategory, + ) + } + } + + @Test + @EnableFlags(Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_SHORTCUT_CUSTOMIZER) + fun categories_correctlyMergesDefaultAndCustomShortcutsOfSameType() { + testScope.runTest { + setCustomInputGestures(listOf(customInputGestureTypeHome)) + systemShortcutsSource.setGroups(groupWithGoHomeShortcutInfo) + helper.showFromActivity() + + val categories by collectLastValue(interactor.shortcutCategories) + + assertThat(categories) + .containsExactly( + systemCategoryWithMergedGoHomeShortcut, + TestShortcuts.multitaskingCategory, TestShortcuts.imeCategory, ) - .inOrder() } + } + + private fun setCustomInputGestures(customInputGestures: List) { + whenever(fakeInputManager.inputManager.getCustomInputGestures(/* filter= */ anyOrNull())) + .thenReturn(customInputGestures) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt index 1580ea5c1272..000024f9b814 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarterTest.kt @@ -16,6 +16,9 @@ package com.android.systemui.keyboard.shortcut.ui +import android.content.Context +import android.content.Context.INPUT_SERVICE +import android.hardware.input.fakeInputManager import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -36,6 +39,8 @@ import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.settings.FakeUserTracker +import com.android.systemui.settings.userTracker import com.android.systemui.statusbar.phone.systemUIDialogFactory import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -44,6 +49,8 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -52,7 +59,7 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() { private val fakeSystemSource = FakeKeyboardShortcutGroupsSource() private val fakeMultiTaskingSource = FakeKeyboardShortcutGroupsSource() - + private val mockUserContext: Context = mock() private val kosmos = Kosmos().also { it.testCase = this @@ -62,8 +69,10 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() { it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource() it.shortcutHelperInputShortcutsSource = FakeKeyboardShortcutGroupsSource() it.shortcutHelperCurrentAppShortcutsSource = FakeKeyboardShortcutGroupsSource() + it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext }) } + private val inputManager = kosmos.fakeInputManager.inputManager private val testScope = kosmos.testScope private val testHelper = kosmos.shortcutHelperTestHelper private val dialogFactory = kosmos.systemUIDialogFactory @@ -85,6 +94,7 @@ class ShortcutHelperDialogStarterTest : SysuiTestCase() { fun setUp() { fakeSystemSource.setGroups(TestShortcuts.systemGroups) fakeMultiTaskingSource.setGroups(TestShortcuts.multitaskingGroups) + whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager) } @Test diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt index 7b3380a6a608..1af7340ad7b4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt @@ -18,6 +18,9 @@ package com.android.systemui.keyboard.shortcut import com.android.systemui.CoreStartable import com.android.systemui.Flags.keyboardShortcutHelperRewrite +import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource import com.android.systemui.keyboard.shortcut.data.source.CurrentAppShortcutsSource @@ -27,6 +30,8 @@ import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsS import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource import com.android.systemui.keyboard.shortcut.qualifiers.AppCategoriesShortcuts import com.android.systemui.keyboard.shortcut.qualifiers.CurrentAppShortcuts +import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories +import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories import com.android.systemui.keyboard.shortcut.qualifiers.InputShortcuts import com.android.systemui.keyboard.shortcut.qualifiers.MultitaskingShortcuts import com.android.systemui.keyboard.shortcut.qualifiers.SystemShortcuts @@ -63,6 +68,18 @@ interface ShortcutHelperModule { impl: AppCategoriesShortcutsSource ): KeyboardShortcutGroupsSource + @Binds + @DefaultShortcutCategories + fun defaultShortcutCategoriesRepository( + impl: DefaultShortcutCategoriesRepository + ): ShortcutCategoriesRepository + + @Binds + @CustomShortcutCategories + fun customShortcutCategoriesRepository( + impl: CustomShortcutCategoriesRepository + ): ShortcutCategoriesRepository + companion object { @Provides @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt index 899fd15d6115..dd0db88c06d1 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt @@ -35,9 +35,9 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory +import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.withContext class ShortcutCategoriesUtils @Inject @@ -92,7 +92,7 @@ constructor( } .filter { it.shortcuts.isNotEmpty() } return if (subCategories.isEmpty()) { - Log.wtf(TAG, "Empty sub categories after converting $shortcutGroups") + Log.w(TAG, "Empty sub categories after converting $shortcutGroups") null } else { ShortcutCategory(type, subCategories) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt index 39fc27d35082..06aadb84f079 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt @@ -16,37 +16,66 @@ package com.android.systemui.keyboard.shortcut.domain.interactor +import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository +import com.android.systemui.keyboard.shortcut.qualifiers.CustomShortcutCategories +import com.android.systemui.keyboard.shortcut.qualifiers.DefaultShortcutCategories import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory -import javax.inject.Inject +import dagger.Lazy import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flowOf +import javax.inject.Inject @SysUISingleton class ShortcutHelperCategoriesInteractor @Inject -constructor(categoriesRepository: DefaultShortcutCategoriesRepository) { - +constructor( + @DefaultShortcutCategories defaultCategoriesRepository: ShortcutCategoriesRepository, + @CustomShortcutCategories customCategoriesRepositoryLazy: Lazy, +) { val shortcutCategories: Flow> = - categoriesRepository.categories.map { categories -> - categories.map { category -> groupSubCategoriesInCategory(category) } + defaultCategoriesRepository.categories.combine( + if (keyboardShortcutHelperShortcutCustomizer()) { + customCategoriesRepositoryLazy.get().categories + } else { + flowOf(emptyList()) + } + ) { defaultShortcutCategories, customShortcutCategories -> + groupCategories(defaultShortcutCategories + customShortcutCategories) } - private fun groupSubCategoriesInCategory(shortcutCategory: ShortcutCategory): ShortcutCategory { - val subCategoriesWithGroupedShortcuts = - shortcutCategory.subCategories.map { + private fun groupCategories( + shortcutCategories: List + ): List { + return shortcutCategories + .groupBy { it.type } + .entries + .map { (categoryType, groupedCategories) -> + ShortcutCategory( + type = categoryType, + subCategories = + groupSubCategories(groupedCategories.flatMap { it.subCategories }), + ) + } + } + + private fun groupSubCategories( + subCategories: List + ): List { + return subCategories + .groupBy { it.label } + .entries + .map { (label, groupedSubcategories) -> ShortcutSubCategory( - label = it.label, - shortcuts = groupShortcutsInSubcategory(it.shortcuts), + label = label, + shortcuts = + groupShortcutsInSubcategory(groupedSubcategories.flatMap { it.shortcuts }), ) } - return ShortcutCategory( - type = shortcutCategory.type, - subCategories = subCategoriesWithGroupedShortcuts, - ) } private fun groupShortcutsInSubcategory(shortcuts: List) = diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt new file mode 100644 index 000000000000..8acb9eebbd4d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/CustomShortcutCategories.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.qualifiers + +import javax.inject.Qualifier + +@Qualifier annotation class CustomShortcutCategories diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt new file mode 100644 index 000000000000..f94e10d17964 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/qualifiers/DefaultShortcutCategories.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.qualifiers + +import javax.inject.Qualifier + +@Qualifier annotation class DefaultShortcutCategories diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt index 912bfe99cea8..fa3edaf592c2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt @@ -40,7 +40,6 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.res.R import com.android.systemui.settings.UserTracker -import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -51,6 +50,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext +import javax.inject.Inject class ShortcutHelperViewModel @Inject @@ -123,7 +123,7 @@ constructor( userContext.packageManager.getApplicationIcon(type.packageName) IconSource(painter = DrawablePainter(drawable = iconDrawable)) } catch (e: NameNotFoundException) { - Log.wtf( + Log.w( "ShortcutHelperViewModel", "Package not found when retrieving icon for ${type.packageName}", ) @@ -153,7 +153,7 @@ constructor( packageManagerForUser.getApplicationInfo(type.packageName, /* flags= */ 0) return packageManagerForUser.getApplicationLabel(currentAppInfo).toString() } catch (e: NameNotFoundException) { - Log.wtf( + Log.w( "ShortcutHelperViewModel", "Package Not found when retrieving Label for ${type.packageName}", ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt index 7383faf2fd78..14cb9a8ce662 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt @@ -278,7 +278,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { testScope.runTest { fakeSystemSource.setGroups( groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"), - groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2"), + groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2", groupLabel = SECOND_SIMPLE_GROUP_LABEL), ) fakeMultiTaskingSource.setGroups( groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1") @@ -298,7 +298,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { ShortcutCategory( System, subCategoryWithShortcutLabels("first Foo shortcut1"), - subCategoryWithShortcutLabels("second foO shortcut2"), + subCategoryWithShortcutLabels("second foO shortcut2", subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL), ), ), ShortcutCategoryUi( @@ -380,16 +380,16 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java) } - private fun groupWithShortcutLabels(vararg shortcutLabels: String) = - KeyboardShortcutGroup(SIMPLE_GROUP_LABEL, shortcutLabels.map { simpleShortcutInfo(it) }) + private fun groupWithShortcutLabels(vararg shortcutLabels: String, groupLabel: String = FIRST_SIMPLE_GROUP_LABEL) = + KeyboardShortcutGroup(groupLabel, shortcutLabels.map { simpleShortcutInfo(it) }) .apply { packageName = "test.package.name" } private fun simpleShortcutInfo(label: String) = KeyboardShortcutInfo(label, KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON) - private fun subCategoryWithShortcutLabels(vararg shortcutLabels: String) = + private fun subCategoryWithShortcutLabels(vararg shortcutLabels: String, subCategoryLabel: String = FIRST_SIMPLE_GROUP_LABEL) = ShortcutSubCategory( - label = SIMPLE_GROUP_LABEL, + label = subCategoryLabel, shortcuts = shortcutLabels.map { simpleShortcut(it) }, ) @@ -402,6 +402,7 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { } companion object { - private const val SIMPLE_GROUP_LABEL = "simple group" + private const val FIRST_SIMPLE_GROUP_LABEL = "simple group 1" + private const val SECOND_SIMPLE_GROUP_LABEL = "simple group 2" } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 903bc8ebf42d..7c5f901eeff9 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -136,7 +136,11 @@ val Kosmos.shortcutHelperStateInteractor by } val Kosmos.shortcutHelperCategoriesInteractor by - Kosmos.Fixture { ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) } + Kosmos.Fixture { + ShortcutHelperCategoriesInteractor( + defaultShortcutCategoriesRepository, + ) { customShortcutCategoriesRepository } + } val Kosmos.shortcutHelperViewModel by Kosmos.Fixture { -- GitLab From 2671b65f47730758c696b58276a08642a19c3c3a Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 13 Nov 2024 16:26:25 +0000 Subject: [PATCH 067/656] minor refactoring Test: NONE trivial refactor Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer Bug: 373631223 Change-Id: Iab06af82057cd12abd653a8ac1b82f05b7f7d82d --- .../data/repository/CustomShortcutCategoriesRepository.kt | 4 ++-- .../keyboard/shortcut/data/repository/InputGestures.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt index ec1d358b6bd2..afe87435e159 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt @@ -148,7 +148,7 @@ constructor( private fun fetchGroupLabelByGestureType( @KeyGestureEvent.KeyGestureType keyGestureType: Int ): String? { - InputGestures.gestureToInternalKeyboardShortcutGroupLabelMap[keyGestureType]?.let { + InputGestures.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let { return context.getString(it) } ?: return null } @@ -156,7 +156,7 @@ constructor( private fun fetchShortcutInfoLabelByGestureType( @KeyGestureEvent.KeyGestureType keyGestureType: Int ): String? { - InputGestures.gestureToInternalKeyboardShortcutInfoLabelMap[keyGestureType]?.let { + InputGestures.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let { return context.getString(it) } ?: return null } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt index 90be9888e622..7bb294df98cd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestures.kt @@ -81,7 +81,7 @@ object InputGestures { KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING to AppCategories, ) - val gestureToInternalKeyboardShortcutGroupLabelMap = + val gestureToInternalKeyboardShortcutGroupLabelResIdMap = mapOf( // System Category KEY_GESTURE_TYPE_HOME to R.string.shortcut_helper_category_system_controls, @@ -129,7 +129,7 @@ object InputGestures { R.string.keyboard_shortcut_group_applications, ) - val gestureToInternalKeyboardShortcutInfoLabelMap = + val gestureToInternalKeyboardShortcutInfoLabelResIdMap = mapOf( // System Category KEY_GESTURE_TYPE_HOME to R.string.group_system_access_home_screen, -- GitLab From c0dd0da46ab4df9545eaf853d04eb0abc998e0a5 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Fri, 15 Nov 2024 16:33:32 +0100 Subject: [PATCH 068/656] Use a switch statement instead of static list of layouts The static list is not very scalable when we need to add things behind flags, so let's just use a switch statement. Bug: 378660052 Test: presubmint Flag: android.app.notifications_redesign_templates Change-Id: I560c6f7c09cd82860dd17a9af2e6cfc88455cd05 --- core/java/android/app/Notification.java | 30 +++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cfe0ff9440e7..940039714cb9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -811,20 +811,32 @@ public class Notification implements Parcelable } private static boolean isStandardLayout(int layoutId) { - // TODO: b/359128724 - Add to static list when inlining the flag. + if (Flags.notificationsRedesignTemplates()) { + return switch (layoutId) { + case R.layout.notification_2025_template_collapsed_base, + R.layout.notification_2025_template_header, + R.layout.notification_template_material_heads_up_base, + R.layout.notification_template_material_big_base, + R.layout.notification_template_material_big_picture, + R.layout.notification_template_material_big_text, + R.layout.notification_template_material_inbox, + R.layout.notification_template_material_messaging, + R.layout.notification_template_material_big_messaging, + R.layout.notification_template_material_conversation, + R.layout.notification_template_material_media, + R.layout.notification_template_material_big_media, + R.layout.notification_template_material_call, + R.layout.notification_template_material_big_call, + R.layout.notification_template_header -> true; + case R.layout.notification_template_material_progress -> Flags.apiRichOngoing(); + default -> false; + }; + } if (Flags.apiRichOngoing()) { if (layoutId == R.layout.notification_template_material_progress) { return true; } } - // TODO: b/378660052 - Add to static list when inlining the flag. - if (Flags.notificationsRedesignTemplates()) { - switch(layoutId) { - case R.layout.notification_2025_template_collapsed_base: - case R.layout.notification_2025_template_header: - return true; - } - } return STANDARD_LAYOUTS.contains(layoutId); } -- GitLab From 59fa0b7739cd95d7e2ee7a646478477226e8c72e Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 14 Nov 2024 14:36:11 +0100 Subject: [PATCH 069/656] [Notif redesign] Update spacing of collapsed group children This is to match the redesign specs, including aligning to the new, larger "small" icon. Note that notification_2025_hybrid and notification_2025_hybrid_conversation are forks of hybrid_notification and hybrid_conversation_notification respectively. Bug: 378660052 Test: visual test, screenshot tests to come later Flag: android.app.notifications_redesign_templates Change-Id: I18ded714d191f7dcc082806f0b4984c29e66b9de --- .../res/layout/notification_2025_hybrid.xml | 42 +++++++++++ .../notification_2025_hybrid_conversation.xml | 74 +++++++++++++++++++ packages/SystemUI/res/values/dimens.xml | 12 +++ .../HybridConversationNotificationView.java | 23 ++++-- .../notification/row/HybridGroupManager.java | 5 +- .../row/HybridNotificationView.java | 20 +++++ .../row/SingleLineViewInflater.kt | 5 +- 7 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 packages/SystemUI/res/layout/notification_2025_hybrid.xml create mode 100644 packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid.xml b/packages/SystemUI/res/layout/notification_2025_hybrid.xml new file mode 100644 index 000000000000..8c34cd4165e0 --- /dev/null +++ b/packages/SystemUI/res/layout/notification_2025_hybrid.xml @@ -0,0 +1,42 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml new file mode 100644 index 000000000000..ff5d9d3567f6 --- /dev/null +++ b/packages/SystemUI/res/layout/notification_2025_hybrid_conversation.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7af005721e13..61768330e648 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -788,6 +788,18 @@ 24dp + + 16dp + + + 11dp + + + 16dp + 1dp diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java index 0738a03f8237..1ff0d9262476 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Flags; import android.content.Context; import android.content.res.ColorStateList; import android.graphics.drawable.Icon; @@ -93,12 +94,22 @@ public class HybridConversationNotificationView extends HybridNotificationView { } mConversationSenderName = requireViewById(R.id.conversation_notification_sender); applyTextColor(mConversationSenderName, mSecondaryTextColor); - mFacePileSize = getResources() - .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size); - mFacePileAvatarSize = getResources() - .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_avatar_size); - mSingleAvatarSize = getResources() - .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size); + if (Flags.notificationsRedesignTemplates()) { + mFacePileSize = getResources() + .getDimensionPixelSize(R.dimen.notification_2025_single_line_face_pile_size); + mFacePileAvatarSize = getResources() + .getDimensionPixelSize( + R.dimen.notification_2025_single_line_face_pile_avatar_size); + mSingleAvatarSize = getResources() + .getDimensionPixelSize(R.dimen.notification_2025_single_line_avatar_size); + } else { + mFacePileSize = getResources() + .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size); + mFacePileAvatarSize = getResources() + .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_avatar_size); + mSingleAvatarSize = getResources() + .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size); + } mFacePileProtectionWidth = getResources().getDimensionPixelSize( R.dimen.conversation_single_line_face_pile_protection_width); mTransformationHelper.setCustomTransformation( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index 09c034978977..e5e559f1a5da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -63,9 +63,8 @@ public class HybridGroupManager { private HybridNotificationView inflateHybridView(View contentView, ViewGroup parent) { Trace.beginSection("HybridGroupManager#inflateHybridView"); LayoutInflater inflater = LayoutInflater.from(mContext); - int layout = contentView instanceof ConversationLayout - ? R.layout.hybrid_conversation_notification - : R.layout.hybrid_notification; + int layout = HybridNotificationView.getLayoutResource( + /* isConversation = */ contentView instanceof ConversationLayout); HybridNotificationView hybrid = (HybridNotificationView) inflater.inflate(layout, parent, false); parent.addView(hybrid); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java index da8c4dc08bf0..61f4e96bf99a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridNotificationView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.row; import static android.app.Notification.COLOR_INVALID; import android.annotation.Nullable; +import android.app.Flags; import android.content.Context; import android.content.res.TypedArray; import android.text.TextUtils; @@ -73,6 +74,25 @@ public class HybridNotificationView extends AlphaOptimizedLinearLayout return mTextView; } + /** + * Get layout resource for this view based on {@param isConversation}. + */ + public static int getLayoutResource(boolean isConversation) { + if (Flags.notificationsRedesignTemplates()) { + if (isConversation) { + return R.layout.notification_2025_hybrid_conversation; + } else { + return R.layout.notification_2025_hybrid; + } + } else { + if (isConversation) { + return R.layout.hybrid_conversation_notification; + } else { + return R.layout.hybrid_notification; + } + } + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt index 4e26ae85100c..e702f10d7f50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt @@ -412,10 +412,7 @@ internal object SingleLineViewInflater { traceSection("SingleLineViewInflater#inflateSingleLineView") { val inflater = LayoutInflater.from(context) - val layoutRes: Int = - if (isConversation) - com.android.systemui.res.R.layout.hybrid_conversation_notification - else com.android.systemui.res.R.layout.hybrid_notification + val layoutRes: Int = HybridNotificationView.getLayoutResource(isConversation) view = inflater.inflate(layoutRes, /* root= */ null) as HybridNotificationView if (view == null) { Log.wtf(TAG, "Single-line view inflation result is null for entry: ${entry.logKey}") -- GitLab From d3cb2296f0d6c3fbbce5bb9b971bd10c277e4444 Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Fri, 15 Nov 2024 16:07:22 +0000 Subject: [PATCH 070/656] Set title on oobe tutorial for more context for TalkBack. Fix: 377637671 Test: Manual. TalkBack. Flag: EXEMPT Bugfix. Change-Id: I702952a3ebe511345fb4178a698dbcefb65c7868 --- .../touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt index e1f7bd59005c..cd5b9afc2cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt @@ -30,6 +30,7 @@ import com.android.compose.theme.PlatformTheme import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger.TutorialContext import com.android.systemui.inputdevice.tutorial.KeyboardTouchpadTutorialMetricsLogger +import com.android.systemui.res.R import com.android.systemui.touchpad.tutorial.ui.composable.BackGestureTutorialScreen import com.android.systemui.touchpad.tutorial.ui.composable.HomeGestureTutorialScreen import com.android.systemui.touchpad.tutorial.ui.composable.RecentAppsGestureTutorialScreen @@ -54,6 +55,7 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + setTitle(getString(R.string.launch_touchpad_tutorial_notification_content)) setContent { PlatformTheme { TouchpadTutorialScreen(vm, closeTutorial = ::finishTutorial) } } -- GitLab From 1dfdc0a5e5f2eb3ff737b58a6d2ae92975ac891d Mon Sep 17 00:00:00 2001 From: youngtaecha Date: Wed, 23 Oct 2024 15:36:30 +0000 Subject: [PATCH 071/656] Apply telephony_config_json_parser for satellite accesss control Bug: 349604674 Flag: com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn Test: atest SatelliteAccessConfigurationParserTest(http://ab/I68600010335089848-passed) Change-Id: I0752b58fc5973ea59e980a41dc71730a1b6c6bde --- core/res/res/values/config_telephony.xml | 5 ++ .../telephony/satellite/EarfcnRange.java | 22 +++++- .../SatelliteAccessConfiguration.java | 52 +++++++++---- .../telephony/satellite/SatelliteInfo.java | 73 +++++++++++++------ .../satellite/SatellitePosition.java | 22 ++++++ 5 files changed, 134 insertions(+), 40 deletions(-) diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 31e9913dd988..fc63e81a7f25 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -460,6 +460,11 @@ 10 + + + + true diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.java b/telephony/java/android/telephony/satellite/EarfcnRange.java index 38043b570c2f..207b25d60d90 100644 --- a/telephony/java/android/telephony/satellite/EarfcnRange.java +++ b/telephony/java/android/telephony/satellite/EarfcnRange.java @@ -24,6 +24,8 @@ import android.os.Parcelable; import com.android.internal.telephony.flags.Flags; +import java.util.Objects; + /** * EARFCN (E-UTRA Absolute Radio Frequency Channel Number): A number that identifies a * specific frequency channel in LTE/5G NR, used to define the carrier frequency. @@ -73,10 +75,10 @@ public final class EarfcnRange implements Parcelable { * @param startEarfcn The starting earfcn value. * @param endEarfcn The ending earfcn value. */ - public EarfcnRange(@IntRange(from = 0, to = 65535) int endEarfcn, - @IntRange(from = 0, to = 65535) int startEarfcn) { - mEndEarfcn = endEarfcn; + public EarfcnRange(@IntRange(from = 0, to = 65535) int startEarfcn, + @IntRange(from = 0, to = 65535) int endEarfcn) { mStartEarfcn = startEarfcn; + mEndEarfcn = endEarfcn; } @Override @@ -85,6 +87,7 @@ public final class EarfcnRange implements Parcelable { } @Override + @NonNull public String toString() { return "startEarfcn: " + mStartEarfcn + ", " + "endEarfcn: " + mEndEarfcn; } @@ -121,4 +124,17 @@ public final class EarfcnRange implements Parcelable { public @IntRange(from = 0, to = 65535) int getEndEarfcn() { return mEndEarfcn; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof EarfcnRange that)) return false; + + return (that.mStartEarfcn == mStartEarfcn) && (that.mEndEarfcn == mEndEarfcn); + } + + @Override + public int hashCode() { + return Objects.hash(mStartEarfcn, mEndEarfcn); + } } diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java index c3ae70b48854..c1a6ae850985 100644 --- a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java +++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java @@ -24,7 +24,9 @@ import androidx.annotation.NonNull; import com.android.internal.telephony.flags.Flags; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * SatelliteAccessConfiguration is used to store satellite access configuration @@ -44,25 +46,25 @@ public final class SatelliteAccessConfiguration implements Parcelable { * The list of tag IDs associated with the current location */ @NonNull - private int[] mTagIds; + private List mTagIdList; /** * Constructor for {@link SatelliteAccessConfiguration}. * * @param satelliteInfos The list of {@link SatelliteInfo} objects representing the satellites * accessible with this configuration. - * @param tagIds The list of tag IDs associated with this configuration. + * @param tagIdList The list of tag IDs associated with this configuration. */ public SatelliteAccessConfiguration(@NonNull List satelliteInfos, - @NonNull int[] tagIds) { + @NonNull List tagIdList) { mSatelliteInfoList = satelliteInfos; - mTagIds = tagIds; + mTagIdList = tagIdList; } public SatelliteAccessConfiguration(Parcel in) { mSatelliteInfoList = in.createTypedArrayList(SatelliteInfo.CREATOR); - mTagIds = new int[in.readInt()]; - in.readIntArray(mTagIds); + mTagIdList = new ArrayList<>(); + in.readList(mTagIdList, Integer.class.getClassLoader(), Integer.class); } public static final Creator CREATOR = @@ -91,12 +93,7 @@ public final class SatelliteAccessConfiguration implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeTypedList(mSatelliteInfoList); - if (mTagIds != null && mTagIds.length > 0) { - dest.writeInt(mTagIds.length); - dest.writeIntArray(mTagIds); - } else { - dest.writeInt(0); - } + dest.writeList(mTagIdList); } /** @@ -116,7 +113,34 @@ public final class SatelliteAccessConfiguration implements Parcelable { * @return The list of tag IDs. */ @NonNull - public int[] getTagIds() { - return mTagIds; + public List getTagIds() { + return mTagIdList; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SatelliteAccessConfiguration that)) return false; + + return mSatelliteInfoList.equals(that.mSatelliteInfoList) + && Objects.equals(mTagIdList, that.mTagIdList); + } + + @Override + public int hashCode() { + int result = Objects.hash(mSatelliteInfoList); + result = 31 * result + Objects.hashCode(mTagIdList); + return result; + } + + @Override + @NonNull + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SatelliteAccessConfiguration{"); + sb.append("mSatelliteInfoList=").append(mSatelliteInfoList); + sb.append(", mTagIds=").append(mTagIdList); + sb.append('}'); + return sb.toString(); } } diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java index bca907e49993..7ff231812c8a 100644 --- a/telephony/java/android/telephony/satellite/SatelliteInfo.java +++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java @@ -17,6 +17,7 @@ package android.telephony.satellite; import android.annotation.FlaggedApi; +import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelUuid; import android.os.Parcelable; @@ -25,7 +26,9 @@ import androidx.annotation.NonNull; import com.android.internal.telephony.flags.Flags; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.UUID; /** @@ -47,17 +50,18 @@ public class SatelliteInfo implements Parcelable { * Position information of a satellite. * This includes the longitude and altitude of the satellite. */ + @Nullable private SatellitePosition mPosition; /** - * The frequency bands to scan. Bands and earfcns won't overlap. + * The frequency band list to scan. Bands and earfcns won't overlap. * Bands will be filled only if the whole band is needed. * Maximum length of the vector is 8. */ - private int[] mBands; + private List mBandList; /** - * EARFCN (E-UTRA Absolute Radio Frequency Channel Number) Ranges + * EARFCN (E-UTRA Absolute Radio Frequency Channel Number) range list * The supported frequency range list. * Maximum length of the vector is 8. */ @@ -71,13 +75,8 @@ public class SatelliteInfo implements Parcelable { } mPosition = in.readParcelable(SatellitePosition.class.getClassLoader(), SatellitePosition.class); - int numBands = in.readInt(); - mBands = new int[numBands]; - if (numBands > 0) { - for (int i = 0; i < numBands; i++) { - mBands[i] = in.readInt(); - } - } + mBandList = new ArrayList<>(); + in.readList(mBandList, Integer.class.getClassLoader(), Integer.class); mEarfcnRangeList = in.createTypedArrayList(EarfcnRange.CREATOR); } @@ -86,15 +85,15 @@ public class SatelliteInfo implements Parcelable { * * @param satelliteId The ID of the satellite. * @param satellitePosition The {@link SatellitePosition} of the satellite. - * @param bands The list of frequency bands supported by the satellite. + * @param bandList The list of frequency bandList supported by the satellite. * @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN * ranges supported by the satellite. */ - public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition, - @NonNull int[] bands, @NonNull List earfcnRanges) { + public SatelliteInfo(@NonNull UUID satelliteId, @Nullable SatellitePosition satellitePosition, + @NonNull List bandList, @NonNull List earfcnRanges) { mId = satelliteId; mPosition = satellitePosition; - mBands = bands; + mBandList = bandList; mEarfcnRangeList = earfcnRanges; } @@ -119,12 +118,7 @@ public class SatelliteInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(new ParcelUuid(mId), flags); dest.writeParcelable(mPosition, flags); - if (mBands != null && mBands.length > 0) { - dest.writeInt(mBands.length); - dest.writeIntArray(mBands); - } else { - dest.writeInt(0); - } + dest.writeList(mBandList); dest.writeTypedList(mEarfcnRangeList); } @@ -141,8 +135,10 @@ public class SatelliteInfo implements Parcelable { /** * Returns the position of the satellite. * - * @return The {@link SatellitePosition} of the satellite. + * @return The {@link SatellitePosition} of the satellite, or {@code null} if the position is + * not available. */ + @Nullable public SatellitePosition getSatellitePosition() { return mPosition; } @@ -153,8 +149,8 @@ public class SatelliteInfo implements Parcelable { * @return The list of frequency bands. */ @NonNull - public int[] getBands() { - return mBands; + public List getBands() { + return mBandList; } /** @@ -166,4 +162,35 @@ public class SatelliteInfo implements Parcelable { public List getEarfcnRanges() { return mEarfcnRangeList; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SatelliteInfo that)) return false; + + return mId.equals(that.mId) + && Objects.equals(mPosition, that.mPosition) + && Objects.equals(mBandList, that.mBandList) + && mEarfcnRangeList.equals(that.mEarfcnRangeList); + } + + @Override + public int hashCode() { + int result = Objects.hash(mId, mPosition, mEarfcnRangeList); + result = 31 * result + Objects.hashCode(mBandList); + return result; + } + + @Override + @NonNull + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SatelliteInfo{"); + sb.append("mId=").append(mId); + sb.append(", mPosition=").append(mPosition); + sb.append(", mBandList=").append(mBandList); + sb.append(", mEarfcnRangeList=").append(mEarfcnRangeList); + sb.append('}'); + return sb.toString(); + } } diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java index 1e8c0180f456..dd463e00ebb5 100644 --- a/telephony/java/android/telephony/satellite/SatellitePosition.java +++ b/telephony/java/android/telephony/satellite/SatellitePosition.java @@ -23,6 +23,8 @@ import androidx.annotation.NonNull; import com.android.internal.telephony.flags.Flags; +import java.util.Objects; + /** * The position of a satellite in Earth orbit. * @@ -111,4 +113,24 @@ public class SatellitePosition implements Parcelable { public double getAltitudeKm() { return mAltitudeKm; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SatellitePosition that)) return false; + + return Double.compare(that.mLongitudeDegree, mLongitudeDegree) == 0 + && Double.compare(that.mAltitudeKm, mAltitudeKm) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(mLongitudeDegree, mAltitudeKm); + } + + @Override + @NonNull + public String toString() { + return "mLongitudeDegree: " + mLongitudeDegree + ", " + "mAltitudeKm: " + mAltitudeKm; + } } -- GitLab From 8756bcb6794b7e74fa272814a7b50b52ba23ba15 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Fri, 15 Nov 2024 13:20:24 +0000 Subject: [PATCH 072/656] Ensure QS classes use display-aware context This commit updates all classes in com.android.systemui.qs to utilize a @ShadeDisplayAware annotated context. This change prepares for the potential movement of the shade window to a different display, ensuring correct inflation and rendering of keyguard elements. A linter will be implemented shortly to enforce the use of this annotated context and prevent issues with non-annotated contexts/resources. Currently, this change has no functional impact as the feature flag is disabled. The @ShadeDisplayAware context remains equivalent to the @Application context. Bug: 362719719 Bug: 374267505 Test: SystemUI presubmit tests for all changed classes Flag: com.android.systemui.shade_window_goes_around Change-Id: If69405be44c14387e8e404b0c56a64e9c6c27ff8 --- .../src/com/android/systemui/qs/QSHostAdapter.kt | 3 ++- .../com/android/systemui/qs/QSSecurityFooterUtils.java | 3 ++- .../composefragment/dagger/QSFragmentComposeModule.kt | 3 ++- .../android/systemui/qs/customize/TileQueryHelper.java | 3 ++- .../qs/footer/ui/viewmodel/FooterActionsViewModel.kt | 5 +++-- .../qs/panels/ui/viewmodel/EditModeViewModel.kt | 4 ++-- .../repository/InstalledTilesComponentRepository.kt | 10 +++++----- .../domain/autoaddable/NightDisplayAutoAddable.kt | 3 ++- .../com/android/systemui/qs/tiles/UserDetailView.java | 3 ++- .../base/interactor/DisabledByPolicyInteractor.kt | 5 +++-- .../com/android/systemui/qs/tiles/di/QSTilesModule.kt | 3 ++- .../qs/tiles/dialog/InternetDialogController.java | 3 ++- .../qs/tiles/dialog/InternetDialogDelegate.java | 3 ++- .../data/repository/CustomTileDefaultsRepository.kt | 3 ++- .../repository/CustomTilePackageUpdatesRepository.kt | 3 ++- .../qs/tiles/impl/custom/domain/CustomTileMapper.kt | 6 +++++- .../interactor/CustomTileUserActionInteractor.kt | 3 ++- .../tiles/impl/internet/domain/InternetTileMapper.kt | 5 +++-- .../domain/interactor/InternetTileDataInteractor.kt | 3 ++- .../modes/domain/interactor/ModesTileDataInteractor.kt | 3 ++- .../interactor/DataSaverTileUserActionInteractor.kt | 3 ++- .../domain/interactor/UiModeNightTileDataInteractor.kt | 3 ++- 22 files changed, 53 insertions(+), 30 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt index 8b0694219630..0d464f5a0936 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt @@ -28,6 +28,7 @@ import com.android.systemui.qs.pipeline.data.repository.TileSpecRepository.Compa import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository import com.android.systemui.qs.pipeline.shared.TileSpec +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -48,7 +49,7 @@ class QSHostAdapter @Inject constructor( private val interactor: CurrentTilesInteractor, - private val context: Context, + @ShadeDisplayAware private val context: Context, private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder, @Application private val scope: CoroutineScope, dumpManager: DumpManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java index d38f8492c883..88f0318c2fbf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooterUtils.java @@ -87,6 +87,7 @@ import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig; import com.android.systemui.res.R; import com.android.systemui.security.data.model.SecurityModel; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.SecurityController; @@ -177,7 +178,7 @@ public class QSSecurityFooterUtils implements DialogInterface.OnClickListener { @Inject QSSecurityFooterUtils( - @Application Context context, DevicePolicyManager devicePolicyManager, + @ShadeDisplayAware Context context, DevicePolicyManager devicePolicyManager, UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, SecurityController securityController, @Background Looper bgLooper, DialogTransitionAnimator dialogTransitionAnimator) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt index 676f6a426264..2ec729223a8d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/dagger/QSFragmentComposeModule.kt @@ -20,6 +20,7 @@ import android.content.Context import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.flags.QSComposeFragment +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.util.Utils import dagger.Module import dagger.Provides @@ -34,7 +35,7 @@ interface QSFragmentComposeModule { @Provides @SysUISingleton @Named(QS_USING_MEDIA_PLAYER) - fun providesUsingMedia(@Application context: Context): Boolean { + fun providesUsingMedia(@ShadeDisplayAware context: Context): Boolean { return QSComposeFragment.isEnabled && Utils.useQsMediaPlayer(context) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java index 89f85ab14dd6..15e34990e111 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import com.android.systemui.res.R; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeDisplayAware; import java.util.ArrayList; import java.util.Arrays; @@ -69,7 +70,7 @@ public class TileQueryHelper { @Inject public TileQueryHelper( - Context context, + @ShadeDisplayAware Context context, UserTracker userTracker, @Main Executor mainExecutor, @Background Executor bgExecutor diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index 564bc78a3f98..8ef637545e69 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -37,6 +37,7 @@ import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig import com.android.systemui.res.R +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.util.icuMessageFormat import javax.inject.Inject import javax.inject.Named @@ -112,7 +113,7 @@ class FooterActionsViewModel( class Factory @Inject constructor( - @Application private val context: Context, + @ShadeDisplayAware private val context: Context, private val falsingManager: FalsingManager, private val footerActionsInteractor: FooterActionsInteractor, private val globalActionsDialogLiteProvider: Provider, @@ -175,7 +176,7 @@ class FooterActionsViewModel( } fun FooterActionsViewModel( - @Application appContext: Context, + @ShadeDisplayAware appContext: Context, footerActionsInteractor: FooterActionsInteractor, falsingManager: FalsingManager, globalActionsDialogLite: GlobalActionsDialogLite, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt index 4e34e73654fc..faab6960a99c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt @@ -56,7 +56,7 @@ constructor( private val tilesAvailabilityInteractor: TilesAvailabilityInteractor, private val minTilesInteractor: MinimumTilesInteractor, @ShadeDisplayAware private val configurationInteractor: ConfigurationInteractor, - @Application private val applicationContext: Context, + @ShadeDisplayAware private val context: Context, @Named("Default") private val defaultGridLayout: GridLayout, @Application private val applicationScope: CoroutineScope, gridLayoutTypeInteractor: GridLayoutTypeInteractor, @@ -140,7 +140,7 @@ constructor( .combine(configurationInteractor.onAnyConfigurationChange.emitOnStart()) { tiles, _ -> - tiles.fastMap { it.load(applicationContext) } + tiles.fastMap { it.load(context) } } } else { emptyFlow() diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt index c5b27376a82a..41cdefdb2396 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt @@ -30,8 +30,8 @@ import androidx.annotation.GuardedBy import com.android.systemui.common.data.repository.PackageChangeRepository import com.android.systemui.common.shared.model.PackageChangeModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.util.kotlin.isComponentActuallyEnabled import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -54,7 +54,7 @@ interface InstalledTilesComponentRepository { class InstalledTilesComponentRepositoryImpl @Inject constructor( - @Application private val applicationContext: Context, + @ShadeDisplayAware private val context: Context, @Background private val backgroundScope: CoroutineScope, private val packageChangeRepository: PackageChangeRepository ) : InstalledTilesComponentRepository { @@ -77,10 +77,10 @@ constructor( * context. */ val packageManager = - if (applicationContext.userId == userId) { - applicationContext.packageManager + if (context.userId == userId) { + context.packageManager } else { - applicationContext + context .createContextAsUser( UserHandle.of(userId), /* flags */ 0, diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt index 31ea734fb842..e9c91ca0db12 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/NightDisplayAutoAddable.kt @@ -27,6 +27,7 @@ import com.android.systemui.qs.pipeline.domain.model.AutoAddTracking import com.android.systemui.qs.pipeline.domain.model.AutoAddable import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.NightDisplayTile +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -42,7 +43,7 @@ class NightDisplayAutoAddable @Inject constructor( private val nightDisplayListenerBuilder: NightDisplayListenerModule.Builder, - context: Context, + @ShadeDisplayAware context: Context, ) : AutoAddable { private val enabled = ColorDisplayManager.isNightDisplayAvailable(context) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java index 6b654beea149..fed8b60a653f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java @@ -39,6 +39,7 @@ import com.android.systemui.qs.PseudoGridView; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController; import com.android.systemui.res.R; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter; import com.android.systemui.statusbar.policy.UserSwitcherController; @@ -95,7 +96,7 @@ public class UserDetailView extends PseudoGridView { } @Inject - public Adapter(Context context, UserSwitcherController controller, + public Adapter(@ShadeDisplayAware Context context, UserSwitcherController controller, UiEventLogger uiEventLogger, FalsingManager falsingManager) { super(controller); mContext = context; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt index 87b89ea6810a..45775272e01f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt @@ -27,6 +27,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext @@ -70,7 +71,7 @@ interface DisabledByPolicyInteractor { class DisabledByPolicyInteractorImpl @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, private val activityStarter: ActivityStarter, private val restrictedLockProxy: RestrictedLockProxy, @Background private val backgroundDispatcher: CoroutineDispatcher, @@ -105,7 +106,7 @@ constructor( /** Mockable proxy for [RestrictedLockUtilsInternal] static methods. */ @VisibleForTesting -class RestrictedLockProxy @Inject constructor(private val context: Context) { +class RestrictedLockProxy @Inject constructor(@ShadeDisplayAware private val context: Context) { @WorkerThread fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean = diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt index b766ee0d4333..222fa3efbe94 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel +import com.android.systemui.shade.ShadeDisplayAware import dagger.Binds import dagger.Module import dagger.Provides @@ -66,6 +67,6 @@ interface QSTilesModule { companion object { - @Provides fun provideTilesTheme(context: Context): Theme = context.theme + @Provides fun provideTilesTheme(@ShadeDisplayAware context: Context): Theme = context.theme } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 8f6c4e743269..244f024625db 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -87,6 +87,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.connectivity.AccessPointController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.LocationController; @@ -240,7 +241,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi } @Inject - public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger, + public InternetDialogController(@ShadeDisplayAware Context context, UiEventLogger uiEventLogger, ActivityStarter starter, AccessPointController accessPointController, SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java index 89b9eee52f2a..0ab533bb9838 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java @@ -71,6 +71,7 @@ import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.res.R; +import com.android.systemui.shade.ShadeDisplayAware; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.wifitrackerlib.WifiEntry; @@ -190,7 +191,7 @@ public class InternetDialogDelegate implements @AssistedInject public InternetDialogDelegate( - Context context, + @ShadeDisplayAware Context context, InternetDialogManager internetDialogManager, InternetDialogController internetDialogController, @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt index 1546ec2c54bd..32fb1d18724d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt @@ -26,6 +26,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults import com.android.systemui.qs.tiles.impl.di.QSTileScope +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -68,7 +69,7 @@ interface CustomTileDefaultsRepository { class CustomTileDefaultsRepositoryImpl @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, @Application applicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, ) : CustomTileDefaultsRepository { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt index 0ebd6f2b4ac3..cd4938f01b63 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt @@ -28,6 +28,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.impl.di.QSTileScope +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -51,7 +52,7 @@ class CustomTilePackageUpdatesRepositoryImpl @Inject constructor( private val tileSpec: TileSpec.CustomTileSpec, - @Application private val context: Context, + @ShadeDisplayAware private val context: Context, @QSTileScope private val tileScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, ) : CustomTilePackageUpdatesRepository { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt index 60aa4ea4759f..c446865f31af 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt @@ -30,12 +30,16 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState +import com.android.systemui.shade.ShadeDisplayAware import javax.inject.Inject @SysUISingleton class CustomTileMapper @Inject -constructor(private val context: Context, private val uriGrantsManager: IUriGrantsManager) : +constructor( + @ShadeDisplayAware private val context: Context, + private val uriGrantsManager: IUriGrantsManager +) : QSTileDataToStateMapper { override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt index af2bb9d0d2f7..1153b5c67261 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt @@ -42,6 +42,7 @@ import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataMod import com.android.systemui.qs.tiles.impl.di.QSTileScope import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.settings.DisplayTracker +import com.android.systemui.shade.ShadeDisplayAware import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -51,7 +52,7 @@ import kotlinx.coroutines.withContext class CustomTileUserActionInteractor @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, private val tileSpec: TileSpec, private val qsTileLogger: QSTileLogger, private val windowManager: IWindowManager, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt index fc945851cdad..1a6876d0b765 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt @@ -30,6 +30,7 @@ import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileMode import com.android.systemui.qs.tiles.viewmodel.QSTileConfig import com.android.systemui.qs.tiles.viewmodel.QSTileState import com.android.systemui.res.R +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel import javax.inject.Inject @@ -37,9 +38,9 @@ import javax.inject.Inject class InternetTileMapper @Inject constructor( - @Main private val resources: Resources, + @ShadeDisplayAware private val resources: Resources, private val theme: Resources.Theme, - private val context: Context, + @ShadeDisplayAware private val context: Context, @Main private val handler: Handler, ) : QSTileDataToStateMapper { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt index 6fe3979fa446..6d10843decc0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel import com.android.systemui.res.R +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor @@ -54,7 +55,7 @@ import kotlinx.coroutines.flow.stateIn class InternetTileDataInteractor @Inject constructor( - private val context: Context, + @ShadeDisplayAware private val context: Context, @Application private val scope: CoroutineScope, airplaneModeRepository: AirplaneModeRepository, private val connectivityRepository: ConnectivityRepository, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt index 3e44258229f9..9b2880b6d47f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt @@ -28,6 +28,7 @@ import com.android.systemui.qs.tiles.ModesTile import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo @@ -42,7 +43,7 @@ import kotlinx.coroutines.flow.map class ModesTileDataInteractor @Inject constructor( - val context: Context, + @ShadeDisplayAware val context: Context, val zenModeInteractor: ZenModeInteractor, @Background val bgDispatcher: CoroutineDispatcher, ) : QSTileDataInteractor { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt index 252e3f84df6c..05bdf0a92679 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt @@ -32,6 +32,7 @@ import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverDialogDelegate import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.settings.UserFileManager +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.DataSaverController import javax.inject.Inject @@ -42,7 +43,7 @@ import kotlinx.coroutines.withContext class DataSaverTileUserActionInteractor @Inject constructor( - @Application private val context: Context, + @ShadeDisplayAware private val context: Context, @Main private val coroutineContext: CoroutineContext, @Background private val backgroundContext: CoroutineContext, private val dataSaverController: DataSaverController, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt index c928e8af17fc..7af3576d8cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt @@ -25,6 +25,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel +import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.LocationController @@ -38,7 +39,7 @@ import kotlinx.coroutines.flow.flowOf class UiModeNightTileDataInteractor @Inject constructor( - @Application private val context: Context, + @ShadeDisplayAware private val context: Context, private val configurationController: ConfigurationController, private val uiModeManager: UiModeManager, private val batteryController: BatteryController, -- GitLab From 47414432d61ce79083505ff013372d68eb058fc6 Mon Sep 17 00:00:00 2001 From: Taran Singh Date: Thu, 14 Nov 2024 19:54:50 +0000 Subject: [PATCH 073/656] Revert^2 "Verify KeyEvents in IME" d12c5fe88d4d737afea9a57f3f928ed4186737cc Fixed the broken API test in postsubmit by making the newly introduced InputMethodSession method `default`. Bug: 331730488 Bug: 378971472 Test: atest CtsInputMethodTestCases InputMethodServiceTest Flag: android.view.inputmethod.verify_key_event Change-Id: I6589cc73839acb6b2c3f9256c664b5db7e1ade8a --- core/api/current.txt | 2 + .../AbstractInputMethodService.java | 17 ++++++++ .../IInputMethodSessionWrapper.java | 43 ++++++++++++++++++- .../InputMethodService.java | 18 ++++++++ .../view/inputmethod/InputMethodSession.java | 18 ++++++++ .../android/view/inputmethod/flags.aconfig | 8 ++++ 6 files changed, 104 insertions(+), 2 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 5b9f1ef3408a..bf93860f58db 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -21070,6 +21070,7 @@ package android.inputmethodservice { method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface(); method public abstract android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface(); method public boolean onGenericMotionEvent(android.view.MotionEvent); + method @FlaggedApi("android.view.inputmethod.verify_key_event") public boolean onShouldVerifyKeyEvent(@NonNull android.view.KeyEvent); method public boolean onTrackballEvent(android.view.MotionEvent); } @@ -21087,6 +21088,7 @@ package android.inputmethodservice { method public void dispatchTrackballEvent(int, android.view.MotionEvent, android.view.inputmethod.InputMethodSession.EventCallback); method public boolean isEnabled(); method public boolean isRevoked(); + method @FlaggedApi("android.view.inputmethod.verify_key_event") public boolean onShouldVerifyKeyEvent(@NonNull android.view.KeyEvent); method public void revokeSelf(); method public void setEnabled(boolean); } diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java index 4bc5bd2427ea..26308f69cfbe 100644 --- a/core/java/android/inputmethodservice/AbstractInputMethodService.java +++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java @@ -16,6 +16,9 @@ package android.inputmethodservice; +import static android.view.inputmethod.Flags.FLAG_VERIFY_KEY_EVENT; + +import android.annotation.FlaggedApi; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; @@ -193,6 +196,12 @@ public abstract class AbstractInputMethodService extends WindowProviderService } } + @FlaggedApi(FLAG_VERIFY_KEY_EVENT) + @Override + public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) { + return AbstractInputMethodService.this.onShouldVerifyKeyEvent(event); + } + /** * Take care of dispatching incoming trackball events to the appropriate * callbacks on the service, and tell the client when this is done. @@ -308,6 +317,14 @@ public abstract class AbstractInputMethodService extends WindowProviderService return false; } + /** + * @see InputMethodService#onShouldVerifyKeyEvent(KeyEvent) + */ + @FlaggedApi(FLAG_VERIFY_KEY_EVENT) + public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) { + return false; + } + /** @hide */ @Override public final int getWindowType() { diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java index 62b131af74fe..9b37533f5b02 100644 --- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java @@ -16,12 +16,16 @@ package android.inputmethodservice; +import static android.view.inputmethod.Flags.verifyKeyEvent; + import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.graphics.Rect; +import android.hardware.input.InputManager; import android.os.Bundle; import android.os.Looper; import android.os.Message; +import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; import android.view.InputChannel; @@ -41,6 +45,8 @@ import com.android.internal.inputmethod.IRemoteInputConnection; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import java.util.Objects; + class IInputMethodSessionWrapper extends IInputMethodSession.Stub implements HandlerCaller.Callback { private static final String TAG = "InputMethodWrapper"; @@ -56,6 +62,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub private static final int DO_REMOVE_IME_SURFACE = 130; private static final int DO_FINISH_INPUT = 140; private static final int DO_INVALIDATE_INPUT = 150; + private final Context mContext; @UnsupportedAppUsage @@ -66,6 +73,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub public IInputMethodSessionWrapper(Context context, InputMethodSession inputMethodSession, InputChannel channel) { + mContext = context; mCaller = new HandlerCaller(context, null, this, true /*asyncHandler*/); mInputMethodSession = inputMethodSession; @@ -233,6 +241,8 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub } private final class ImeInputEventReceiver extends InputEventReceiver implements InputMethodSession.EventCallback { + // Time after which a KeyEvent is invalid + private static final long KEY_EVENT_ALLOW_PERIOD_MS = 100L; private final SparseArray mPendingEvents = new SparseArray(); public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) { @@ -247,10 +257,23 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub return; } + if (event instanceof KeyEvent keyEvent && needsVerification(keyEvent)) { + // any KeyEvent with modifiers (e.g. Ctrl/Alt/Fn) must be verified that + // they originated from system. + InputManager im = mContext.getSystemService(InputManager.class); + Objects.requireNonNull(im); + final long age = SystemClock.uptimeMillis() - keyEvent.getEventTime(); + if (age >= KEY_EVENT_ALLOW_PERIOD_MS && im.verifyInputEvent(keyEvent) == null) { + Log.w(TAG, "Unverified or Invalid KeyEvent injected into IME. Dropping " + + keyEvent); + finishInputEvent(event, false /* handled */); + return; + } + } + final int seq = event.getSequenceNumber(); mPendingEvents.put(seq, event); - if (event instanceof KeyEvent) { - KeyEvent keyEvent = (KeyEvent)event; + if (event instanceof KeyEvent keyEvent) { mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this); } else { MotionEvent motionEvent = (MotionEvent)event; @@ -271,5 +294,21 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub finishInputEvent(event, handled); } } + + private boolean hasKeyModifiers(KeyEvent event) { + if (event.hasNoModifiers()) { + return false; + } + return event.hasModifiers(KeyEvent.META_CTRL_ON) + || event.hasModifiers(KeyEvent.META_ALT_ON) + || event.hasModifiers(KeyEvent.KEYCODE_FUNCTION); + } + + private boolean needsVerification(KeyEvent event) { + //TODO(b/331730488): Handle a11y events as well. + return verifyKeyEvent() + && (hasKeyModifiers(event) + || mInputMethodSession.onShouldVerifyKeyEvent(event)); + } } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index dadb5c386b76..a8fde4a3f7c6 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -56,6 +56,7 @@ import static android.view.inputmethod.ConnectionlessHandwritingCallback.CONNECT import static android.view.inputmethod.ConnectionlessHandwritingCallback.CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED; import static android.view.inputmethod.Flags.FLAG_CONNECTIONLESS_HANDWRITING; import static android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP_API; +import static android.view.inputmethod.Flags.FLAG_VERIFY_KEY_EVENT; import static android.view.inputmethod.Flags.ctrlShiftShortcut; import static android.view.inputmethod.Flags.predictiveBackIme; @@ -3734,6 +3735,23 @@ public class InputMethodService extends AbstractInputMethodService { return doMovementKey(keyCode, event, MOVEMENT_DOWN); } + /** + * Received by the IME before dispatch to {@link #onKeyDown(int, KeyEvent)} to let the system + * know if the {@link KeyEvent} needs to be verified that it originated from the system. + * {@link KeyEvent}s may originate from outside of the system and any sensitive keys should be + * marked for verification. One example of this could be using key shortcuts for switching to + * another IME. + * + * @param keyEvent the event that may need verification. + * @return {@code true} if {@link KeyEvent} should have its HMAC verified before dispatch, + * {@code false} otherwise. + */ + @FlaggedApi(FLAG_VERIFY_KEY_EVENT) + @Override + public boolean onShouldVerifyKeyEvent(@NonNull KeyEvent keyEvent) { + return false; + } + /** * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java index 4f48cb684e8c..1806a8369d01 100644 --- a/core/java/android/view/inputmethod/InputMethodSession.java +++ b/core/java/android/view/inputmethod/InputMethodSession.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.NonNull; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; import android.os.Bundle; @@ -124,6 +125,23 @@ public interface InputMethodSession { */ public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback); + /** + * Received by the IME before dispatch to {@link InputMethodService#onKeyDown(int, KeyEvent)} + * to let the system know if the {@link KeyEvent} needs to be verified that it originated from + * the system. {@link KeyEvent}s may originate from outside of the system and any sensitive keys + * should be marked for verification. One example of this could be using key shortcuts for + * switching to another IME. + * + * @param event the event that may need verification. + * @return {@code true} if {@link KeyEvent} should have its HMAC verified before dispatch, + * {@code false} otherwise. + * + * @hide + */ + default boolean onShouldVerifyKeyEvent(@NonNull KeyEvent event) { + return false; + } + /** * This method is called when there is a track ball event. * diff --git a/core/java/android/view/inputmethod/flags.aconfig b/core/java/android/view/inputmethod/flags.aconfig index deaf95797127..73abc472be6d 100644 --- a/core/java/android/view/inputmethod/flags.aconfig +++ b/core/java/android/view/inputmethod/flags.aconfig @@ -184,3 +184,11 @@ flag { bug: "350047836" is_fixed_read_only: true } + +flag { + name: "verify_key_event" + namespace: "input_method" + description: "Verify KeyEvents in IME" + bug: "331730488" + is_fixed_read_only: true +} -- GitLab From 217c1f4a9c13bf11192af665d7349df9f7b27453 Mon Sep 17 00:00:00 2001 From: Anton Potapov Date: Fri, 15 Nov 2024 16:32:19 +0000 Subject: [PATCH 074/656] Add slider input events interactor. This interactor provides touches and button presses that affect the Slider of the corresponding `@VolumeDialogSliderScope` Flag: com.android.systemui.volume_redesign Test: manual on foldable Test: atest VolumeDialogSliderInputEventsInteractorTest Bug: 369995895 Change-Id: Ia4339b8d1f0f1a470a07a3b541355d8ddfab6b55 --- ...meDialogSliderInputEventsInteractorTest.kt | 92 +++++++++++++++++++ .../dagger/VolumeDialogSliderComponent.kt | 3 + ...VolumeDialogSliderTouchEventsRepository.kt | 37 ++++++++ ...VolumeDialogSliderInputEventsInteractor.kt | 61 ++++++++++++ .../sliders/shared/model/SliderInputEvent.kt | 27 ++++++ .../ui/VolumeDialogSliderTouchesViewBinder.kt | 52 +++++++++++ .../ui/VolumeDialogSlidersViewBinder.kt | 14 ++- .../VolumeDialogSliderTouchesViewModel.kt | 36 ++++++++ ...DialogSliderTouchEventsRepositoryKosmos.kt | 22 +++++ ...eDialogSliderInputEventsInteractorKosmo.kt | 33 +++++++ 10 files changed, 373 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt create mode 100644 packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt create mode 100644 packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepositoryKosmos.kt create mode 100644 packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorKosmo.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt new file mode 100644 index 000000000000..799ca4a49038 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.domain.interactor + +import android.app.ActivityManager +import android.testing.TestableLooper +import android.view.MotionEvent +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.fakeVolumeDialogController +import com.android.systemui.testKosmos +import com.android.systemui.volume.Events +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor +import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.runner.RunWith + +private val volumeDialogTimeout = 3.seconds + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper +class VolumeDialogSliderInputEventsInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + + private lateinit var underTest: VolumeDialogSliderInputEventsInteractor + + @Before + fun setup() { + underTest = kosmos.volumeDialogSliderInputEventsInteractor + } + + @Test + fun inputEvents_resetDialogVisibilityTimeout() = + with(kosmos) { + testScope.runTest { + runCurrent() + val dialogVisibility by + collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility) + fakeVolumeDialogController.onShowRequested( + Events.SHOW_REASON_VOLUME_CHANGED, + false, + ActivityManager.LOCK_TASK_MODE_LOCKED, + ) + runCurrent() + advanceTimeBy(volumeDialogTimeout / 2) + assertThat(dialogVisibility) + .isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + + underTest.onTouchEvent( + MotionEvent.obtain( + /* downTime = */ 0, + /* eventTime = */ 0, + /* action = */ 0, + /* x = */ 0f, + /* y = */ 0f, + /* metaState = */ 0, + ) + ) + advanceTimeBy(volumeDialogTimeout / 2) + + assertThat(dialogVisibility) + .isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt index 538ee47915a8..772ae7736cad 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.dialog.sliders.dagger import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType +import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderTouchesViewBinder import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder import dagger.BindsInstance import dagger.Subcomponent @@ -31,6 +32,8 @@ interface VolumeDialogSliderComponent { fun sliderViewBinder(): VolumeDialogSliderViewBinder + fun sliderTouchesViewBinder(): VolumeDialogSliderTouchesViewBinder + @Subcomponent.Factory interface Factory { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt new file mode 100644 index 000000000000..adc2383c3a46 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepository.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.data.repository + +import android.annotation.SuppressLint +import android.view.MotionEvent +import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull + +@VolumeDialogSliderScope +class VolumeDialogSliderTouchEventsRepository @Inject constructor() { + + @SuppressLint("SharedFlowCreation") + private val mutableSliderTouchEvents: MutableStateFlow = MutableStateFlow(null) + val sliderTouchEvent: Flow = mutableSliderTouchEvents.filterNotNull() + + fun update(event: MotionEvent) { + mutableSliderTouchEvents.tryEmit(MotionEvent.obtain(event)) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt new file mode 100644 index 000000000000..c7b4184a9f2f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractor.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.domain.interactor + +import android.view.MotionEvent +import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogCallbacksInteractor +import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor +import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel +import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope +import com.android.systemui.volume.dialog.sliders.data.repository.VolumeDialogSliderTouchEventsRepository +import com.android.systemui.volume.dialog.sliders.shared.model.SliderInputEvent +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.onEach + +@VolumeDialogSliderScope +class VolumeDialogSliderInputEventsInteractor +@Inject +constructor( + @VolumeDialog coroutineScope: CoroutineScope, + volumeDialogCallbacksInteractor: VolumeDialogCallbacksInteractor, + private val visibilityInteractor: VolumeDialogVisibilityInteractor, + private val repository: VolumeDialogSliderTouchEventsRepository, +) { + + val event: Flow = + merge( + repository.sliderTouchEvent.map { SliderInputEvent.Touch(it) }, + volumeDialogCallbacksInteractor.event + .filterIsInstance(VolumeDialogEventModel.VolumeChangedFromKey::class) + .map { SliderInputEvent.Button }, + ) + + init { + event.onEach { visibilityInteractor.resetDismissTimeout() }.launchIn(coroutineScope) + } + + fun onTouchEvent(newEvent: MotionEvent) { + repository.update(newEvent) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt new file mode 100644 index 000000000000..37dbb4b3a81d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/shared/model/SliderInputEvent.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.shared.model + +import android.view.MotionEvent + +/** Models input event happened on the Volume Slider */ +sealed interface SliderInputEvent { + + data class Touch(val event: MotionEvent) : SliderInputEvent + + data object Button : SliderInputEvent +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt new file mode 100644 index 000000000000..7fd177d55d76 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.ui + +import android.annotation.SuppressLint +import android.view.View +import com.android.systemui.lifecycle.WindowLifecycleState +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.lifecycle.viewModel +import com.android.systemui.res.R +import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope +import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderTouchesViewModel +import com.google.android.material.slider.Slider +import javax.inject.Inject + +@VolumeDialogSliderScope +class VolumeDialogSliderTouchesViewBinder +@Inject +constructor(private val viewModelFactory: VolumeDialogSliderTouchesViewModel.Factory) { + + @SuppressLint("ClickableViewAccessibility") + fun bind(view: View) { + with(view.requireViewById(R.id.volume_dialog_slider)) { + repeatWhenAttached { + viewModel( + traceName = "VolumeDialogSliderTouchesViewBinder", + minWindowLifecycleState = WindowLifecycleState.ATTACHED, + factory = { viewModelFactory.create() }, + ) { viewModel -> + setOnTouchListener { _, event -> + viewModel.onTouchEvent(event) + false + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt index 9078f82d3e98..1b2b4fff5b8e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt @@ -26,6 +26,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel import com.android.systemui.res.R import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope +import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel import javax.inject.Inject import kotlinx.coroutines.flow.launchIn @@ -50,7 +51,7 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) ) { viewModel -> viewModel.sliders .onEach { uiModel -> - uiModel.sliderComponent.sliderViewBinder().bind(mainSliderContainer) + uiModel.sliderComponent.bindSlider(mainSliderContainer) val floatingSliderViewBinders = uiModel.floatingSliderComponent floatingSlidersContainer.ensureChildCount( @@ -58,9 +59,9 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) count = floatingSliderViewBinders.size, ) floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent -> - sliderComponent - .sliderViewBinder() - .bind(floatingSlidersContainer.getChildAt(index)) + sliderComponent.bindSlider( + floatingSlidersContainer.getChildAt(index) + ) } } .launchIn(this) @@ -68,6 +69,11 @@ constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) } } } + + private fun VolumeDialogSliderComponent.bindSlider(sliderContainer: View) { + sliderViewBinder().bind(sliderContainer) + sliderTouchesViewBinder().bind(sliderContainer) + } } private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt new file mode 100644 index 000000000000..144c75da2b2b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.ui.viewmodel + +import android.view.MotionEvent +import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +class VolumeDialogSliderTouchesViewModel +@AssistedInject +constructor(private val interactor: VolumeDialogSliderInputEventsInteractor) { + + fun onTouchEvent(event: MotionEvent) { + interactor.onTouchEvent(event) + } + + @AssistedFactory + interface Factory { + fun create(): VolumeDialogSliderTouchesViewModel + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepositoryKosmos.kt new file mode 100644 index 000000000000..1e8dfa1c9767 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/data/repository/VolumeDialogSliderTouchEventsRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.volumeDialogSliderTouchEventsRepository by + Kosmos.Fixture { VolumeDialogSliderTouchEventsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorKosmo.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorKosmo.kt new file mode 100644 index 000000000000..39ae5aa3ce1f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInputEventsInteractorKosmo.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.dialog.sliders.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogCallbacksInteractor +import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor +import com.android.systemui.volume.dialog.sliders.data.repository.volumeDialogSliderTouchEventsRepository + +val Kosmos.volumeDialogSliderInputEventsInteractor: VolumeDialogSliderInputEventsInteractor by + Kosmos.Fixture { + VolumeDialogSliderInputEventsInteractor( + applicationCoroutineScope, + volumeDialogCallbacksInteractor, + volumeDialogVisibilityInteractor, + volumeDialogSliderTouchEventsRepository, + ) + } -- GitLab From 858168cf8b0322f206f65178533e040fa8a2240a Mon Sep 17 00:00:00 2001 From: yutingfang Date: Thu, 31 Oct 2024 16:14:07 -0700 Subject: [PATCH 075/656] Add checkOp overload APIs that accept attributionTag Add new checkOp overloads that accept attributionTag as a parameter to bring functionality parity with noteOp. Since the checkOp APIs now have the same security check as noteOp/startOp, we can deprecate unsafe version of checkOps. Flag: android.permission.flags.check_op_overload_api_enabled Bug: 240617242 Test: CTS atest AppOpsTest Change-Id: Iac486dafcb18464068df9e38243d34112a2b87e0 --- core/api/current.txt | 15 +- core/java/android/app/AppOpsManager.java | 196 +++++++++++++----- core/java/android/permission/flags.aconfig | 11 +- .../android/internal/app/IAppOpsService.aidl | 2 +- .../android/server/appop/AppOpsService.java | 5 +- 5 files changed, 165 insertions(+), 64 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 15411f41be30..6c2a8b43f16b 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -5098,8 +5098,11 @@ package android.app { } public class AppOpsManager { - method @Deprecated public int checkOp(@NonNull String, int, @NonNull String); - method @Deprecated public int checkOpNoThrow(@NonNull String, int, @NonNull String); + method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOp(@NonNull String, int, @NonNull String); + method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOp(@NonNull String, int, @NonNull String, @Nullable String); + method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpNoThrow(@NonNull String, int, @NonNull String, @Nullable String); + method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpNoThrow(@NonNull String, int, @NonNull String); + method @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int checkOpRawNoThrow(@NonNull String, int, @NonNull String, @Nullable String); method @Deprecated public void checkPackage(int, @NonNull String); method @Deprecated public void finishOp(@NonNull String, int, @NonNull String); method public void finishOp(@NonNull String, int, @NonNull String, @Nullable String); @@ -5128,10 +5131,10 @@ package android.app { method public void startWatchingMode(@NonNull String, @Nullable String, int, @NonNull android.app.AppOpsManager.OnOpChangedListener); method public void stopWatchingActive(@NonNull android.app.AppOpsManager.OnOpActiveChangedListener); method public void stopWatchingMode(@NonNull android.app.AppOpsManager.OnOpChangedListener); - method public int unsafeCheckOp(@NonNull String, int, @NonNull String); - method public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String); - method public int unsafeCheckOpRaw(@NonNull String, int, @NonNull String); - method public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOp(@NonNull String, int, @NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpNoThrow(@NonNull String, int, @NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpRaw(@NonNull String, int, @NonNull String); + method @Deprecated @FlaggedApi("android.permission.flags.check_op_overload_api_enabled") public int unsafeCheckOpRawNoThrow(@NonNull String, int, @NonNull String); field public static final int MODE_ALLOWED = 0; // 0x0 field public static final int MODE_DEFAULT = 3; // 0x3 field public static final int MODE_ERRORED = 2; // 0x2 diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6c03b32a4816..b1ddd7b61601 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -8916,12 +8916,21 @@ public class AppOpsManager { } /** - * Do a quick check for whether an application might be able to perform an operation. - * This is not a security check; you must use {@link #noteOp(String, int, String, - * String, String)} or {@link #startOp(String, int, String, String, String)} for your actual - * security checks. This function can just be used for a quick check to see if an operation has - * been disabled for the application, as an early reject of some work. This does not modify the - * time stamp or other data about the operation. + * Check whether an application might be able to perform an operation. + *

+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is + * not a security check; you must use {@link #noteOp(String, int, String, String, + * String)} or {@link #startOp(String, int, String, String, String)} for your actual security + * checks. This function can just be used for a quick check to see if an operation has been + * disabled for the application, as an early reject of some work. + *

+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, this + * is no longer an unsafe check, and it does the same security check as {@link #noteOp(String, + * int, String, String, String)} and {@link #startOp(String, int, String, String, String)}. + * However, it's preferred to use {@link #checkOp(String, int, String)}, since the word "unsafe" + * in the name of this API is no longer accurate. + *

+ * This API does not modify the time stamp or other data about the operation. * * @param op The operation to check. One of the OPSTR_* constants. * @param uid The user id of the application attempting to perform the operation. @@ -8930,31 +8939,108 @@ public class AppOpsManager { * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without * causing the app to crash). * @throws SecurityException If the app has been configured to crash on this op. + * + * @deprecated Use {@link #checkOp(String, int, String)} */ + @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int unsafeCheckOp(@NonNull String op, int uid, @NonNull String packageName) { return checkOp(strOpToOp(op), uid, packageName); } /** - * @deprecated Renamed to {@link #unsafeCheckOp(String, int, String)}. + * Check whether an application can perform an operation. + *

+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is + * not a security check; you must use {@link #noteOp(String, int, String, String, + * String)} or {@link #startOp(String, int, String, String, String)} for your actual security + * checks. This function can just be used for a quick check to see if an operation has been + * disabled for the application, as an early reject of some work. + *

+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, it + * does the same security check as {@link #noteOp(String, int, String, String, String)} and + * {@link #startOp(String, int, String, String, String)}, and should be preferred to use. + *

+ * This API does not modify the time stamp or other data about the operation. + * + * @param op The operation to check. One of the OPSTR_* constants. + * @param uid The uid of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or + * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without + * causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. */ - @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int checkOp(@NonNull String op, int uid, @NonNull String packageName) { return checkOp(strOpToOp(op), uid, packageName); } /** - * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it - * returns {@link #MODE_ERRORED}. + * Like {@link #unsafeCheckOp(String, int, String)} but instead of throwing a + * {@link SecurityException} it returns {@link #MODE_ERRORED}. + * + * @deprecated Use {@link #checkOpNoThrow(String, int, String)} */ + @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int unsafeCheckOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) { return checkOpNoThrow(strOpToOp(op), uid, packageName); } /** - * @deprecated Renamed to {@link #unsafeCheckOpNoThrow(String, int, String)}. + * Check whether an application can perform an operation. It does the same security check as + * {@link #noteOp(String, int, String, String, String)} and {@link #startOp(String, int, String, + * String, String)}, but does not modify the time stamp or other data about the operation. + * + * @param op The operation to check. One of the OPSTR_* constants. + * @param uid The uid of the application attempting to perform the operation. + * @param packageName The name of the application attempting to perform the operation. + * @param attributionTag The {@link Context#createAttributionContext attribution tag} of the + * calling context or {@code null} for default attribution + * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED} + * if it is not allowed and should be silently ignored (without causing the app to crash). + * @throws SecurityException If the app has been configured to crash on this op. */ - @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) + public int checkOp(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String attributionTag) { + int mode = checkOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, + Context.DEVICE_ID_DEFAULT); + if (mode == MODE_ERRORED) { + throw new SecurityException(buildSecurityExceptionMsg(strOpToOp(op), uid, packageName)); + } + return mode; + } + + /** + * Like {@link #checkOp(String, int, String, String)} but instead of throwing a + * {@link SecurityException} it returns {@link #MODE_ERRORED}. + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) + public int checkOpNoThrow(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String attributionTag) { + return checkOpNoThrow(strOpToOp(op), uid, packageName, attributionTag, + Context.DEVICE_ID_DEFAULT); + } + + /** + * Like {@link #checkOp(String, int, String, String)} but returns the raw mode + * associated with the op. Does not throw a security exception, does not translate + * {@link #MODE_FOREGROUND}. + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) + public int checkOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String attributionTag) { + return checkOpRawNoThrow(strOpToOp(op), uid, packageName, attributionTag, + Context.DEVICE_ID_DEFAULT); + } + + /** + * Like {@link #checkOp(String, int, String)} but instead of throwing a + * {@link SecurityException} it returns {@link #MODE_ERRORED}. + */ + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int checkOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) { return checkOpNoThrow(strOpToOp(op), uid, packageName); } @@ -8962,16 +9048,23 @@ public class AppOpsManager { /** * Like {@link #checkOp} but returns the raw mode associated with the op. * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. + * + * @deprecated Use {@link #checkOpRawNoThrow(String, int, String, String)} instead */ + @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int unsafeCheckOpRaw(@NonNull String op, int uid, @NonNull String packageName) { return unsafeCheckOpRawNoThrow(op, uid, packageName); } /** - * Like {@link #unsafeCheckOpNoThrow(String, int, String)} but returns the raw - * mode associated with the op. Does not throw a security exception, does not translate - * {@link #MODE_FOREGROUND}. + * Like {@link #checkOp} but returns the raw mode associated with the op. + * Does not throw a security exception, does not translate {@link #MODE_FOREGROUND}. + * + * @deprecated Use {@link #checkOpRawNoThrow(String, int, String, String)} instead */ + @Deprecated + @FlaggedApi(android.permission.flags.Flags.FLAG_CHECK_OP_OVERLOAD_API_ENABLED) public int unsafeCheckOpRawNoThrow(@NonNull String op, int uid, @NonNull String packageName) { return unsafeCheckOpRawNoThrow(strOpToOp(op), uid, packageName); } @@ -8982,8 +9075,9 @@ public class AppOpsManager { * @hide */ public int unsafeCheckOpRawNoThrow(int op, @NonNull AttributionSource attributionSource) { - return unsafeCheckOpRawNoThrow(op, attributionSource.getUid(), - attributionSource.getPackageName(), attributionSource.getDeviceId()); + return checkOpRawNoThrow(op, attributionSource.getUid(), + attributionSource.getPackageName(), attributionSource.getAttributionTag(), + attributionSource.getDeviceId()); } /** @@ -9004,20 +9098,20 @@ public class AppOpsManager { * @hide */ public int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName) { - return unsafeCheckOpRawNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT); + return checkOpRawNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT); } - private int unsafeCheckOpRawNoThrow(int op, int uid, @NonNull String packageName, - int virtualDeviceId) { + private int checkOpRawNoThrow(int op, int uid, @NonNull String packageName, + @Nullable String attributionTag, int virtualDeviceId) { try { int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( - new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, + new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag, "unsafeCheckOpRawNoThrow")); } else { mode = mService.checkOperationRawForDevice( - op, uid, packageName, null, virtualDeviceId); + op, uid, packageName, attributionTag, virtualDeviceId); } return mode; } catch (RemoteException e) { @@ -9416,10 +9510,12 @@ public class AppOpsManager { } /** - * Do a quick check for whether an application might be able to perform an operation. - * This is not a security check; you must use {@link #noteOp(String, int, String, - * String, String)} or {@link #startOp(int, int, String, boolean, String, String)} for your - * actual security checks, which also ensure that the given uid and package name are consistent. + * Check whether an application can perform an operation. + *

+ * For platform versions before {@link android.os.Build.VERSION_CODES#BAKLAVA}, this is + * not a security check; you must use {@link #noteOp(String, int, String, String, + * String)} or {@link #startOp(int, int, String, boolean, String, String)} for your actual + * security checks, which also ensure that the given uid and package name are consistent. * This function can just be used for a quick check to see if an operation has been disabled for * the application, as an early reject of some work. This does not modify the time stamp or * other data about the operation. @@ -9435,6 +9531,13 @@ public class AppOpsManager { * as {@link #MODE_ALLOWED}. * * + *

+ * For platform versions equal to or after {@link android.os.Build.VERSION_CODES#BAKLAVA}, it + * does the same security check as {@link #noteOp(String, int, String, String, String)} and + * {@link #startOp(String, int, String, String, String)}. + *

+ * This API does not modify the time stamp or other data about the operation. + * * @param op The operation to check. One of the OP_* constants. * @param uid The user id of the application attempting to perform the operation. * @param packageName The name of the application attempting to perform the operation. @@ -9446,29 +9549,11 @@ public class AppOpsManager { */ @UnsupportedAppUsage public int checkOp(int op, int uid, String packageName) { - try { - int mode; - if (isAppOpModeCachingEnabled(op)) { - mode = sAppOpModeCache.query( - new AppOpModeQuery(op, uid, packageName, Context.DEVICE_ID_DEFAULT, null, - "checkOp")); - if (mode == MODE_FOREGROUND) { - // We only cache raw mode. If the mode is FOREGROUND, we need another binder - // call to fetch translated value based on the process state. - mode = mService.checkOperationForDevice(op, uid, packageName, - Context.DEVICE_ID_DEFAULT); - } - } else { - mode = mService.checkOperationForDevice(op, uid, packageName, - Context.DEVICE_ID_DEFAULT); - } - if (mode == MODE_ERRORED) { - throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); - } - return mode; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + int mode = checkOpNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT); + if (mode == MODE_ERRORED) { + throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } + return mode; } /** @@ -9481,7 +9566,7 @@ public class AppOpsManager { */ public int checkOpNoThrow(int op, AttributionSource attributionSource) { return checkOpNoThrow(op, attributionSource.getUid(), attributionSource.getPackageName(), - attributionSource.getDeviceId()); + attributionSource.getAttributionTag(), attributionSource.getDeviceId()); } /** @@ -9494,23 +9579,26 @@ public class AppOpsManager { */ @UnsupportedAppUsage public int checkOpNoThrow(int op, int uid, String packageName) { - return checkOpNoThrow(op, uid, packageName, Context.DEVICE_ID_DEFAULT); + return checkOpNoThrow(op, uid, packageName, null, Context.DEVICE_ID_DEFAULT); } - private int checkOpNoThrow(int op, int uid, String packageName, int virtualDeviceId) { + private int checkOpNoThrow(int op, int uid, String packageName, @Nullable String attributionTag, + int virtualDeviceId) { try { int mode; if (isAppOpModeCachingEnabled(op)) { mode = sAppOpModeCache.query( - new AppOpModeQuery(op, uid, packageName, virtualDeviceId, null, + new AppOpModeQuery(op, uid, packageName, virtualDeviceId, attributionTag, "checkOpNoThrow")); if (mode == MODE_FOREGROUND) { // We only cache raw mode. If the mode is FOREGROUND, we need another binder // call to fetch translated value based on the process state. - mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); + mode = mService.checkOperationForDevice(op, uid, packageName, attributionTag, + virtualDeviceId); } } else { - mode = mService.checkOperationForDevice(op, uid, packageName, virtualDeviceId); + mode = mService.checkOperationForDevice(op, uid, packageName, attributionTag, + virtualDeviceId); } return mode; } catch (RemoteException e) { diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig index 0a35fe399531..b5a822b715d5 100644 --- a/core/java/android/permission/flags.aconfig +++ b/core/java/android/permission/flags.aconfig @@ -383,7 +383,7 @@ flag { bug: "363318732" } -flag{ +flag { name: "note_op_batching_enabled" is_fixed_read_only: true is_exported: true @@ -409,3 +409,12 @@ flag { description: "This flag is used to short circuit the request for permananently denied permissions" bug: "378923900" } + +flag { + name: "check_op_overload_api_enabled" + is_exported: true + is_fixed_read_only: true + namespace: "permissions" + description: "Add new checkOp APIs that accept attributionTag" + bug: "240617242" +} diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 3ec70649294b..2cfc680a3fe8 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -152,7 +152,7 @@ interface IAppOpsService { in AttributionSourceState attributionSourceStateState, boolean skipProxyOperation); int checkOperationRawForDevice(int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId); - int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId); + int checkOperationForDevice(int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId); SyncNotedAppOp noteOperationForDevice(int code, int uid, String packageName, @nullable String attributionTag, int virtualDeviceId, boolean shouldCollectAsyncNotedOp, String message, boolean shouldCollectMessage); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 5e74d67905a6..06c586f5e9c2 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2876,7 +2876,8 @@ public class AppOpsService extends IAppOpsService.Stub { } @Override - public int checkOperationForDevice(int code, int uid, String packageName, int virtualDeviceId) { + public int checkOperationForDevice(int code, int uid, String packageName, + @Nullable String attributionTag, int virtualDeviceId) { if (Binder.getCallingPid() != Process.myPid() && Flags.appopAccessTrackingLoggingEnabled()) { FrameworkStatsLog.write( @@ -2884,7 +2885,7 @@ public class AppOpsService extends IAppOpsService.Stub { APP_OP_NOTE_OP_OR_CHECK_OP_BINDER_API_CALLED__BINDER_API__CHECK_OPERATION, false); } - return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, null, + return mCheckOpsDelegateDispatcher.checkOperation(code, uid, packageName, attributionTag, virtualDeviceId, false /*raw*/); } -- GitLab From 77e9e405527979bed9008308cc0ef3cd03b5f706 Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Fri, 15 Nov 2024 19:25:56 +0000 Subject: [PATCH 076/656] Add feature flag for supporting minor version in Resources Bug: 373535266 Change-Id: Ie0bc24725bb151385406f23482c175dc91eded13 Test: Presubmit only Flag: EXEMPT flag creation --- core/java/android/content/res/flags.aconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/java/android/content/res/flags.aconfig b/core/java/android/content/res/flags.aconfig index f23c193e2da0..6fc7d90a8237 100644 --- a/core/java/android/content/res/flags.aconfig +++ b/core/java/android/content/res/flags.aconfig @@ -105,3 +105,12 @@ flag { # This flag is used to control aapt2 behavior. is_fixed_read_only: true } + +flag { + name: "resources_minor_version_support" + is_exported: true + namespace: "resource_manager" + description: "Feature flag for supporting minor version in Resources" + bug: "373535266" + is_fixed_read_only: true +} -- GitLab From dfaaa276d65d87adb3c16c3392ec5051cc174f1a Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Thu, 14 Nov 2024 16:32:02 -0500 Subject: [PATCH 077/656] Fix bubble bar expanded view flicker on unfold Track the task view visibility state in the expanded view and wait for it to be visible with a surface created or recreated before starting the expansion animation. Previously we removed the task view from its previous parent and immediately added it to the new expanded view and made it visible. This caused the task view to flicker and the expanded view background to be visible while the surface was destroyed and recreated. Flag: com.android.wm.shell.enable_bubble_bar Fixes: 374640759 Test: manual - have a floating bubble - expand it and unfold - observe expanded view animates correctly (with a delay) - add a new bubble to the bubble bar - expand it and observe animation - switch bubbles and observe anination - fold and add a bubble without expanding it - unfold, expand and observe animation Change-Id: I128eddd5df9ef3b722dc449e214f0961e6ac96cb --- .../bubbles/bar/BubbleBarExpandedViewTest.kt | 97 ++++++++++++++++++- .../bubbles/bar/BubbleBarLayerViewTest.kt | 2 + .../wm/shell/bubbles/BubbleTaskView.kt | 13 +++ .../bubbles/bar/BubbleBarAnimationHelper.java | 46 ++++----- .../bubbles/bar/BubbleBarExpandedView.java | 88 +++++++++++++++-- 5 files changed, 211 insertions(+), 35 deletions(-) diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt index 0d742cc6e382..9a17693dd75e 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles.bar import android.app.ActivityManager +import android.content.ComponentName import android.content.Context import android.content.pm.ShortcutInfo import android.graphics.Insets @@ -24,6 +25,7 @@ import android.graphics.Rect import android.view.LayoutInflater import android.view.View import android.view.WindowManager +import android.widget.FrameLayout import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -110,9 +112,9 @@ class BubbleBarExpandedViewTest { regionSamplingProvider = TestRegionSamplingProvider() - bubbleExpandedView = (inflater.inflate( + bubbleExpandedView = inflater.inflate( R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ - ) as BubbleBarExpandedView) + ) as BubbleBarExpandedView bubbleExpandedView.initialize( expandedViewManager, positioner, @@ -124,11 +126,11 @@ class BubbleBarExpandedViewTest { regionSamplingProvider, ) - getInstrumentation().runOnMainSync(Runnable { + getInstrumentation().runOnMainSync { bubbleExpandedView.onAttachedToWindow() // Helper should be created once attached to window testableRegionSamplingHelper = regionSamplingProvider!!.helper - }) + } bubble = Bubble( "key", @@ -254,6 +256,93 @@ class BubbleBarExpandedViewTest { assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble) } + @Test + fun animateExpansion_waitsUntilTaskCreated() { + var animated = false + bubbleExpandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + bubbleExpandedView.onTaskCreated() + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndVisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be removed from its parent + assertThat(taskView.taskView.parent).isNull() + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send an invisible signal to simulate the surface getting destroyed + expandedView.onContentVisibilityChanged(false) + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + assertThat(animated).isTrue() + } + + @Test + fun animateExpansion_taskViewAttachedAndInvisible() { + val inflater = LayoutInflater.from(context) + val expandedView = inflater.inflate( + R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */ + ) as BubbleBarExpandedView + val taskView = FakeBubbleTaskViewFactory().create() + val taskViewParent = FrameLayout(context) + taskViewParent.addView(taskView.taskView) + taskView.listener.onTaskCreated(666, ComponentName(context, "BubbleBarExpandedViewTest")) + assertThat(taskView.isVisible).isTrue() + taskView.listener.onTaskVisibilityChanged(666, false) + assertThat(taskView.isVisible).isFalse() + + expandedView.initialize( + expandedViewManager, + positioner, + BubbleLogger(uiEventLoggerFake), + false /* isOverflow */, + taskView, + mainExecutor, + bgExecutor, + regionSamplingProvider, + ) + + // the task view should be added to the expanded view + assertThat(taskView.taskView.parent).isEqualTo(expandedView) + + var animated = false + expandedView.animateExpansionWhenTaskViewVisible { animated = true } + assertThat(animated).isFalse() + + // send a visible signal to simulate a new surface getting created + expandedView.onContentVisibilityChanged(true) + + assertThat(animated).isTrue() + } + private fun BubbleBarExpandedView.menuView(): BubbleBarMenuView { return findViewByPredicate { it is BubbleBarMenuView } } diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt index 00d9a931cebe..6fca79876f12 100644 --- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt @@ -253,6 +253,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() @@ -276,6 +277,7 @@ class BubbleBarLayerViewTest { getInstrumentation().runOnMainSync { bubbleBarLayerView.showExpandedView(bubble) + bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true) } waitForExpandedViewAnimation() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt index 68fc0c99abee..a517a2d550b9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskView.kt @@ -42,6 +42,16 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { var componentName: ComponentName? = null private set + /** + * Whether the task view is visible and has a surface. Note that this does not check the alpha + * value of the task view. + * + * When this is `true` it is safe to start showing the task view. Otherwise if this is `false` + * callers should wait for it to be visible which will be indicated either by a call to + * [TaskView.Listener.onTaskCreated] or [TaskView.Listener.onTaskVisibilityChanged]. */ + var isVisible = false + private set + /** [TaskView.Listener] for users of this class. */ var delegateListener: TaskView.Listener? = null @@ -61,9 +71,12 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) { this@BubbleTaskView.taskId = taskId isCreated = true componentName = name + // when the task is created it is visible + isVisible = true } override fun onTaskVisibilityChanged(taskId: Int, visible: Boolean) { + this@BubbleTaskView.isVisible = visible delegateListener?.onTaskVisibilityChanged(taskId, visible) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index 74c3748dccaf..a313bd004a51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -161,6 +161,7 @@ public class BubbleBarAnimationHelper { updateExpandedView(); bbev.setAnimating(true); + bbev.setSurfaceZOrderedOnTop(true); bbev.setContentVisibility(false); bbev.setAlpha(0f); bbev.setTaskViewAlpha(0f); @@ -171,28 +172,29 @@ public class BubbleBarAnimationHelper { bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - mExpandedViewAlphaAnimator.start(); - - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); - PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) - .spring(AnimatableScaleMatrix.SCALE_X, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .spring(AnimatableScaleMatrix.SCALE_Y, - AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), - mScaleInSpringConfig) - .addUpdateListener((target, values) -> { - bbev.setAnimationMatrix(mExpandedViewContainerMatrix); - }) - .withEndActions(() -> { - bbev.setAnimationMatrix(null); - updateExpandedView(); - bbev.setSurfaceZOrderedOnTop(false); - if (afterAnimation != null) { - afterAnimation.run(); - } - }) - .start(); + bbev.animateExpansionWhenTaskViewVisible(() -> { + mExpandedViewAlphaAnimator.start(); + + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); + PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) + .spring(AnimatableScaleMatrix.SCALE_X, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .spring(AnimatableScaleMatrix.SCALE_Y, + AnimatableScaleMatrix.getAnimatableValueForScaleFactor(1f), + mScaleInSpringConfig) + .addUpdateListener((target, values) -> { + bbev.setAnimationMatrix(mExpandedViewContainerMatrix); + }) + .withEndActions(() -> { + bbev.setAnimationMatrix(null); + updateExpandedView(); + if (afterAnimation != null) { + afterAnimation.run(); + } + }) + .start(); + }); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index 272dfecb0bf9..e1dabdcc7f9c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -131,6 +131,11 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView /** Current corner radius */ private float mCurrentCornerRadius = 0f; + /** A runnable to start the expansion animation as soon as the task view is made visible. */ + @Nullable + private Runnable mAnimateExpansion = null; + private TaskViewVisibilityState mVisibilityState = TaskViewVisibilityState.INVISIBLE; + /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha @@ -140,6 +145,18 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private boolean mIsAnimating; private boolean mIsDragging; + /** An enum value that tracks the visibility state of the task view */ + private enum TaskViewVisibilityState { + /** The task view is going away, and we're waiting for the surface to be destroyed. */ + PENDING_INVISIBLE, + /** The task view is invisible and does not have a surface. */ + INVISIBLE, + /** The task view is in the process of being added to a surface. */ + PENDING_VISIBLE, + /** The task view is visible and has a surface. */ + VISIBLE + } + public BubbleBarExpandedView(Context context) { this(context, null); } @@ -206,16 +223,27 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, expandedViewManager, /* listener= */ this, bubbleTaskView, /* viewParent= */ this); + + // if the task view is already attached to a parent we need to remove it if (mTaskView.getParent() != null) { + // it's possible that the task view is visible, e.g. if we're unfolding, in which + // case removing it will trigger a visibility change. we have to wait for that + // signal before we can add it to this expanded view, otherwise the signal will be + // incorrect because the task view will have a surface. + // if the task view is not visible, then it has no surface and removing it will not + // trigger any visibility change signals. + if (bubbleTaskView.isVisible()) { + mVisibilityState = TaskViewVisibilityState.PENDING_INVISIBLE; + } ((ViewGroup) mTaskView.getParent()).removeView(mTaskView); } - FrameLayout.LayoutParams lp = - new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); - addView(mTaskView, lp); - mTaskView.setEnableSurfaceClipping(true); - mTaskView.setCornerRadius(mCurrentCornerRadius); - mTaskView.setVisibility(VISIBLE); - mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + + // if we're invisible it's safe to setup the task view and then await on the visibility + // signal. + if (mVisibilityState == TaskViewVisibilityState.INVISIBLE) { + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + } // Handle view needs to draw on top of task view. bringChildToFront(mHandleView); @@ -269,6 +297,16 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView }); } + private void setupTaskView() { + FrameLayout.LayoutParams lp = + new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT); + addView(mTaskView, lp); + mTaskView.setEnableSurfaceClipping(true); + mTaskView.setCornerRadius(mCurrentCornerRadius); + mTaskView.setVisibility(VISIBLE); + mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0)); + } + public BubbleBarHandleView getHandleView() { return mHandleView; } @@ -326,15 +364,28 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { - setContentVisibility(true); + if (mTaskView != null) { + mTaskView.setAlpha(0); + } if (mListener != null) { mListener.onTaskCreated(); } + // when the task is created we're visible + onTaskViewVisible(); } @Override public void onContentVisibilityChanged(boolean visible) { - setContentVisibility(visible); + if (mVisibilityState == TaskViewVisibilityState.PENDING_INVISIBLE && !visible) { + // the surface is now destroyed. set up the task view and wait for the visibility + // signal. + mVisibilityState = TaskViewVisibilityState.PENDING_VISIBLE; + setupTaskView(); + return; + } + if (visible) { + onTaskViewVisible(); + } } @Override @@ -350,6 +401,25 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView mListener.onBackPressed(); } + void animateExpansionWhenTaskViewVisible(Runnable animateExpansion) { + if (mVisibilityState == TaskViewVisibilityState.VISIBLE) { + animateExpansion.run(); + } else { + mAnimateExpansion = animateExpansion; + } + } + + private void onTaskViewVisible() { + // if we're waiting to be visible, start the expansion animation if it's pending. + if (mVisibilityState == TaskViewVisibilityState.PENDING_VISIBLE) { + mVisibilityState = TaskViewVisibilityState.VISIBLE; + if (mAnimateExpansion != null) { + mAnimateExpansion.run(); + mAnimateExpansion = null; + } + } + } + /** * Set whether this view is currently being dragged. * -- GitLab From 5081110e7c6a5b17dc3692e8e8cbee517025cb5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A1n=20Kozynski?= Date: Fri, 15 Nov 2024 15:27:59 -0500 Subject: [PATCH 078/656] Properly dispose of JavaAdapter collector This allows to use a mock view in the test that will is not attached/dettached and prevent leaks. Test: atest com.android.systemui.qs Fixes: 378522890 Flag: com.android.systemui.scene_container Change-Id: Id1716a912a76bc9161d65faf9d0161efccf19b3c --- .../qs/QSPanelControllerBaseTest.java | 12 ++--- .../systemui/qs/QSPanelControllerTest.kt | 50 ++++++++++--------- .../systemui/qs/QuickQSPanelControllerTest.kt | 7 +-- .../systemui/qs/QSPanelControllerBase.java | 8 ++- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java index 83f95eaf4cda..e9633f49f76d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -21,9 +21,6 @@ import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.pa import static com.google.common.truth.Truth.assertThat; -import static kotlinx.coroutines.flow.FlowKt.asStateFlow; -import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; - import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -39,6 +36,9 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.asStateFlow; +import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow; + import android.content.res.Configuration; import android.content.res.Resources; import android.platform.test.annotations.DisableFlags; @@ -70,9 +70,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.util.animation.DisappearParameters; -import kotlinx.coroutines.flow.MutableStateFlow; -import kotlinx.coroutines.flow.StateFlow; - import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -88,6 +85,8 @@ import java.util.List; import javax.inject.Provider; +import kotlinx.coroutines.flow.MutableStateFlow; +import kotlinx.coroutines.flow.StateFlow; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -232,6 +231,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase { @After public void tearDown() { + mController.destroy(); disallowTestableLooperAsMainThread(); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt index 02c5b5ad214c..96f6a622e2f3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QSPanelControllerTest.kt @@ -1,10 +1,10 @@ package com.android.systemui.qs import android.content.res.Configuration -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest import android.testing.TestableResources import android.view.ContextThemeWrapper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase @@ -37,8 +37,8 @@ import org.mockito.Mockito.any import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @@ -81,37 +81,39 @@ class QSPanelControllerTest : SysuiTestCase() { setShouldUseSplitShade(false) whenever(qsPanel.resources).thenReturn(testableResources.resources) whenever(qsPanel.context) - .thenReturn( ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) + .thenReturn(ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)) whenever(qsPanel.getOrCreateTileLayout()).thenReturn(pagedTileLayout) whenever(statusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false) whenever(qsPanel.setListening(anyBoolean())).then { whenever(qsPanel.isListening).thenReturn(it.getArgument(0)) } - controller = QSPanelController( - qsPanel, - tunerService, - qsHost, - qsCustomizerController, - /* usingMediaPlayer= */ usingMediaPlayer, - mediaHost, - qsTileRevealControllerFactory, - dumpManager, - metricsLogger, - uiEventLogger, - qsLogger, - brightnessControllerFactory, - brightnessSliderFactory, - falsingManager, - statusBarKeyguardViewManager, - ResourcesSplitShadeStateController(), - longPressEffectProvider, - mediaCarouselInteractor, - ) + controller = + QSPanelController( + qsPanel, + tunerService, + qsHost, + qsCustomizerController, + /* usingMediaPlayer= */ usingMediaPlayer, + mediaHost, + qsTileRevealControllerFactory, + dumpManager, + metricsLogger, + uiEventLogger, + qsLogger, + brightnessControllerFactory, + brightnessSliderFactory, + falsingManager, + statusBarKeyguardViewManager, + ResourcesSplitShadeStateController(), + longPressEffectProvider, + mediaCarouselInteractor, + ) } @After fun tearDown() { + controller.destroy() reset(mediaHost) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt index 369bb228494c..7880aceb53be 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController import com.android.systemui.util.leak.RotationUtils +import javax.inject.Provider import org.junit.After import org.junit.Before import org.junit.Test @@ -45,9 +46,8 @@ import org.mockito.Mockito.any import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import javax.inject.Provider import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidJUnit4::class) @@ -108,6 +108,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { @After fun tearDown() { controller.onViewDetached() + controller.destroy() } @Test @@ -184,7 +185,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() { dumpManager, ResourcesSplitShadeStateController(), longPressEffectProvider, - mediaCarouselInteractor + mediaCarouselInteractor, ) { private var rotation = RotationUtils.ROTATION_NONE diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 85bcc25e140c..afb852ae824c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -50,6 +50,7 @@ import com.android.systemui.util.kotlin.JavaAdapterKt; import kotlin.Unit; import kotlin.jvm.functions.Function1; +import kotlinx.coroutines.DisposableHandle; import kotlinx.coroutines.flow.StateFlow; import java.io.PrintWriter; @@ -107,6 +108,8 @@ public abstract class QSPanelControllerBase extends ViewContr setLayoutForMediaInScene(); }; + private DisposableHandle mJavaAdapterDisposableHandle; + private boolean mLastListening; @VisibleForTesting @@ -221,6 +224,9 @@ public abstract class QSPanelControllerBase extends ViewContr mView.removeTile(record); } mRecords.clear(); + if (mJavaAdapterDisposableHandle != null) { + mJavaAdapterDisposableHandle.dispose(); + } } @Override @@ -255,7 +261,7 @@ public abstract class QSPanelControllerBase extends ViewContr } private void registerForMediaInteractorChanges() { - JavaAdapterKt.collectFlow( + mJavaAdapterDisposableHandle = JavaAdapterKt.collectFlow( mView, getMediaVisibleFlow(), mMediaOrRecommendationVisibleConsumer -- GitLab From 31986fe98dfc9219f3f65cf3db10f5d7d04f6e0c Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Fri, 15 Nov 2024 11:31:04 -0800 Subject: [PATCH 079/656] [flexiglass] Adds support for "lock now" from KeyguardService Bug: 378708150 Test: manually triggered the code path using "adb shell cmd accessibility call-system-action 8" which triggers the call to KeyguardService.doKeyguardTimeout and verified that it locks immediately regardless of timeout Test: added unit test case Flag: com.android.systemui.scene_container Change-Id: I782370024af222b24801fd23b03c89ba418f6a25 --- .../DeviceUnlockedInteractorTest.kt | 14 +++++++++++++ .../interactor/DeviceEntryInteractor.kt | 21 ++++++++++++------- .../interactor/DeviceUnlockedInteractor.kt | 10 +++++++++ .../systemui/keyguard/KeyguardService.java | 4 +++- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt index f0d79bb83652..772025b2c635 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt @@ -568,6 +568,20 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { assertThat(isUnlocked).isFalse() } + @Test + fun lockNow() = + testScope.runTest { + setLockAfterScreenTimeout(5000) + val isUnlocked by collectLastValue(underTest.deviceUnlockStatus.map { it.isUnlocked }) + unlockDevice() + assertThat(isUnlocked).isTrue() + + underTest.lockNow() + runCurrent() + + assertThat(isUnlocked).isFalse() + } + private fun TestScope.unlockDevice() { val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 1a5654144b65..400d09742d13 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.deviceentry.domain.interactor +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.policy.IKeyguardDismissCallback import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel @@ -43,7 +44,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import com.android.app.tracing.coroutines.launchTraced as launch /** * Hosts application business logic related to device entry. @@ -173,6 +173,14 @@ constructor( ) } + /** + * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically + * dismissed once the authentication challenge is completed. For example, completing a biometric + * authentication challenge via face unlock or fingerprint sensor can automatically bypass the + * lockscreen. + */ + val isBypassEnabled: StateFlow = repository.isBypassEnabled + /** * Attempt to enter the device and dismiss the lockscreen. If authentication is required to * unlock the device it will transition to bouncer. @@ -238,11 +246,8 @@ constructor( isLockscreenEnabled() } - /** - * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically - * dismissed once the authentication challenge is completed. For example, completing a biometric - * authentication challenge via face unlock or fingerprint sensor can automatically bypass the - * lockscreen. - */ - val isBypassEnabled: StateFlow = repository.isBypassEnabled + /** Locks the device instantly. */ + fun lockNow() { + deviceUnlockedInteractor.lockNow() + } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt index b74ca035a229..6fead6e8f9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt @@ -42,6 +42,7 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow @@ -56,6 +57,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch @OptIn(ExperimentalCoroutinesApi::class) @@ -177,6 +179,8 @@ constructor( val deviceUnlockStatus: StateFlow = repository.deviceUnlockStatus.asStateFlow() + private val lockNowRequests = Channel() + override suspend fun onActivated(): Nothing { authenticationInteractor.authenticationMethod.collectLatest { authMethod -> if (!authMethod.isSecure) { @@ -195,6 +199,11 @@ constructor( awaitCancellation() } + /** Locks the device instantly. */ + fun lockNow() { + lockNowRequests.trySend(Unit) + } + private suspend fun handleLockAndUnlockEvents() { try { Log.d(TAG, "started watching for lock and unlock events") @@ -255,6 +264,7 @@ constructor( emptyFlow() } }, + lockNowRequests.receiveAsFlow().map { LockImmediately("lockNow") }, ) .collectLatest(::onLockEvent) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index 32c2bc7c8620..7097c1d2fc4e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -662,7 +662,9 @@ public class KeyguardService extends Service { trace("doKeyguardTimeout"); checkPermission(); - if (KeyguardWmStateRefactor.isEnabled()) { + if (SceneContainerFlag.isEnabled()) { + mDeviceEntryInteractorLazy.get().lockNow(); + } else if (KeyguardWmStateRefactor.isEnabled()) { mKeyguardLockWhileAwakeInteractor.onKeyguardServiceDoKeyguardTimeout(options); } -- GitLab From 33a31fb228009ce1e1ac0c1127c44d488ec97927 Mon Sep 17 00:00:00 2001 From: Alisher Alikhodjaev Date: Fri, 15 Nov 2024 20:36:37 +0000 Subject: [PATCH 080/656] Add Technology Type info for the active secure element list Bug: 364120337 Test: atest -c CtsNfcTestCases Change-Id: I52fcddd1e64a45300a8456427ec716ac8091e1f6 --- nfc/api/system-current.txt | 2 +- nfc/java/android/nfc/INfcAdapter.aidl | 2 +- nfc/java/android/nfc/NfcOemExtension.java | 55 +++++++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 72447b1033d3..25d9d3547024 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -57,7 +57,7 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int forceRoutingTableCommit(); - method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.List getActiveNfceeList(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull public java.util.Map getActiveNfceeList(); method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.nfc.RoutingStatus getRoutingStatus(); method @FlaggedApi("android.nfc.nfc_oem_extension") @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.List getRoutingTable(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean hasUserEnabledNfc(); diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index a08b55fe86b8..e9a3c669cd5b 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -114,7 +114,7 @@ interface INfcAdapter void clearPreference(); void setScreenState(); void checkFirmware(); - List fetchActiveNfceeList(); + Map fetchActiveNfceeList(); void triggerInitialization(); boolean getSettingStatus(); boolean isTagPresent(); diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 326ca6449c53..f912cdcf13aa 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -39,6 +39,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; import android.os.ResultReceiver; +import android.se.omapi.Reader; import android.util.Log; import java.lang.annotation.Retention; @@ -147,6 +148,48 @@ public final class NfcOemExtension { @Retention(RetentionPolicy.SOURCE) public @interface ControllerMode{} + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_NONE = 0; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_A = 1; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_B = 1 << 1; + + /** + * Technology Type for {@link #getActiveNfceeList()}. + * @hide + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + public static final int NFCEE_TECH_F = 1 << 2; + + /** + * Nfc technology flags for {@link #getActiveNfceeList()}. + * + * @hide + */ + @IntDef(flag = true, value = { + NFCEE_TECH_NONE, + NFCEE_TECH_A, + NFCEE_TECH_B, + NFCEE_TECH_F, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NfceeTechnology {} + /** * Event that Host Card Emulation is activated. */ @@ -571,14 +614,18 @@ public final class NfcOemExtension { /** * Get the Active NFCEE (NFC Execution Environment) List * - * @return List of activated secure elements on success - * which can contain "eSE" and "UICC", otherwise empty list. + * @see Reader#getName() for the list of possible NFCEE names. + * + * @return Map< String, @NfceeTechnology Integer > + * A HashMap where keys are activated secure elements and + * the values are bitmap of technologies supported by each secure element + * on success keys can contain "eSE" and "UICC", otherwise empty map. */ @NonNull @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) - public List getActiveNfceeList() { + public Map getActiveNfceeList() { return NfcAdapter.callServiceReturn(() -> - NfcAdapter.sService.fetchActiveNfceeList(), new ArrayList()); + NfcAdapter.sService.fetchActiveNfceeList(), new HashMap()); } /** -- GitLab From acba22d717114115833575b7fd98f13562fc8a48 Mon Sep 17 00:00:00 2001 From: Justin Lannin Date: Fri, 1 Nov 2024 20:15:52 +0000 Subject: [PATCH 081/656] ForegroundService: Include HR, SkinTemp, Spo2 permissions for health fgs. We are deprecating the BODY_SENSORS permission in favor of READ_HEART_RATE, READ_SKIN_TEMPERATURE, and READ_OXYGEN_SATURATION. As part of this we want to ensure that apps using any of those permissions are able to start a foreground service for the "health" type. Based on feedback from the API council, we are marking the API as a @FlaggedApi which should ensure our changes aren't reflected until the flag is flipped. Flag: android.permission.flags.replace_body_sensor_permission_enabled Test: Built Wear emulator, atest BootCompletedFgsStartTest ActivityManagerForegroundServiceTypeTest Bug: 379178288 Bug: 379178636 Bug: 375252723 Change-Id: I304731995f5f2d7b5e85dfd267fbca5ff4ab75fe --- core/api/current.txt | 2 +- .../app/ForegroundServiceTypePolicy.java | 45 ++++++++++++------- core/java/android/content/pm/ServiceInfo.java | 10 ++++- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index a03f8c5c167f..7eba473612c0 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -13799,7 +13799,7 @@ package android.content.pm { field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CAMERA}, anyOf={android.Manifest.permission.CAMERA}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CAMERA = 64; // 0x40 field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE}, anyOf={android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.CHANGE_NETWORK_STATE, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, android.Manifest.permission.NFC, android.Manifest.permission.TRANSMIT_IR, android.Manifest.permission.UWB_RANGING, android.Manifest.permission.RANGING}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE = 16; // 0x10 field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_DATA_SYNC, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; // 0x1 - field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.BODY_SENSORS, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100 + field @FlaggedApi("android.permission.flags.replace_body_sensor_permission_enabled") @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_HEALTH}, anyOf={android.Manifest.permission.ACTIVITY_RECOGNITION, android.Manifest.permission.HIGH_SAMPLING_RATE_SENSORS, android.health.connect.HealthPermissions.READ_HEART_RATE, android.health.connect.HealthPermissions.READ_SKIN_TEMPERATURE, android.health.connect.HealthPermissions.READ_OXYGEN_SATURATION}) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 256; // 0x100 field @RequiresPermission(allOf={android.Manifest.permission.FOREGROUND_SERVICE_LOCATION}, anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_LOCATION = 8; // 0x8 field public static final int FOREGROUND_SERVICE_TYPE_MANIFEST = -1; // 0xffffffff field @RequiresPermission(value=android.Manifest.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK, conditional=true) public static final int FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK = 2; // 0x2 diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index 16444dc5adde..6efc4ef55180 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -62,6 +62,7 @@ import android.content.pm.ServiceInfo.ForegroundServiceType; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; +import android.health.connect.HealthPermissions; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -484,21 +485,35 @@ public abstract class ForegroundServiceTypePolicy { */ public static final @NonNull ForegroundServiceTypePolicyInfo FGS_TYPE_POLICY_HEALTH = new ForegroundServiceTypePolicyInfo( - FOREGROUND_SERVICE_TYPE_HEALTH, - ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, - ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, - new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { - new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH) - }, true), - new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] { - new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION), - new RegularPermission(Manifest.permission.BODY_SENSORS), - new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS), - }, false), - FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */, - true /* permissionEnforcementFlagDefaultValue */, - false /* foregroundOnlyPermission */ - ); + FOREGROUND_SERVICE_TYPE_HEALTH, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + ForegroundServiceTypePolicyInfo.INVALID_CHANGE_ID, + new ForegroundServiceTypePermissions( + new ForegroundServiceTypePermission[] { + new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_HEALTH) + }, + true), + new ForegroundServiceTypePermissions(getAllowedHealthPermissions(), false), + FGS_TYPE_PERM_ENFORCEMENT_FLAG_HEALTH /* permissionEnforcementFlag */, + true /* permissionEnforcementFlagDefaultValue */, + false /* foregroundOnlyPermission */); + + /** Returns the permissions needed for the policy of the health foreground service type. */ + private static ForegroundServiceTypePermission[] getAllowedHealthPermissions() { + final ArrayList permissions = new ArrayList<>(); + permissions.add(new RegularPermission(Manifest.permission.ACTIVITY_RECOGNITION)); + permissions.add(new RegularPermission(Manifest.permission.HIGH_SAMPLING_RATE_SENSORS)); + + if (android.permission.flags.Flags.replaceBodySensorPermissionEnabled()) { + permissions.add(new RegularPermission(HealthPermissions.READ_HEART_RATE)); + permissions.add(new RegularPermission(HealthPermissions.READ_SKIN_TEMPERATURE)); + permissions.add(new RegularPermission(HealthPermissions.READ_OXYGEN_SATURATION)); + } else { + permissions.add(new RegularPermission(Manifest.permission.BODY_SENSORS)); + } + + return permissions.toArray(new ForegroundServiceTypePermission[permissions.size()]); + } /** * The policy for the {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING}. diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java index 4285b0a2b91a..8243d88e6260 100644 --- a/core/java/android/content/pm/ServiceInfo.java +++ b/core/java/android/content/pm/ServiceInfo.java @@ -20,6 +20,7 @@ import android.Manifest; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.RequiresPermission; +import android.health.connect.HealthPermissions; import android.os.Parcel; import android.os.Parcelable; import android.util.Printer; @@ -361,8 +362,10 @@ public class ServiceInfo extends ComponentInfo * {@link android.Manifest.permission#FOREGROUND_SERVICE_HEALTH} and one of the following * permissions: * {@link android.Manifest.permission#ACTIVITY_RECOGNITION}, - * {@link android.Manifest.permission#BODY_SENSORS}, * {@link android.Manifest.permission#HIGH_SAMPLING_RATE_SENSORS}. + * {@link android.health.connect.HealthPermissions#READ_HEART_RATE}, + * {@link android.health.connect.HealthPermissions#READ_SKIN_TEMPERATURE}, + * {@link android.health.connect.HealthPermissions#READ_OXYGEN_SATURATION}, */ @RequiresPermission( allOf = { @@ -370,10 +373,13 @@ public class ServiceInfo extends ComponentInfo }, anyOf = { Manifest.permission.ACTIVITY_RECOGNITION, - Manifest.permission.BODY_SENSORS, Manifest.permission.HIGH_SAMPLING_RATE_SENSORS, + HealthPermissions.READ_HEART_RATE, + HealthPermissions.READ_SKIN_TEMPERATURE, + HealthPermissions.READ_OXYGEN_SATURATION, } ) + @FlaggedApi(android.permission.flags.Flags.FLAG_REPLACE_BODY_SENSOR_PERMISSION_ENABLED) public static final int FOREGROUND_SERVICE_TYPE_HEALTH = 1 << 8; /** -- GitLab From a2bbe84ec6476b8de4525ed76769691f3fdf08c0 Mon Sep 17 00:00:00 2001 From: Harshit Mahajan Date: Thu, 14 Nov 2024 00:46:53 +0000 Subject: [PATCH 082/656] Expose important thresholds for mitigation impacts 1. LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT: no mitigation 2. MEDIUM_USER_IMPACT_THRESHOLD: user starts noticing mitigations 3. HIGH_USER_IMPACT_THRESHOLD: Severe user impact mitigations Bug: 377635591 Test: NA Flag: android.crashrecovery.flags.enable_crashrecovery Change-Id: I6756ac919ba07ae1c080131b57f5cd2e84d3611d --- .../com/android/server/PackageWatchdog.java | 42 +++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index fbf51fd7cca7..0cccfc26a32f 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -726,6 +726,25 @@ public class PackageWatchdog { return mPackagesExemptFromImpactLevelThreshold; } + /** + * The minimum value that can be returned by any observer. + * It represents that no mitigations were available. + */ + public static final int LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + + /** + * The mitigation impact beyond which the user will start noticing the mitigations. + */ + public static final int MEDIUM_USER_IMPACT_THRESHOLD = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_20; + + /** + * The mitigation impact beyond which the user impact is severely high. + */ + public static final int HIGH_USER_IMPACT_THRESHOLD = + PackageHealthObserverImpact.USER_IMPACT_LEVEL_71; + /** * Possible severity values of the user impact of a * {@link PackageHealthObserver#onExecuteHealthCheckMitigation}. @@ -769,6 +788,11 @@ public class PackageWatchdog { /** * Called when health check fails for the {@code versionedPackage}. * + * Note: if the returned user impact is higher than + * {@link #DEFAULT_HIGH_USER_IMPACT_THRESHOLD}, then + * {@link #onExecuteHealthCheckMitigation} would be called only in severe device conditions + * like boot-loop or network failure. + * * @param versionedPackage the package that is failing. This may be null if a native * service is crashing. * @param failureReason the type of failure that is occurring. @@ -776,8 +800,8 @@ public class PackageWatchdog { * (including this time). * * - * @return any one of {@link PackageHealthObserverImpact} to express the impact - * to the user on {@link #onExecuteHealthCheckMitigation} + * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express + * the impact of mitigation on the user in {@link #onExecuteHealthCheckMitigation} */ @PackageHealthObserverImpact int onHealthCheckFailed( @Nullable VersionedPackage versionedPackage, @@ -786,9 +810,8 @@ public class PackageWatchdog { /** * This would be called after {@link #onHealthCheckFailed}. - * This is called only if current observer returned least - * {@link PackageHealthObserverImpact} mitigation for failed health - * check. + * This is called only if current observer returned least impact mitigation for failed + * health check. * * @param versionedPackage the package that is failing. This may be null if a native * service is crashing. @@ -807,6 +830,9 @@ public class PackageWatchdog { * * @param mitigationCount the number of times mitigation has been attempted for this * boot loop (including this time). + * + * @return any value greater than {@link #LEAST_PACKAGE_HEALTH_OBSERVER_IMPACT} to express + * the impact of mitigation on the user in {@link #onExecuteBootLoopMitigation} */ default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { return PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; @@ -814,11 +840,13 @@ public class PackageWatchdog { /** * This would be called after {@link #onBootLoop}. - * This is called only if current observer returned least - * {@link PackageHealthObserverImpact} mitigation for fixing boot loop + * This is called only if current observer returned least impact mitigation for fixing + * boot loop. * * @param mitigationCount the number of times mitigation has been attempted for this * boot loop (including this time). + * + * @return {@code true} if action was executed successfully, {@code false} otherwise */ default boolean onExecuteBootLoopMitigation(int mitigationCount) { return false; -- GitLab From 0cb319fed0657371a6666846cf1a60f2b39c6700 Mon Sep 17 00:00:00 2001 From: Steve Pomeroy Date: Fri, 15 Nov 2024 20:03:26 +0000 Subject: [PATCH 083/656] Add callbacks to NFC Event Listener Bug: 356447790 Test: CTS tests Flag: android.nfc.Flags.nfcEventListener Change-Id: I59d4f3b119849360f8258596dd0c5d03ae413f99 --- nfc/api/current.txt | 9 ++ nfc/java/android/nfc/INfcEventListener.aidl | 5 + .../nfc/cardemulation/CardEmulation.java | 143 ++++++++++++++++-- 3 files changed, 146 insertions(+), 11 deletions(-) diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 9a7a39f213ee..7ae2eafaf461 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -228,6 +228,10 @@ package android.nfc.cardemulation { field public static final String CATEGORY_PAYMENT = "payment"; field public static final String EXTRA_CATEGORY = "category"; field public static final String EXTRA_SERVICE_COMPONENT = "component"; + field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3; // 0x3 + field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; // 0x1 + field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; // 0x2 + field @FlaggedApi("android.nfc.nfc_event_listener") public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; // 0x0 field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DEFAULT = 3; // 0x3 field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_DH = 0; // 0x0 field @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public static final int PROTOCOL_AND_TECHNOLOGY_ROUTE_ESE = 1; // 0x1 @@ -239,8 +243,13 @@ package android.nfc.cardemulation { } @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventListener { + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidConflictOccurred(@NonNull String); + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onAidNotRouted(@NonNull String); + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onInternalErrorReported(int); + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onNfcStateChanged(int); method @FlaggedApi("android.nfc.nfc_event_listener") public default void onObserveModeStateChanged(boolean); method @FlaggedApi("android.nfc.nfc_event_listener") public default void onPreferredServiceChanged(boolean); + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onRemoteFieldChanged(boolean); } public abstract class HostApduService extends android.app.Service { diff --git a/nfc/java/android/nfc/INfcEventListener.aidl b/nfc/java/android/nfc/INfcEventListener.aidl index 5162c26ac536..774d8f875192 100644 --- a/nfc/java/android/nfc/INfcEventListener.aidl +++ b/nfc/java/android/nfc/INfcEventListener.aidl @@ -8,4 +8,9 @@ import android.nfc.ComponentNameAndUser; oneway interface INfcEventListener { void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser); void onObserveModeStateChanged(boolean isEnabled); + void onAidConflictOccurred(in String aid); + void onAidNotRouted(in String aid); + void onNfcStateChanged(in int nfcState); + void onRemoteFieldChanged(boolean isDetected); + void onInternalErrorReported(in int errorType); } \ No newline at end of file diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index 891752475824..e9ec7215e4d0 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -1144,6 +1144,40 @@ public final class CardEmulation { }; } + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public static final int NFC_INTERNAL_ERROR_UNKNOWN = 0; + + /** + * This error is reported when the NFC command watchdog restarts the NFC stack. + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public static final int NFC_INTERNAL_ERROR_NFC_CRASH_RESTART = 1; + + /** + * This error is reported when the NFC controller does not respond or there's an NCI transport + * error. + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public static final int NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR = 2; + + /** + * This error is reported when the NFC stack times out while waiting for a response to a command + * sent to the NFC hardware. + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public static final int NFC_INTERNAL_ERROR_COMMAND_TIMEOUT = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + @IntDef(prefix = "NFC_INTERNAL_ERROR_", value = { + NFC_INTERNAL_ERROR_UNKNOWN, + NFC_INTERNAL_ERROR_NFC_CRASH_RESTART, + NFC_INTERNAL_ERROR_NFC_HARDWARE_ERROR, + NFC_INTERNAL_ERROR_COMMAND_TIMEOUT, + }) + public @interface NfcInternalErrorType {} + /** Listener for preferred service state changes. */ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) public interface NfcEventListener { @@ -1166,6 +1200,57 @@ public final class CardEmulation { */ @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) default void onObserveModeStateChanged(boolean isEnabled) {} + + /** + * This method is called when an AID conflict is detected during an NFC transaction. This + * can happen when multiple services are registered for the same AID. + * + * @param aid The AID that is in conflict + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onAidConflictOccurred(@NonNull String aid) {} + + /** + * This method is called when an AID is not routed to any service during an NFC + * transaction. This can happen when no service is registered for the given AID. + * + * @param aid the AID that was not routed + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onAidNotRouted(@NonNull String aid) {} + + /** + * This method is called when the NFC state changes. + * + * @see NfcAdapter#getAdapterState() + * + * @param state The new NFC state + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onNfcStateChanged(@NfcAdapter.AdapterState int state) {} + /** + * This method is called when the NFC controller is in card emulation mode and an NFC + * reader's field is either detected or lost. + * + * @param isDetected true if an NFC reader is detected, false if it is lost + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onRemoteFieldChanged(boolean isDetected) {} + + /** + * This method is called when an internal error is reported by the NFC stack. + * + * No action is required in response to these events as the NFC stack will automatically + * attempt to recover. These errors are reported for informational purposes only. + * + * Note that these errors can be reported when performing various internal NFC operations + * (such as during device shutdown) and cannot always be explicitly correlated with NFC + * transaction failures. + * + * @param errorType The type of the internal error + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onInternalErrorReported(@NfcInternalErrorType int errorType) {} } private final ArrayMap mNfcEventListeners = new ArrayMap<>(); @@ -1185,25 +1270,61 @@ public final class CardEmulation { mContext.getPackageName(), componentNameAndUser.getComponentName() .getPackageName()); - synchronized (mNfcEventListeners) { - mNfcEventListeners.forEach( - (listener, executor) -> { - executor.execute( - () -> listener.onPreferredServiceChanged(isPreferred)); - }); - } + callListeners(listener -> listener.onPreferredServiceChanged(isPreferred)); } public void onObserveModeStateChanged(boolean isEnabled) { if (!android.nfc.Flags.nfcEventListener()) { return; } + callListeners(listener -> listener.onObserveModeStateChanged(isEnabled)); + } + + public void onAidConflictOccurred(String aid) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + callListeners(listener -> listener.onAidConflictOccurred(aid)); + } + + public void onAidNotRouted(String aid) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + callListeners(listener -> listener.onAidNotRouted(aid)); + } + + public void onNfcStateChanged(int state) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + callListeners(listener -> listener.onNfcStateChanged(state)); + } + + public void onRemoteFieldChanged(boolean isDetected) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + callListeners(listener -> listener.onRemoteFieldChanged(isDetected)); + } + + public void onInternalErrorReported(@NfcInternalErrorType int errorType) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + callListeners(listener -> listener.onInternalErrorReported(errorType)); + } + + interface ListenerCall { + void invoke(NfcEventListener listener); + } + + private void callListeners(ListenerCall listenerCall) { synchronized (mNfcEventListeners) { mNfcEventListeners.forEach( - (listener, executor) -> { - executor.execute( - () -> listener.onObserveModeStateChanged(isEnabled)); - }); + (listener, executor) -> { + executor.execute(() -> listenerCall.invoke(listener)); + }); } } }; -- GitLab From 121d051b25367340fe27a4f74cd1fac2f0d229fb Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 13 Nov 2024 08:32:00 -0800 Subject: [PATCH 084/656] Set package name via the build file, not via Config. Flag: EXEMPT host test change only Bug: 377765941 Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh -t Change-Id: Ied81b7c246a25ac376db93b2cd14b4d449359ca2 --- ...RavenwoodRuntimeEnvironmentController.java | 31 +++++++++++++++++ .../test/ravenwood/RavenwoodConfig.java | 33 ++++++------------- .../test/ravenwood/RavenwoodRule.java | 11 +++---- .../ravenwood/RavenwoodSystemProperties.java | 4 +-- .../common/RavenwoodCommonUtils.java | 17 ++++++++++ ravenwood/tests/bivalentinst/Android.bp | 7 ++++ .../RavenwoodInstrumentationTest_nonself.java | 7 ---- .../RavenwoodInstrumentationTest_self.java | 9 ----- ravenwood/tests/bivalenttest/Android.bp | 2 ++ .../bivalenttest/RavenwoodConfigTest.java | 10 +----- .../compat/RavenwoodCompatFrameworkTest.kt | 12 +------ .../RavenwoodRunnerConfigValidationTest.java | 9 +++++ .../RavenwoodRunnerTestBase.java | 3 ++ ravenwood/tests/runtime-test/Android.bp | 2 +- 14 files changed, 88 insertions(+), 69 deletions(-) diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 678a97be60a2..04cb17b7b25b 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -22,6 +22,8 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_R import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING; import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; +import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt; +import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; @@ -39,6 +41,7 @@ import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.os.Binder; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.HandlerThread; import android.os.Looper; @@ -154,6 +157,13 @@ public class RavenwoodRuntimeEnvironmentController { private static RavenwoodAwareTestRunner sRunner; private static RavenwoodSystemProperties sProps; + private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT; + private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname"; + + private static int sTargetSdkLevel; + private static String sTestPackageName; + private static String sTargetPackageName; + /** * Initialize the global environment. */ @@ -235,9 +245,22 @@ public class RavenwoodRuntimeEnvironmentController { System.setProperty("android.junit.runner", "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner"); + loadRavenwoodProperties(); + assertMockitoVersion(); } + private static void loadRavenwoodProperties() { + var props = RavenwoodSystemProperties.readProperties("ravenwood.properties"); + + sTargetSdkLevel = withDefault( + parseNullableInt(props.get("targetSdkVersionInt")), DEFAULT_TARGET_SDK_LEVEL); + sTargetPackageName = withDefault(props.get("packageName"), DEFAULT_PACKAGE_NAME); + sTestPackageName = withDefault(props.get("instPackageName"), sTargetPackageName); + + // TODO(b/377765941) Read them from the manifest too? + } + /** * Initialize the environment. */ @@ -267,6 +290,14 @@ public class RavenwoodRuntimeEnvironmentController { Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); } + config.mTargetPackageName = sTargetPackageName; + config.mTestPackageName = sTestPackageName; + config.mTargetSdkLevel = sTargetSdkLevel; + + Log.i(TAG, "TargetPackageName=" + sTargetPackageName); + Log.i(TAG, "TestPackageName=" + sTestPackageName); + Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel); + RavenwoodRuntimeState.sUid = config.mUid; RavenwoodRuntimeState.sPid = config.mPid; RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 3ed8b0a748e1..619c8e30c78e 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Instrumentation; import android.content.Context; -import android.os.Build; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -30,16 +29,12 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** - * Represents how to configure the ravenwood environment for a test class. - * - * If a ravenwood test class has a public static field with the {@link Config} annotation, - * Ravenwood will extract the config from it and initializes the environment. The type of the - * field must be of {@link RavenwoodConfig}. + * @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it. */ +@Deprecated public final class RavenwoodConfig { /** * Use this to mark a field as the configuration. @@ -66,7 +61,7 @@ public final class RavenwoodConfig { String mTestPackageName; String mTargetPackageName; - int mTargetSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; + int mTargetSdkLevel; final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties(); @@ -91,12 +86,6 @@ public final class RavenwoodConfig { return RavenwoodRule.isOnRavenwood(); } - private void setDefaults() { - if (mTargetPackageName == null) { - mTargetPackageName = mTestPackageName; - } - } - public static class Builder { private final RavenwoodConfig mConfig = new RavenwoodConfig(); @@ -120,28 +109,27 @@ public final class RavenwoodConfig { } /** - * Configure the package name of the test, which corresponds to - * {@link Instrumentation#getContext()}. + * @deprecated no longer used. Package name is set in the build file. (for now) */ + @Deprecated public Builder setPackageName(@NonNull String packageName) { - mConfig.mTestPackageName = Objects.requireNonNull(packageName); return this; } /** - * Configure the package name of the target app, which corresponds to - * {@link Instrumentation#getTargetContext()}. Defaults to {@link #setPackageName}. + * @deprecated no longer used. Package name is set in the build file. (for now) */ + @Deprecated public Builder setTargetPackageName(@NonNull String packageName) { - mConfig.mTargetPackageName = Objects.requireNonNull(packageName); return this; } + /** - * Configure the target SDK level of the test. + * @deprecated no longer used. Target SDK level is set in the build file. (for now) */ + @Deprecated public Builder setTargetSdkLevel(int sdkLevel) { - mConfig.mTargetSdkLevel = sdkLevel; return this; } @@ -205,7 +193,6 @@ public final class RavenwoodConfig { } public RavenwoodConfig build() { - mConfig.setDefaults(); return mConfig; } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index bfa3802ce583..f7acd9022300 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -36,10 +36,8 @@ import java.util.Objects; import java.util.regex.Pattern; /** - * @deprecated Use {@link RavenwoodConfig} to configure the ravenwood environment instead. - * A {@link RavenwoodRule} is no longer needed for {@link DisabledOnRavenwood}. To get the - * {@link Context} and {@link Instrumentation}, use - * {@link androidx.test.platform.app.InstrumentationRegistry} instead. + * @deprecated This class is undergoing a major change. Reach out to g/ravenwood if you need + * any featues in it. */ @Deprecated public final class RavenwoodRule implements TestRule { @@ -128,11 +126,10 @@ public final class RavenwoodRule implements TestRule { } /** - * Configure the identity of this process to be the given package name for the duration - * of the test. Has no effect on non-Ravenwood environments. + * @deprecated no longer used. */ + @Deprecated public Builder setPackageName(@NonNull String packageName) { - mBuilder.setPackageName(packageName); return this; } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index 3e4619f55c6d..9bd376a76f77 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -52,7 +52,7 @@ public class RavenwoodSystemProperties { "vendor_dlkm", }; - private static Map readProperties(String propFile) { + static Map readProperties(String propFile) { // Use an ordered map just for cleaner dump log. final Map ret = new LinkedHashMap<>(); try { @@ -60,7 +60,7 @@ public class RavenwoodSystemProperties { .map(String::trim) .filter(s -> !s.startsWith("#")) .map(s -> s.split("\\s*=\\s*", 2)) - .filter(a -> a.length == 2) + .filter(a -> a.length == 2 && a[1].length() > 0) .forEach(a -> ret.put(a[0], a[1])); } catch (IOException e) { throw new RuntimeException(e); diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java index 520f050f0655..e83f8416214e 100644 --- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java @@ -284,4 +284,21 @@ public class RavenwoodCommonUtils { th.printStackTrace(writer); return stringWriter.toString(); } + + /** Same as {@link Integer#parseInt(String)} but accepts null and returns null. */ + @Nullable + public static Integer parseNullableInt(@Nullable String value) { + if (value == null) { + return null; + } + return Integer.parseInt(value); + } + + /** + * @return {@code value} if it's non-null. Otherwise, returns {@code def}. + */ + @Nullable + public static T withDefault(@Nullable T value, @Nullable T def) { + return value != null ? value : def; + } } diff --git a/ravenwood/tests/bivalentinst/Android.bp b/ravenwood/tests/bivalentinst/Android.bp index 41e45e5a6d95..31e3bcc3634f 100644 --- a/ravenwood/tests/bivalentinst/Android.bp +++ b/ravenwood/tests/bivalentinst/Android.bp @@ -27,6 +27,9 @@ android_ravenwood_test { "junit", "truth", ], + + package_name: "com.android.ravenwood.bivalentinsttest_self_inst", + resource_apk: "RavenwoodBivalentInstTest_self_inst_device", auto_gen_config: true, } @@ -53,6 +56,10 @@ android_ravenwood_test { "truth", ], resource_apk: "RavenwoodBivalentInstTestTarget", + + package_name: "com.android.ravenwood.bivalentinst_target_app", + inst_package_name: "com.android.ravenwood.bivalentinsttest_nonself_inst", + inst_resource_apk: "RavenwoodBivalentInstTest_nonself_inst_device", auto_gen_config: true, } diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java index 77874bd28677..919aa439d4ef 100644 --- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java +++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_nonself.java @@ -19,8 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import android.app.Instrumentation; import android.content.Context; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodConfig.Config; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -40,11 +38,6 @@ public class RavenwoodInstrumentationTest_nonself { private static final String TEST_PACKAGE_NAME = "com.android.ravenwood.bivalentinsttest_nonself_inst"; - @Config - public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(TEST_PACKAGE_NAME) - .setTargetPackageName(TARGET_PACKAGE_NAME) - .build(); private static Instrumentation sInstrumentation; private static Context sTestContext; diff --git a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java index 203923bb551b..81cfa662920c 100644 --- a/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java +++ b/ravenwood/tests/bivalentinst/test/com/android/ravenwoodtest/bivalentinst/RavenwoodInstrumentationTest_self.java @@ -19,8 +19,6 @@ import static com.google.common.truth.Truth.assertThat; import android.app.Instrumentation; import android.content.Context; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodConfig.Config; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -41,13 +39,6 @@ public class RavenwoodInstrumentationTest_self { private static final String TEST_PACKAGE_NAME = "com.android.ravenwood.bivalentinsttest_self_inst"; - @Config - public static final RavenwoodConfig sConfig = new RavenwoodConfig.Builder() - .setPackageName(TEST_PACKAGE_NAME) - .setTargetPackageName(TARGET_PACKAGE_NAME) - .build(); - - private static Instrumentation sInstrumentation; private static Context sTestContext; private static Context sTargetContext; diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp index 40e6672a3c63..ac545dfb06cc 100644 --- a/ravenwood/tests/bivalenttest/Android.bp +++ b/ravenwood/tests/bivalenttest/Android.bp @@ -84,6 +84,8 @@ java_defaults { android_ravenwood_test { name: "RavenwoodBivalentTest", defaults: ["ravenwood-bivalent-defaults"], + target_sdk_version: "34", + package_name: "com.android.ravenwoodtest.bivalenttest", auto_gen_config: true, } diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java index a5a16c14600b..306c2b39c70d 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodConfigTest.java @@ -20,8 +20,6 @@ import static android.platform.test.ravenwood.RavenwoodConfig.isOnRavenwood; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; -import android.platform.test.ravenwood.RavenwoodConfig; - import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -33,13 +31,7 @@ import org.junit.runner.RunWith; */ @RunWith(AndroidJUnit4.class) public class RavenwoodConfigTest { - private static final String PACKAGE_NAME = "com.test"; - - @RavenwoodConfig.Config - public static RavenwoodConfig sConfig = - new RavenwoodConfig.Builder() - .setPackageName(PACKAGE_NAME) - .build(); + private static final String PACKAGE_NAME = "com.android.ravenwoodtest.bivalenttest"; @Test public void testConfig() { diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt index a95760db1a61..882c91c43ee9 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt @@ -16,8 +16,6 @@ package com.android.ravenwoodtest.bivalenttest.compat import android.app.compat.CompatChanges -import android.os.Build -import android.platform.test.ravenwood.RavenwoodConfig import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest import org.junit.Assert @@ -26,14 +24,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class RavenwoodCompatFrameworkTest { - companion object { - @JvmField // Expose as a raw field, not as a property. - @RavenwoodConfig.Config - val config = RavenwoodConfig.Builder() - .setTargetSdkLevel(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - .build() - } - @Test fun testEnabled() { Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1)) @@ -53,4 +43,4 @@ class RavenwoodCompatFrameworkTest { fun testEnabledAfterUForUApps() { Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4)) } -} \ No newline at end of file +} diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java index 02d10732245d..f94b98bc1fb8 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java @@ -24,6 +24,7 @@ import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -32,6 +33,10 @@ import org.junit.runner.RunWith; /** * Test for @Config field extraction and validation. + * + * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but + * some of the tests may need to be re-implemented one way or another. (e.g. the package name + * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead. */ @NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner. public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase { @@ -59,6 +64,7 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase testRunFinished: 1,0,0,0 """) // CHECKSTYLE:ON + @Ignore // Package name is no longer set via config. public static class ConfigInBaseClassTest extends ConfigInBaseClass { @Test public void test() { @@ -83,6 +89,7 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase testRunFinished: 1,0,0,0 """) // CHECKSTYLE:ON + @Ignore // Package name is no longer set via config. public static class ConfigOverridingTest extends ConfigInBaseClass { static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest"; @@ -376,6 +383,7 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase testRunFinished: 1,0,0,0 """) // CHECKSTYLE:ON + @Ignore // Package name is no longer set via config. public static class RuleInBaseClassSuccessTest extends RuleInBaseClass { @Test @@ -437,6 +445,7 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase testRunFinished: 1,1,0,0 """) // CHECKSTYLE:ON + @Ignore // Package name is no longer set via config. public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass { @Test diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java index f7a2198a9bc4..0e3d053e90b1 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java @@ -25,6 +25,8 @@ import android.util.Log; import junitparams.JUnitParamsRunner; import junitparams.Parameters; + +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.Description; import org.junit.runner.JUnitCore; @@ -103,6 +105,7 @@ public abstract class RavenwoodRunnerTestBase { var thisClass = this.getClass(); var ret = Arrays.stream(thisClass.getNestMembers()) .filter((c) -> c.getAnnotation(Expected.class) != null) + .filter((c) -> c.getAnnotation(Ignore.class) == null) .toArray(Class[]::new); assertThat(ret.length).isGreaterThan(0); diff --git a/ravenwood/tests/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp index 0c0df1f993aa..c3520031ea7d 100644 --- a/ravenwood/tests/runtime-test/Android.bp +++ b/ravenwood/tests/runtime-test/Android.bp @@ -9,7 +9,7 @@ package { android_ravenwood_test { name: "RavenwoodRuntimeTest", - + target_sdk_version: "34", libs: [ "ravenwood-helper-runtime", ], -- GitLab From 6bb38e026bed3e2ea269a96fe48ad5a1c6626916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Wed, 13 Nov 2024 17:27:02 +0000 Subject: [PATCH 085/656] Use Context/PermissionManager for NM.areNotificationsEnabled() Instead of calling into NMS to perform the same query. The advantage is that PermissionManager caches results client-side. Bug: 379323264 Test: CTS Flag: android.app.nm_binder_perf_permission_check Change-Id: I93b8491924f570cb69ef5b0972243241e66c72c1 --- core/java/android/app/NotificationManager.java | 16 +++++++++++----- core/java/android/app/notification.aconfig | 7 +++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index c49b02210dd4..c310f95814dc 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -16,6 +16,8 @@ package android.app; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.service.notification.Flags.notificationClassification; import android.annotation.CallbackExecutor; @@ -1597,11 +1599,15 @@ public class NotificationManager { * Returns whether notifications from the calling package are enabled. */ public boolean areNotificationsEnabled() { - INotificationManager service = getService(); - try { - return service.areNotificationsEnabled(mContext.getPackageName()); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (Flags.nmBinderPerfPermissionCheck()) { + return mContext.checkSelfPermission(POST_NOTIFICATIONS) == PERMISSION_GRANTED; + } else { + INotificationManager service = getService(); + try { + return service.areNotificationsEnabled(mContext.getPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 2e3d5e15e037..8b6840c1b552 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -276,6 +276,13 @@ flag { bug: "367996732" } +flag { + name: "nm_binder_perf_permission_check" + namespace: "systemui" + description: "Use PermissionManager for areNotificationsEnabled() instead of NMS" + bug: "362981561" +} + flag { name: "no_sbnholder" namespace: "systemui" -- GitLab From 257794eae37c569cf9568a043fb11bb17f11e89a Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Wed, 13 Nov 2024 22:08:19 -0800 Subject: [PATCH 086/656] Use most recent Radio HAL libraries Bug: 310710841 Test: m & boot Flag: EXEMPT depencency only Change-Id: If599fa561246b09e11bda2f6429920f18183b587 --- Android.bp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Android.bp b/Android.bp index 54cb2684068d..49a6a2b3ec65 100644 --- a/Android.bp +++ b/Android.bp @@ -103,10 +103,10 @@ filegroup { ":android.hardware.gnss-V2-java-source", ":android.hardware.graphics.common-V3-java-source", ":android.hardware.keymaster-V4-java-source", - ":android.hardware.radio-V3-java-source", - ":android.hardware.radio.data-V3-java-source", - ":android.hardware.radio.network-V3-java-source", - ":android.hardware.radio.voice-V3-java-source", + ":android.hardware.radio-V4-java-source", + ":android.hardware.radio.data-V4-java-source", + ":android.hardware.radio.network-V4-java-source", + ":android.hardware.radio.voice-V4-java-source", ":android.hardware.security.secureclock-V1-java-source", ":android.hardware.thermal-V3-java-source", ":android.hardware.tv.tuner-V3-java-source", @@ -232,13 +232,13 @@ java_library { "android.hardware.gnss-V2.1-java", "android.hardware.health-V1.0-java-constants", "android.hardware.radio-V1.6-java", - "android.hardware.radio.data-V3-java", - "android.hardware.radio.ims-V2-java", - "android.hardware.radio.messaging-V3-java", - "android.hardware.radio.modem-V3-java", - "android.hardware.radio.network-V3-java", - "android.hardware.radio.sim-V3-java", - "android.hardware.radio.voice-V3-java", + "android.hardware.radio.data-V4-java", + "android.hardware.radio.ims-V3-java", + "android.hardware.radio.messaging-V4-java", + "android.hardware.radio.modem-V4-java", + "android.hardware.radio.network-V4-java", + "android.hardware.radio.sim-V4-java", + "android.hardware.radio.voice-V4-java", "android.hardware.thermal-V1.0-java-constants", "android.hardware.thermal-V1.0-java", "android.hardware.thermal-V1.1-java", -- GitLab From 1e2f7e1d5a69c6dfab825e4530bad880984d99ad Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Fri, 15 Nov 2024 09:56:09 -0800 Subject: [PATCH 087/656] Do not mask exceptions from initialization Flag: EXEMPT host test change only Bug: 379184974 Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh Change-Id: I0918856ffab2225e530fe8b663cdccfedf03b5fb --- .../ravenwood/RavenwoodAwareTestRunner.java | 6 +++- ...RavenwoodRuntimeEnvironmentController.java | 18 ++++++++--- .../common/RavenwoodCommonUtils.java | 30 ++++++++++++++++++- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java index 869d854f7b23..9b71f8050c80 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -30,6 +30,8 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.ravenwood.common.RavenwoodCommonUtils; + import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.Runner; @@ -229,7 +231,9 @@ public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase s.evaluate(); onAfter(description, scope, order, null); } catch (Throwable t) { - if (onAfter(description, scope, order, t)) { + var shouldReportFailure = RavenwoodCommonUtils.runIgnoringException( + () -> onAfter(description, scope, order, t)); + if (shouldReportFailure == null || shouldReportFailure) { throw t; } } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 678a97be60a2..64b49101e6ba 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -256,7 +256,9 @@ public class RavenwoodRuntimeEnvironmentController { initInner(runner.mState.getConfig()); } catch (Exception th) { Log.e(TAG, "init() failed", th); - reset(); + + RavenwoodCommonUtils.runIgnoringException(()-> reset()); + SneakyThrow.sneakyThrow(th); } } @@ -349,8 +351,11 @@ public class RavenwoodRuntimeEnvironmentController { * Partially re-initialize after each test method invocation */ public static void reinit() { - var config = sRunner.mState.getConfig(); - Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); + // sRunner could be null, if there was a failure in the initialization. + if (sRunner != null) { + var config = sRunner.mState.getConfig(); + Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); + } } private static void initializeCompatIds(RavenwoodConfig config) { @@ -380,6 +385,9 @@ public class RavenwoodRuntimeEnvironmentController { /** * De-initialize. + * + * Note, we call this method when init() fails too, so this method should deal with + * any partially-initialized states. */ public static void reset() { if (RAVENWOOD_VERBOSE_LOGGING) { @@ -411,7 +419,9 @@ public class RavenwoodRuntimeEnvironmentController { config.mState.mSystemServerContext.cleanUp(); } - Looper.getMainLooper().quit(); + if (Looper.getMainLooper() != null) { + Looper.getMainLooper().quit(); + } Looper.clearMainLooperForTest(); ActivityManager.reset$ravenwood(); diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java index 520f050f0655..2bd079a9d135 100644 --- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java @@ -30,6 +30,7 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.function.Supplier; public class RavenwoodCommonUtils { private static final String TAG = "RavenwoodCommonUtils"; @@ -277,8 +278,35 @@ public class RavenwoodCommonUtils { (isStatic ? "static" : ""))); } + /** + * Run a supplier and swallow the exception, if any. + * + * It's a dangerous function. Only use it in an exception handler where we don't want to crash. + */ + @Nullable + public static T runIgnoringException(@NonNull Supplier s) { + try { + return s.get(); + } catch (Throwable th) { + log(TAG, "Warning: Exception detected! " + getStackTraceString(th)); + } + return null; + } + + /** + * Run a runnable and swallow the exception, if any. + * + * It's a dangerous function. Only use it in an exception handler where we don't want to crash. + */ + public static void runIgnoringException(@NonNull Runnable r) { + runIgnoringException(() -> { + r.run(); + return null; + }); + } + @NonNull - public static String getStackTraceString(@Nullable Throwable th) { + public static String getStackTraceString(@NonNull Throwable th) { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); th.printStackTrace(writer); -- GitLab From 91caa0ed2bed738227ba9333031b46d68e24f7b8 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Thu, 7 Nov 2024 20:42:45 +0000 Subject: [PATCH 088/656] nfc(api): Add a callback for onEeUpdated Will be used to indicate that ome applet has been installed/updated/removed in one of the NFCEE's. Bug: 357535056 Change-Id: Ie47e61ecdad3279a91f24ac976fdbf5819274fc2 Test: atest CtsNfcTestCases --- nfc/api/system-current.txt | 1 + .../android/nfc/INfcOemExtensionCallback.aidl | 1 + nfc/java/android/nfc/NfcOemExtension.java | 15 +++++++++++++++ 3 files changed, 17 insertions(+) diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 72447b1033d3..15814edcd86a 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -96,6 +96,7 @@ package android.nfc { method public void onDisableRequested(@NonNull java.util.function.Consumer); method public void onDisableStarted(); method public void onEeListenActivated(boolean); + method public void onEeUpdated(); method public void onEnableFinished(int); method public void onEnableRequested(@NonNull java.util.function.Consumer); method public void onEnableStarted(); diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl index 1a21c0bae413..e5eac0b4d6fd 100644 --- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl +++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl @@ -48,6 +48,7 @@ interface INfcOemExtensionCallback { void onRfFieldActivated(boolean isActivated); void onRfDiscoveryStarted(boolean isDiscoveryStarted); void onEeListenActivated(boolean isActivated); + void onEeUpdated(); void onGetOemAppSearchIntent(in List firstPackage, in ResultReceiver intentConsumer); void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent); void onLaunchHceAppChooserActivity(in String selectedAid, in List services, in ComponentName failedComponent, in String category); diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 326ca6449c53..9ed678fe6014 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -366,6 +366,15 @@ public final class NfcOemExtension { */ void onEeListenActivated(boolean isActivated); + /** + * Notifies that some NFCEE (NFC Execution Environment) has been updated. + * + *

This indicates that some applet has been installed/updated/removed in + * one of the NFCEE's. + *

+ */ + void onEeUpdated(); + /** * Gets the intent to find the OEM package in the OEM App market. If the consumer returns * {@code null} or a timeout occurs, the intent from the first available package will be @@ -829,6 +838,12 @@ public final class NfcOemExtension { handleVoidCallback(isActivated, cb::onEeListenActivated, ex)); } + @Override + public void onEeUpdated() throws RemoteException { + mCallbackMap.forEach((cb, ex) -> + handleVoidCallback(null, (Object input) -> cb.onEeUpdated(), ex)); + } + @Override public void onStateUpdated(int state) throws RemoteException { mCallbackMap.forEach((cb, ex) -> -- GitLab From 04bb10a56d6a2e9c8febb46edc8b9bcd23b6a255 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Wed, 13 Nov 2024 13:24:59 -0800 Subject: [PATCH 089/656] TIS: Standardize TIS Cam Extensions API Define standardized AIDL interfaces for cam extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I5f8dea3b9d84e2b1f5f8ce0c0783a404c3e6b4f6 --- .../tv/extension/cam/ICamAppInfoListener.aidl | 26 +++++++++++++ .../tv/extension/cam/ICamAppInfoService.aidl | 32 ++++++++++++++++ .../tv/extension/cam/ICamDrmInfoListener.aidl | 26 +++++++++++++ ...CamHostControlAskReleaseReplyCallback.aidl | 24 ++++++++++++ .../cam/ICamHostControlInfoListener.aidl | 24 ++++++++++++ .../extension/cam/ICamHostControlService.aidl | 35 +++++++++++++++++ .../cam/ICamHostControlTuneQuietlyFlag.aidl | 32 ++++++++++++++++ ...CamHostControlTuneQuietlyFlagListener.aidl | 24 ++++++++++++ .../tv/extension/cam/ICamInfoListener.aidl | 28 ++++++++++++++ .../extension/cam/ICamMonitoringService.aidl | 38 +++++++++++++++++++ .../cam/ICamPinCapabilityListener.aidl | 26 +++++++++++++ .../tv/extension/cam/ICamPinService.aidl | 35 +++++++++++++++++ .../extension/cam/ICamPinStatusListener.aidl | 26 +++++++++++++ .../extension/cam/ICamProfileInterface.aidl | 29 ++++++++++++++ .../extension/cam/IContentControlService.aidl | 32 ++++++++++++++++ .../cam/IEnterMenuErrorCallback.aidl | 24 ++++++++++++ .../media/tv/extension/cam/IMmiInterface.aidl | 32 ++++++++++++++++ .../media/tv/extension/cam/IMmiSession.aidl | 31 +++++++++++++++ .../tv/extension/cam/IMmiStatusCallback.aidl | 28 ++++++++++++++ 19 files changed, 552 insertions(+) create mode 100644 media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamHostControlService.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamInfoListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamPinService.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl create mode 100644 media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl create mode 100644 media/java/android/media/tv/extension/cam/IContentControlService.aidl create mode 100644 media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl create mode 100644 media/java/android/media/tv/extension/cam/IMmiInterface.aidl create mode 100644 media/java/android/media/tv/extension/cam/IMmiSession.aidl create mode 100644 media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl diff --git a/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl new file mode 100644 index 000000000000..73ae209062d6 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamAppInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamAppInfoListener { + void onCamAppInfoChanged(int slotId, in Bundle appInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl b/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl new file mode 100644 index 000000000000..d3a03bc80056 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamAppInfoService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamAppInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamAppInfoService { + // Register ICamAppInfoListener to get CICAM Application Information updates. + void addCamAppInfoListener(ICamAppInfoListener listener); + // Unregister ICamAppInfoListener and stop get Application Information notify. + void removeCamAppInfoListener(ICamAppInfoListener listener); + // Get the Application Information of the CICAM. + int getCamAppInfo(int slotId, out Bundle appInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl new file mode 100644 index 000000000000..c727837ed07e --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamDrmInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamDrmInfoListener { + void onCamDrmInfoChanged(int slotId, in Bundle camDrmInfo); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl new file mode 100644 index 000000000000..83f2c01e4681 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlAskReleaseReplyCallback.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlAskReleaseReplyCallback { + void onAskReleaseReply(String sessionToken, int replyStatus); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl new file mode 100644 index 000000000000..f48ca57cccdc --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlInfoListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlInfoListener { + void onCamHostControlInfoChanged(String sessionToken, int sessionStatus); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlService.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlService.aidl new file mode 100644 index 000000000000..6f6c376c1ffa --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamHostControlAskReleaseReplyCallback; +import android.media.tv.extension.cam.ICamHostControlInfoListener; + +/** + * @hide + */ +interface ICamHostControlService { + // Register the listener to monitor host control session updates. + void addCamHostcontrolInfoListener(ICamHostControlInfoListener listener); + // Unregister ICamHostControlInfoListener and stop monitoring. + void removeCamHostcontrolInfoListener(ICamHostControlInfoListener listener); + // Request CICAM to release the resource. + int sendCamHostControlAskRelease(String sessionToken, + ICamHostControlAskReleaseReplyCallback callback); + // Enable/disable the host control mode. + void setHostControlMode(String sessionToken, boolean enable); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl new file mode 100644 index 000000000000..25a78b99d659 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlag.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlagListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamHostControlTuneQuietlyFlag { + // Register listener to notify host control tune_quietly_flag. + void addHcTuneQuietlyFlagListener(ICamHostControlTuneQuietlyFlagListener listener); + // Remove listener and stop monitor host control tune_quietly_flag. + void removeHcTuneQuietlyFlagListener(ICamHostControlTuneQuietlyFlagListener listener); + // Returns host control tune_quietly_flag value. + Bundle getHcTuneQuietlyFlag(String sessionToken); +} diff --git a/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl new file mode 100644 index 000000000000..5967124e5202 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamHostControlTuneQuietlyFlagListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface ICamHostControlTuneQuietlyFlagListener { + void onHcTuneQuietlyFlagChanged(String sessionToken, int tuneQuietlyFlag); +} diff --git a/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl b/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl new file mode 100644 index 000000000000..5410bffaa92d --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamInfoListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamInfoListener { + void onCamInfoChanged(int slotId, in Bundle updatedCamInfo); + void onSlotInfoChanged(int slotId, in Bundle updatedSlotInfo); + void onNewTypeCamInsert(int slotId, in Bundle newCamType); +} diff --git a/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl b/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl new file mode 100644 index 000000000000..7b8014c5880f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamMonitoringService.aidl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamMonitoringService { + // Register a listener for slot/CAM info updates. + void addCamInfoListener(in ICamInfoListener listener); + // Unregister a listener for slot/CAM info updates. + void removeCamInfoListener(in ICamInfoListener listener); + // Get CAM information for the specified slot. + Bundle getCamInfo(int slotId); + // Get slot information. + Bundle getSlotInfo(int slotId); + // Returns list of slot Ids. + int[] getSlotIds(); + // Check if the country supports CAM. + boolean isCamSupported(); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl b/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl new file mode 100644 index 000000000000..f92304af9ade --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinCapabilityListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamPinCapabilityListener { + void onCamPinCapabilityChanged(int slotId, in Bundle bundle); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinService.aidl b/media/java/android/media/tv/extension/cam/ICamPinService.aidl new file mode 100644 index 000000000000..3f6cb2813c74 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamPinCapabilityListener; +import android.media.tv.extension.cam.ICamPinStatusListener; +import android.os.Bundle; + +/** + * @hide + */ +interface ICamPinService { + // Register ICamPinCapabilityListener to get CICAM updates. + void addCamPinCapabilityListener(in ICamPinCapabilityListener listener); + // Unregister ICamPinCapabilityListener and stop monitor PIN status and PIN capability. + void removeCamPinCapabilityListener(in ICamPinCapabilityListener listener); + // Send the PinCode that needs to be validated by CICAM. + int requestCamPinValidation(int slotId, in int[] pinCode, in ICamPinStatusListener listener); + // Get the PIN capabilities of the CICAM. + int getCamPinCapability(int slotId, out Bundle camPinCapability); +} diff --git a/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl b/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl new file mode 100644 index 000000000000..efbc394897d3 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamPinStatusListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface ICamPinStatusListener { + void onCamPinValidationReply(int slotId, in Bundle bundle); +} diff --git a/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl b/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl new file mode 100644 index 000000000000..3f1d40caa9c9 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/ICamProfileInterface.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +interface ICamProfileInterface { + // Get CAM service update information for special slot. + Bundle getCamServiceUpdateInfo(int slotNumber); + // Request CAM TIS resend cam info update broadcast message when APK boot up. + void requestResendProfileInfoBroadcastACON(); +} diff --git a/media/java/android/media/tv/extension/cam/IContentControlService.aidl b/media/java/android/media/tv/extension/cam/IContentControlService.aidl new file mode 100644 index 000000000000..6db79abacb76 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IContentControlService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.ICamDrmInfoListener; +import android.os.Bundle; + +/** + * @hide + */ +interface IContentControlService { + // Register the listener to notify the DRM info changed by the CICAM. + void addCamDrmInfoListener(in ICamDrmInfoListener listener); + // Unregister listener to stop monitor DRM Info. + void removeCamDrmInfoListener(in ICamDrmInfoListener listener); + // Get the DRM Info of current watching channel. + int getCamDrmInfo(int slotId, out Bundle camDrmInfo); +} diff --git a/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl b/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl new file mode 100644 index 000000000000..ff61ddcbcc2f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IEnterMenuErrorCallback.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +oneway interface IEnterMenuErrorCallback { + void onAppInfoEnterMenuError(); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiInterface.aidl b/media/java/android/media/tv/extension/cam/IMmiInterface.aidl new file mode 100644 index 000000000000..17a2a9c6771f --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiInterface.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.media.tv.extension.cam.IEnterMenuErrorCallback; +import android.media.tv.extension.cam.IMmiSession; +import android.media.tv.extension.cam.IMmiStatusCallback; + + +/** + * @hide + */ +interface IMmiInterface { + // Open a session for MMI. + IMmiSession openSession(int slotId, IMmiStatusCallback callback); + // Request to display CI Module setup screen. + void appInfoEnterMenu(int slotId, IEnterMenuErrorCallback callback); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiSession.aidl b/media/java/android/media/tv/extension/cam/IMmiSession.aidl new file mode 100644 index 000000000000..c4f276219c55 --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiSession.aidl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +/** + * @hide + */ +interface IMmiSession { + // Send user answers to CAM on the MMI Menu screen. + void setMenuListAnswer(int response); + // Send user answers to CAM on the MMI Enq screen. + void setEnquiryAnswer(int answerId, String answer); + // Send CloseMmi APDU to Cam. + void closeMmi(); + // Release MMI session. + void close(); +} diff --git a/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl b/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl new file mode 100644 index 000000000000..3e68ced6a1ce --- /dev/null +++ b/media/java/android/media/tv/extension/cam/IMmiStatusCallback.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.cam; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IMmiStatusCallback { + void onMmiEnq(in Bundle request); + void onMmiListMenu(in Bundle request); + void onMmiClose(); +} -- GitLab From 393262d851b253bd1c829a23b1ecfaff8f210be4 Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Fri, 15 Nov 2024 13:41:05 -0800 Subject: [PATCH 090/656] TIS: Standardize TIS Tune Extensions API Define standardized AIDL interfaces for tune extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: I359843e2474d19255767330783822566ae25e188 --- .../tune/IChannelTunedInterface.aidl | 27 +++++++++++++++ .../extension/tune/IChannelTunedListener.aidl | 26 +++++++++++++++ .../media/tv/extension/tune/IMuxTune.aidl | 26 +++++++++++++++ .../tv/extension/tune/IMuxTuneSession.aidl | 33 +++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl create mode 100644 media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl create mode 100644 media/java/android/media/tv/extension/tune/IMuxTune.aidl create mode 100644 media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl diff --git a/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl new file mode 100644 index 000000000000..88e50844f998 --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IChannelTunedInterface.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.tune; + +import android.media.tv.extension.tune.IChannelTunedListener; + +/* +* @hide +*/ +interface IChannelTunedInterface { + void addChannelTunedListener(in IChannelTunedListener listener); + void removeChannelTunedListener(in IChannelTunedListener listener); +} diff --git a/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl new file mode 100644 index 000000000000..46875463402d --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IChannelTunedListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.tune; + +import android.os.Bundle; + +/* +* @hide +*/ +oneway interface IChannelTunedListener { + void onChannelTuned(String sessionToken, in Bundle channelTunedInfo); +} diff --git a/media/java/android/media/tv/extension/tune/IMuxTune.aidl b/media/java/android/media/tv/extension/tune/IMuxTune.aidl new file mode 100644 index 000000000000..4e9dbdae6643 --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IMuxTune.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.tune; + +import android.media.tv.extension.tune.IMuxTuneSession; + +/** +* @hide +*/ +interface IMuxTune { + IMuxTuneSession createSession(int broadcastType, String clientToken); +} diff --git a/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl new file mode 100644 index 000000000000..5ad72b48b50b --- /dev/null +++ b/media/java/android/media/tv/extension/tune/IMuxTuneSession.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.tune; + +import android.os.Bundle; + +/** +* @hide +*/ +interface IMuxTuneSession { + // Start mux tune with tune params. + void start(int broadcastType, int frequency, int brandwith, in Bundle muxTuneParams); + // Stop mux tune. + void stop(); + // Release muxtune resources. + void release(); + // Get the session token created by TIS to identify different sessions. + String getSessionToken(); +} -- GitLab From 6508ac8505ec300de510236e089f24e6df0ab09a Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 14 Nov 2024 13:13:32 -0800 Subject: [PATCH 091/656] Use RavenwoodRule for system properties We're going to remove RavenwoodConfig and will start using RavenwoodRule (again) for initializing sysprops. Flag: EXEMPT host test change only Bug: 377765941 Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh -t -s Change-Id: I9b9a5c4f8987ba32353d25ebb3840b725c246e77 --- .../src/android/os/SystemPropertiesTest.java | 7 ++-- .../test/ravenwood/RavenwoodConfig.java | 34 +++++++++---------- .../test/ravenwood/RavenwoodRule.java | 4 +-- .../WakelockPowerStatsCollectorTest.java | 10 +++--- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java index 75aca1b8820c..7ce2ed823540 100644 --- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java +++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java @@ -23,10 +23,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.platform.test.ravenwood.RavenwoodConfig; +import android.platform.test.ravenwood.RavenwoodRule; import androidx.test.filters.SmallTest; +import org.junit.Rule; import org.junit.Test; import java.util.Objects; @@ -39,8 +40,8 @@ public class SystemPropertiesTest { private static final String PERSIST_KEY = "persist.sys.testkey"; private static final String NONEXIST_KEY = "doesnotexist_2341431"; - @RavenwoodConfig.Config - public static final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder() + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() .setSystemPropertyMutable(KEY, null) .setSystemPropertyMutable(UNSET_KEY, null) .setSystemPropertyMutable(PERSIST_KEY, null) diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 3ed8b0a748e1..b7752215820e 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -154,34 +154,32 @@ public final class RavenwoodConfig { } /** - * Configure the given system property as immutable for the duration of the test. - * Read access to the key is allowed, and write access will fail. When {@code value} is - * {@code null}, the value is left as undefined. - * - * All properties in the {@code debug.*} namespace are automatically mutable, with no - * developer action required. - * - * Has no effect on non-Ravenwood environments. + * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyImmutable(String, Object)} */ + @Deprecated public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) { - mConfig.mSystemProperties.setValue(key, value); - mConfig.mSystemProperties.setAccessReadOnly(key); return this; } /** - * Configure the given system property as mutable for the duration of the test. - * Both read and write access to the key is allowed, and its value will be reset between - * each test. When {@code value} is {@code null}, the value is left as undefined. - * - * All properties in the {@code debug.*} namespace are automatically mutable, with no - * developer action required. - * - * Has no effect on non-Ravenwood environments. + * @deprecated Use {@link RavenwoodRule.Builder#setSystemPropertyMutable(String, Object)} */ + @Deprecated public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) { + return this; + } + + Builder setSystemPropertyImmutableReal(@NonNull String key, + @Nullable Object value) { + mConfig.mSystemProperties.setValue(key, value); + mConfig.mSystemProperties.setAccessReadOnly(key); + return this; + } + + Builder setSystemPropertyMutableReal(@NonNull String key, + @Nullable Object value) { mConfig.mSystemProperties.setValue(key, value); mConfig.mSystemProperties.setAccessReadWrite(key); return this; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index bfa3802ce583..af936da2fe48 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -155,7 +155,7 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyImmutable(key, value); + mBuilder.setSystemPropertyImmutableReal(key, value); return this; } @@ -170,7 +170,7 @@ public final class RavenwoodRule implements TestRule { * Has no effect on non-Ravenwood environments. */ public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) { - mBuilder.setSystemPropertyMutable(key, value); + mBuilder.setSystemPropertyMutableReal(key, value); return this; } diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java index 0d5d277b00ef..ed927c6ab699 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WakelockPowerStatsCollectorTest.java @@ -26,8 +26,7 @@ import android.content.Context; import android.os.Process; import android.platform.test.annotations.DisableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.platform.test.ravenwood.RavenwoodConfig; -import android.platform.test.ravenwood.RavenwoodConfig.Config; +import android.platform.test.ravenwood.RavenwoodRule; import com.android.internal.os.PowerStats; import com.android.server.power.feature.flags.Flags; @@ -39,10 +38,9 @@ import org.junit.Test; public class WakelockPowerStatsCollectorTest { - @Config - public static final RavenwoodConfig sConfig = - new RavenwoodConfig.Builder() - .setProvideMainThread(true) + @Rule + public final RavenwoodRule mRule = + new RavenwoodRule.Builder() .setSystemPropertyImmutable( "persist.sys.com.android.server.power.feature.flags." + "framework_wakelock_info-override", -- GitLab From 92901b7c88fe8d670df9d5ef92d3b2bf507ce2f8 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 13 Nov 2024 16:19:42 -0800 Subject: [PATCH 092/656] Log bubble removed events from bubble bar Check in bubble data if we are removing a bubble while bubble bar is active if the removal reason matches one the defined reasons. If criteria is met, log an event. Bug: 349845968 Test: atest com.android.wm.shell.bubbles.BubbleDataTest Test: manual, cancel a bubbled notif from the test app, observe that cancel event is logged Test: manual, finish bubble activity using test app, observe that finish event is logged Test: manual, turn off bubbles from settings for test app while having active bubbles, observe that blocked event is logged Flag: com.android.wm.shell.enable_bubble_bar Change-Id: I0480fd830f695f1fbdefa098039df275d2ec9522 --- .../android/wm/shell/bubbles/BubbleData.java | 25 +++++++- .../wm/shell/bubbles/BubbleLogger.java | 4 +- .../wm/shell/bubbles/BubblePositioner.java | 7 +++ .../wm/shell/bubbles/BubbleDataTest.java | 62 +++++++++++++++++-- 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 4de9dfa54c5d..ec6af9f1883a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -759,7 +759,9 @@ public class BubbleData { if (b != null) { b.stopInflation(); } - mLogger.logOverflowRemove(b, reason); + if (!mPositioner.isShowingInBubbleBar()) { + mLogger.logStackOverflowRemove(b, reason); + } mOverflowBubbles.remove(b); mStateChange.bubbleRemoved(b, reason); mStateChange.removedOverflowBubble = b; @@ -802,6 +804,27 @@ public class BubbleData { setNewSelectedIndex(indexToRemove); } maybeSendDeleteIntent(reason, bubbleToRemove); + + if (mPositioner.isShowingInBubbleBar()) { + logBubbleBarBubbleRemoved(bubbleToRemove, reason); + } + } + + private void logBubbleBarBubbleRemoved(Bubble bubble, @DismissReason int reason) { + switch (reason) { + case Bubbles.DISMISS_NOTIF_CANCEL: + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED); + break; + case Bubbles.DISMISS_TASK_FINISHED: + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH); + break; + case Bubbles.DISMISS_BLOCKED: + case Bubbles.DISMISS_NO_LONGER_BUBBLE: + mLogger.log(bubble, BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED); + break; + default: + // skip logging other events + } } private void setNewSelectedIndex(int indexOfSelected) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java index 36630733e1da..a2cc48b1f460 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java @@ -182,10 +182,12 @@ public class BubbleLogger { } /** + * Log when a bubble is removed from overflow in stack view + * * @param b Bubble removed from overflow * @param r Reason that bubble was removed */ - public void logOverflowRemove(Bubble b, @Bubbles.DismissReason int r) { + public void logStackOverflowRemove(Bubble b, @Bubbles.DismissReason int r) { if (r == Bubbles.DISMISS_NOTIF_CANCEL) { log(b, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_CANCEL); } else if (r == Bubbles.DISMISS_GROUP_CANCELLED) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index c386c9398624..068b2d246500 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -830,6 +830,13 @@ public class BubblePositioner { mShowingInBubbleBar = showingInBubbleBar; } + /** + * Whether bubbles ar showing in the bubble bar from launcher. + */ + boolean isShowingInBubbleBar() { + return mShowingInBubbleBar; + } + public void setBubbleBarLocation(BubbleBarLocation location) { mBubbleBarLocation = location; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 6fa37885b724..72595e67193f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -47,6 +47,7 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; +import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.bubbles.BubbleData.TimeSource; import com.android.wm.shell.common.ShellExecutor; @@ -102,6 +103,7 @@ public class BubbleDataTest extends ShellTestCase { private BubbleData mBubbleData; private TestableBubblePositioner mPositioner; + private UiEventLoggerFake mUiEventLogger; @Mock private TimeSource mTimeSource; @@ -112,8 +114,6 @@ public class BubbleDataTest extends ShellTestCase { @Mock private PendingIntent mDeleteIntent; @Mock - private BubbleLogger mBubbleLogger; - @Mock private BubbleEducationController mEducationController; @Mock private ShellExecutor mMainExecutor; @@ -196,10 +196,12 @@ public class BubbleDataTest extends ShellTestCase { mock(Icon.class), mMainExecutor, mBgExecutor); + mUiEventLogger = new UiEventLoggerFake(); + mPositioner = new TestableBubblePositioner(mContext, mContext.getSystemService(WindowManager.class)); - mBubbleData = new BubbleData(getContext(), mBubbleLogger, mPositioner, mEducationController, - mMainExecutor, mBgExecutor); + mBubbleData = new BubbleData(getContext(), new BubbleLogger(mUiEventLogger), mPositioner, + mEducationController, mMainExecutor, mBgExecutor); // Used by BubbleData to set lastAccessedTime when(mTimeSource.currentTimeMillis()).thenReturn(1000L); @@ -296,6 +298,58 @@ public class BubbleDataTest extends ShellTestCase { assertThat(bubbleBarUpdate.removedBubbles).isEmpty(); } + @Test + public void testRemoveBubbleFromBubbleBar_notifCancelled_logEvent() { + mPositioner.setShowingInBubbleBar(true); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NOTIF_CANCEL); + assertThat(mUiEventLogger.numLogs()).isEqualTo(1); + assertThat(mUiEventLogger.eventId(0)).isEqualTo( + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_CANCELED.getId()); + } + + @Test + public void testRemoveBubbleFromBubbleBar_taskFinished_logEvent() { + mPositioner.setShowingInBubbleBar(true); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_TASK_FINISHED); + assertThat(mUiEventLogger.numLogs()).isEqualTo(1); + assertThat(mUiEventLogger.eventId(0)).isEqualTo( + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_ACTIVITY_FINISH.getId()); + } + + @Test + public void testRemoveBubbleFromBubbleBar_notifBlocked_logEvent() { + mPositioner.setShowingInBubbleBar(true); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED); + assertThat(mUiEventLogger.numLogs()).isEqualTo(1); + assertThat(mUiEventLogger.eventId(0)).isEqualTo( + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId()); + } + + @Test + public void testRemoveBubbleFromBubbleBar_noLongerBubble_logEvent() { + mPositioner.setShowingInBubbleBar(true); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_NO_LONGER_BUBBLE); + assertThat(mUiEventLogger.numLogs()).isEqualTo(1); + assertThat(mUiEventLogger.eventId(0)).isEqualTo( + BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId()); + } + @Test public void ifSuppress_hideFlyout() { // Setup -- GitLab From 330bb37ff5fed0804f047751796bf9a190ea6bcf Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 14 Nov 2024 09:08:42 -0800 Subject: [PATCH 093/656] Log when bubble is moved to overflow Log when there are too many bubbles in the bubble bar and the oldest bubble gets moved to overflow. Bug: 349845968 Test: atest com.android.wm.shell.bubbles.BubbleDataTest Test: manual, trigger multiple bubbles until there are too many, check that when oldest bubble moves to overflow, event is logged Flag: com.android.wm.shell.enable_bubble_bar Change-Id: Ib6f2f9a48772c0571291c80ab301b2ddc9b95c00 --- .../android/wm/shell/bubbles/BubbleData.java | 2 +- .../wm/shell/bubbles/BubbleLogger.java | 20 ++++++++++------ .../wm/shell/bubbles/BubbleDataTest.java | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index ec6af9f1883a..294569190f68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -885,7 +885,7 @@ public class BubbleData { return; } ProtoLog.d(WM_SHELL_BUBBLES, "overflowBubble=%s", bubble.getKey()); - mLogger.logOverflowAdd(bubble, reason); + mLogger.logOverflowAdd(bubble, mPositioner.isShowingInBubbleBar(), reason); if (mOverflowBubbles.isEmpty()) { mStateChange.showOverflowChanged = true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java index a2cc48b1f460..347df330c4b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleLogger.java @@ -203,13 +203,19 @@ public class BubbleLogger { * @param b Bubble added to overflow * @param r Reason that bubble was added to overflow */ - public void logOverflowAdd(Bubble b, @Bubbles.DismissReason int r) { - if (r == Bubbles.DISMISS_AGED) { - log(b, Event.BUBBLE_OVERFLOW_ADD_AGED); - } else if (r == Bubbles.DISMISS_USER_GESTURE) { - log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE); - } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) { - log(b, Event.BUBBLE_OVERFLOW_RECOVER); + public void logOverflowAdd(Bubble b, boolean bubbleBar, @Bubbles.DismissReason int r) { + if (bubbleBar) { + if (r == Bubbles.DISMISS_AGED) { + log(b, Event.BUBBLE_BAR_OVERFLOW_ADD_AGED); + } + } else { + if (r == Bubbles.DISMISS_AGED) { + log(b, Event.BUBBLE_OVERFLOW_ADD_AGED); + } else if (r == Bubbles.DISMISS_USER_GESTURE) { + log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE); + } else if (r == Bubbles.DISMISS_RELOAD_FROM_DISK) { + log(b, Event.BUBBLE_OVERFLOW_RECOVER); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index 72595e67193f..ce640b5e5195 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -350,6 +350,30 @@ public class BubbleDataTest extends ShellTestCase { BubbleLogger.Event.BUBBLE_BAR_BUBBLE_REMOVED_BLOCKED.getId()); } + @Test + public void testRemoveBubbleFromBubbleBar_addToOverflow_logEvent() { + mPositioner.setShowingInBubbleBar(true); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_AGED); + assertThat(mUiEventLogger.numLogs()).isEqualTo(1); + assertThat(mUiEventLogger.eventId(0)).isEqualTo( + BubbleLogger.Event.BUBBLE_BAR_OVERFLOW_ADD_AGED.getId()); + } + + @Test + public void testRemoveBubble_notifCancelled_noLog() { + mPositioner.setShowingInBubbleBar(false); + + sendUpdatedEntryAtTime(mEntryA1, 1000); + mBubbleData.setListener(mListener); + + mBubbleData.dismissBubbleWithKey(mEntryA1.getKey(), Bubbles.DISMISS_BLOCKED); + assertThat(mUiEventLogger.numLogs()).isEqualTo(0); + } + @Test public void ifSuppress_hideFlyout() { // Setup -- GitLab From b214d1512645b7ad43bd74569d83a551f3508459 Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Fri, 15 Nov 2024 14:41:00 -0800 Subject: [PATCH 094/656] [flexiglass] Immediately locks device when sleep button is pressed. Bug: 378708150 Test: manually verified that hitting the sleep button (using "adb shell input keyevent 223") immediately locks the device even if the "lock after screen timeout" setting is not set to "immediately" Test: added a unit test Test: atest PlatformScenarioTests:android.platform.test.scenario.sysui.notification.LockscreenSilentNotificationVisibility#goToLockscreen_silentShown_showAlertingNotification -- --abi arm64-v8a Flag: com.android.systemui.scene_container Change-Id: Ia7cc37cf65d9f6b720e56baf1ef9ab84d6331875 --- .../DeviceUnlockedInteractorTest.kt | 21 +++++++++++++++++++ .../interactor/DeviceUnlockedInteractor.kt | 4 +++- .../power/shared/model/WakeSleepReason.kt | 4 ++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt index 772025b2c635..47cba0723804 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorTest.kt @@ -582,6 +582,27 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() { assertThat(isUnlocked).isFalse() } + @Test + fun deviceUnlockStatus_isResetToFalse_whenDeviceGoesToSleep_fromSleepButton() = + testScope.runTest { + setLockAfterScreenTimeout(5000) + kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false + val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) + + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + assertThat(deviceUnlockStatus?.isUnlocked).isTrue() + + kosmos.powerInteractor.setAsleepForTest( + sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON + ) + runCurrent() + + assertThat(deviceUnlockStatus?.isUnlocked).isFalse() + } + private fun TestScope.unlockDevice() { val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus) diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt index 6fead6e8f9c3..351d77c2ae6e 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt @@ -233,10 +233,12 @@ constructor( .map { (isAsleep, lastSleepReason) -> if (isAsleep) { if ( - lastSleepReason == WakeSleepReason.POWER_BUTTON && + (lastSleepReason == WakeSleepReason.POWER_BUTTON) && authenticationInteractor.getPowerButtonInstantlyLocks() ) { LockImmediately("locked instantly from power button") + } else if (lastSleepReason == WakeSleepReason.SLEEP_BUTTON) { + LockImmediately("locked instantly from sleep button") } else { LockWithDelay("entering sleep") } diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt index 776a8f47f056..c57b53bab442 100644 --- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt +++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt @@ -26,6 +26,9 @@ enum class WakeSleepReason( /** The physical power button was pressed to wake up or sleep the device. */ POWER_BUTTON(isTouch = false, PowerManager.WAKE_REASON_POWER_BUTTON), + /** The sleep button was pressed to sleep the device. */ + SLEEP_BUTTON(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON), + /** The user has tapped or double tapped to wake the screen. */ TAP(isTouch = true, PowerManager.WAKE_REASON_TAP), @@ -78,6 +81,7 @@ enum class WakeSleepReason( fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason { return when (reason) { PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON + PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON -> SLEEP_BUTTON PowerManager.GO_TO_SLEEP_REASON_TIMEOUT -> TIMEOUT PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD -> FOLD else -> OTHER -- GitLab From 32632587f7de7a0d8866e92687e67cd3c5debc5d Mon Sep 17 00:00:00 2001 From: Sherry Huang Date: Wed, 13 Nov 2024 13:56:15 -0800 Subject: [PATCH 095/656] TIS: Standardize TIS Teletext Extensions API Define standardized AIDL interfaces for teletext extension package. Flag: android.media.tv.flags.tif_extension_standardization Bug: b/344029126 Test: local testing with m Change-Id: Ida965ee51785e4b3900c272601d715a57774462d --- .../teletext/IDataServiceSignalInfo.aidl | 35 ++++++++++++++++ .../IDataServiceSignalInfoListener.aidl | 26 ++++++++++++ .../teletext/ITeletextPageSubCode.aidl | 41 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl create mode 100644 media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl create mode 100644 media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl new file mode 100644 index 000000000000..a3725e4e25db --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfo.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.teletext; + +import android.media.tv.extension.teletext.IDataServiceSignalInfoListener; +import android.os.Bundle; + + +/** + * @hide + */ +interface IDataServiceSignalInfo { + // Get Teletext data service signal information. + Bundle getDataServiceSignalInfo(String sessionToken); + // Add a listener that receives notifications of teletext running information. + void addDataServiceSignalInfoListener(String clientToken, + IDataServiceSignalInfoListener listener); + // Remove a listener that receives notifications of Teletext running information. + void removeDataServiceSignalInfoListener(String clientToken, + IDataServiceSignalInfoListener listener); +} diff --git a/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl new file mode 100644 index 000000000000..0e99bf5cc955 --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/IDataServiceSignalInfoListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.teletext; + +import android.os.Bundle; + +/** + * @hide + */ +oneway interface IDataServiceSignalInfoListener { + void onDataServiceSignalInfoChanged (String sessionToken, in Bundle changedSignalInfo); +} diff --git a/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl new file mode 100644 index 000000000000..c96ffc02f438 --- /dev/null +++ b/media/java/android/media/tv/extension/teletext/ITeletextPageSubCode.aidl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.extension.teletext; + +import android.os.Bundle; + +/** + * @hide + */ +interface ITeletextPageSubCode { + // Get Teletext page number + Bundle getTeletextPageNumber(String sessionToken); + // Set Teletext page number. + void setTeleltextPageNumber(String sessionToken, int pageNumber); + // Get Teletext sub page number. + Bundle getTeletextPageSubCode(String sessionToken); + // Set Teletext sub page number. + void setTeletextPageSubCode(String sessionToken, int pageSubCode); + // Get Teletext TopInfo. + Bundle getTeletextHasTopInfo(String sessionToken); + // Get Teletext TopBlockList. + Bundle getTeletextTopBlockList(String sessionToken); + // Get Teletext TopGroupList. + Bundle getTeletextTopGroupList(String sessionToken, int indexGroup); + // Get Teletext TopPageList. + Bundle getTeletextTopPageList(String sessionToken, int indexPage); +} -- GitLab From 18682dc6f75c6164afe319ccac6cb61c71855e6c Mon Sep 17 00:00:00 2001 From: Jorge Betancourt Date: Fri, 15 Nov 2024 22:54:49 +0000 Subject: [PATCH 096/656] fix incorrect casting for child color filters Flag: com.android.graphics.hwui.flags.runtime_color_filters_blenders Test: atest CtsUiRenderingTestCases:RuntimeShaderTests CtsUiRenderingTestCases:RuntimeColorFilterTests CtsUiRenderingTestCases:RuntimeXfermodeTests Bug: b/358126864 b/379193391 Change-Id: Ic0a92c18075e1fd9f07080c2b9a7a795e541cdee --- .../android/graphics/RuntimeColorFilter.java | 6 ++++-- .../java/android/graphics/RuntimeShader.java | 11 ++++++++++- .../java/android/graphics/RuntimeXfermode.java | 4 +++- libs/hwui/jni/ColorFilter.cpp | 18 +++++++++++++++++- libs/hwui/jni/RuntimeXfermode.cpp | 16 ++++++++++++++++ libs/hwui/jni/Shader.cpp | 12 ++++++++++++ 6 files changed, 62 insertions(+), 5 deletions(-) diff --git a/graphics/java/android/graphics/RuntimeColorFilter.java b/graphics/java/android/graphics/RuntimeColorFilter.java index d112f7153fca..a64acfe767a9 100644 --- a/graphics/java/android/graphics/RuntimeColorFilter.java +++ b/graphics/java/android/graphics/RuntimeColorFilter.java @@ -280,7 +280,8 @@ public class RuntimeColorFilter extends ColorFilter { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(getNativeInstance(), filterName, colorFilter.getNativeInstance()); + nativeUpdateInputColorFilter(getNativeInstance(), filterName, + colorFilter.getNativeInstance()); } /** @@ -318,5 +319,6 @@ public class RuntimeColorFilter extends ColorFilter { long colorFilter, String uniformName, int value1, int value2, int value3, int value4, int count); private static native void nativeUpdateChild(long colorFilter, String childName, long child); - + private static native void nativeUpdateInputColorFilter(long colorFilter, String childName, + long inputFilter); } diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java index 6316c1fb8b47..3543e991924e 100644 --- a/graphics/java/android/graphics/RuntimeShader.java +++ b/graphics/java/android/graphics/RuntimeShader.java @@ -264,6 +264,9 @@ public class RuntimeShader extends Shader { * enable better heap tracking & tooling support */ private ArrayMap mShaderUniforms = new ArrayMap<>(); + private ArrayMap mColorFilterUniforms = new ArrayMap<>(); + private ArrayMap mXfermodeUniforms = new ArrayMap<>(); + /** * Creates a new RuntimeShader. @@ -544,8 +547,10 @@ public class RuntimeShader extends Shader { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, filterName, + mColorFilterUniforms.put(filterName, colorFilter); + nativeUpdateColorFilter(mNativeInstanceRuntimeShaderBuilder, filterName, colorFilter.getNativeInstance()); + discardNativeInstance(); } /** @@ -563,8 +568,10 @@ public class RuntimeShader extends Shader { if (xfermode == null) { throw new NullPointerException("The xfermode parameter must not be null"); } + mXfermodeUniforms.put(xfermodeName, xfermode); nativeUpdateChild(mNativeInstanceRuntimeShaderBuilder, xfermodeName, xfermode.createNativeInstance()); + discardNativeInstance(); } @@ -594,6 +601,8 @@ public class RuntimeShader extends Shader { int value4, int count); private static native void nativeUpdateShader( long shaderBuilder, String shaderName, long shader); + private static native void nativeUpdateColorFilter( + long shaderBuilder, String colorFilterName, long colorFilter); private static native void nativeUpdateChild( long shaderBuilder, String childName, long child); } diff --git a/graphics/java/android/graphics/RuntimeXfermode.java b/graphics/java/android/graphics/RuntimeXfermode.java index 51d97a4b7487..c8a0b1a11339 100644 --- a/graphics/java/android/graphics/RuntimeXfermode.java +++ b/graphics/java/android/graphics/RuntimeXfermode.java @@ -285,7 +285,8 @@ public class RuntimeXfermode extends Xfermode { if (colorFilter == null) { throw new NullPointerException("The colorFilter parameter must not be null"); } - nativeUpdateChild(mBuilderNativeInstance, filterName, colorFilter.getNativeInstance()); + nativeUpdateColorFilter(mBuilderNativeInstance, filterName, + colorFilter.getNativeInstance()); } /** @@ -325,5 +326,6 @@ public class RuntimeXfermode extends Xfermode { long builder, String uniformName, int value1, int value2, int value3, int value4, int count); private static native void nativeUpdateChild(long builder, String childName, long child); + private static native void nativeUpdateColorFilter(long builder, String childName, long filter); } diff --git a/libs/hwui/jni/ColorFilter.cpp b/libs/hwui/jni/ColorFilter.cpp index 20301d2c76ec..1c6d886f18b7 100644 --- a/libs/hwui/jni/ColorFilter.cpp +++ b/libs/hwui/jni/ColorFilter.cpp @@ -163,6 +163,20 @@ public: filter->updateChild(env, name.c_str(), child); } } + + static void RuntimeColorFilter_updateInputColorFilter(JNIEnv* env, jobject, + jlong colorFilterPtr, jstring childName, + jlong childFilterPtr) { + auto* filter = reinterpret_cast(colorFilterPtr); + ScopedUtfChars name(env, childName); + auto* child = reinterpret_cast(childFilterPtr); + if (filter && child) { + auto childInput = child->getInstance(); + if (childInput) { + filter->updateChild(env, name.c_str(), childInput.release()); + } + } + } }; static const JNINativeMethod colorfilter_methods[] = { @@ -193,7 +207,9 @@ static const JNINativeMethod runtime_color_filter_methods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)ColorFilterGlue::RuntimeColorFilter_updateUniformsInts}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", - (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}}; + (void*)ColorFilterGlue::RuntimeColorFilter_updateChild}, + {"nativeUpdateInputColorFilter", "(JLjava/lang/String;J)V", + (void*)ColorFilterGlue::RuntimeColorFilter_updateInputColorFilter}}; int register_android_graphics_ColorFilter(JNIEnv* env) { android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods, diff --git a/libs/hwui/jni/RuntimeXfermode.cpp b/libs/hwui/jni/RuntimeXfermode.cpp index c1c8964bf5eb..17bee8fb8b15 100644 --- a/libs/hwui/jni/RuntimeXfermode.cpp +++ b/libs/hwui/jni/RuntimeXfermode.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "ColorFilter.h" #include "GraphicsJNI.h" #include "RuntimeEffectUtils.h" #include "SkBlender.h" @@ -93,6 +94,19 @@ static void RuntimeXfermode_updateChild(JNIEnv* env, jobject, jlong builderPtr, } } +static void RuntimeXfermode_updateColorFilter(JNIEnv* env, jobject, jlong builderPtr, + jstring childName, jlong colorFilterPtr) { + auto* builder = reinterpret_cast(builderPtr); + ScopedUtfChars name(env, childName); + auto* child = reinterpret_cast(colorFilterPtr); + if (child) { + auto childInput = child->getInstance(); + if (childInput) { + UpdateChild(env, builder, name.c_str(), childInput.release()); + } + } +} + static const JNINativeMethod gRuntimeXfermodeMethods[] = { {"nativeGetFinalizer", "()J", (void*)RuntimeXfermode_getNativeFinalizer}, {"nativeCreateBlenderBuilder", "(Ljava/lang/String;)J", @@ -107,6 +121,8 @@ static const JNINativeMethod gRuntimeXfermodeMethods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)RuntimeXfermode_updateIntUniforms}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeXfermode_updateChild}, + {"nativeUpdateColorFilter", "(JLjava/lang/String;J)V", + (void*)RuntimeXfermode_updateColorFilter}, }; int register_android_graphics_RuntimeXfermode(JNIEnv* env) { diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 018c2b1374d0..eadb9dea566f 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -1,5 +1,6 @@ #include +#include "ColorFilter.h" #include "Gainmap.h" #include "GraphicsJNI.h" #include "RuntimeEffectUtils.h" @@ -331,6 +332,15 @@ static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder builder->child(name.c_str()) = sk_ref_sp(shader); } +static void RuntimeShader_updateColorFilter(JNIEnv* env, jobject, jlong shaderBuilder, + jstring jUniformName, jlong colorFilterHandle) { + SkRuntimeShaderBuilder* builder = reinterpret_cast(shaderBuilder); + ScopedUtfChars name(env, jUniformName); + auto* childEffect = reinterpret_cast(colorFilterHandle); + + UpdateChild(env, builder, name.c_str(), childEffect->getInstance().release()); +} + static void RuntimeShader_updateChild(JNIEnv* env, jobject, jlong shaderBuilder, jstring jUniformName, jlong childHandle) { SkRuntimeShaderBuilder* builder = reinterpret_cast(shaderBuilder); @@ -380,6 +390,8 @@ static const JNINativeMethod gRuntimeShaderMethods[] = { {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)RuntimeShader_updateIntUniforms}, {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader}, + {"nativeUpdateColorFilter", "(JLjava/lang/String;J)V", + (void*)RuntimeShader_updateColorFilter}, {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateChild}, }; -- GitLab From fdf8b183263913c35200642cecdae8d882e7b9d9 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 15 Nov 2024 23:13:09 +0000 Subject: [PATCH 097/656] nfc(api): Add return status for pause/resume Polling APIs Bug: 375983875 Test: TH Change-Id: Ie56ef44561cdcc617ba1aeeb8970ccad1b0c079d --- nfc/api/system-current.txt | 6 ++- nfc/java/android/nfc/INfcAdapter.aidl | 4 +- nfc/java/android/nfc/NfcOemExtension.java | 45 ++++++++++++++++++++--- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 72447b1033d3..c5b22ac398d6 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -65,9 +65,9 @@ package android.nfc { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean isTagPresent(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overwriteRoutingTable(int, int, int, int); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void pausePolling(int); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int pausePolling(int); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); - method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void resumePolling(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int resumePolling(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAutoChangeEnabled(boolean); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void setControllerAlwaysOnMode(int); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); @@ -83,6 +83,8 @@ package android.nfc { field public static final int HCE_ACTIVATE = 1; // 0x1 field public static final int HCE_DATA_TRANSFERRED = 2; // 0x2 field public static final int HCE_DEACTIVATE = 3; // 0x3 + field public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; // 0x2 + field public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; // 0x1 field public static final int STATUS_OK = 0; // 0x0 field public static final int STATUS_UNKNOWN_ERROR = 1; // 0x1 } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index a08b55fe86b8..7ed6019c31b7 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -52,8 +52,8 @@ interface INfcAdapter int getState(); boolean disable(boolean saveState, in String pkg); boolean enable(in String pkg); - void pausePolling(int timeoutInMs); - void resumePolling(); + int pausePolling(int timeoutInMs); + int resumePolling(); void setForegroundDispatch(in PendingIntent intent, in IntentFilter[] filters, in TechListParcel techLists); diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 326ca6449c53..5be23fd900db 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -172,6 +172,31 @@ public final class NfcOemExtension { @Retention(RetentionPolicy.SOURCE) public @interface HostCardEmulationAction {} + /** + * Status code returned when the polling state change request succeeded. + * @see #pausePolling() + * @see #resumePolling() + */ + public static final int POLLING_STATE_CHANGE_SUCCEEDED = 1; + /** + * Status code returned when the polling state change request is already in + * required state. + * @see #pausePolling() + * @see #resumePolling() + */ + public static final int POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE = 2; + /** + * Possible status codes for {@link #pausePolling()} and + * {@link #resumePolling()}. + * @hide + */ + @IntDef(value = { + POLLING_STATE_CHANGE_SUCCEEDED, + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PollingStateChangeStatusCode {} + /** * Status OK */ @@ -644,24 +669,32 @@ public final class NfcOemExtension { /** * Pauses NFC tag reader mode polling for a {@code timeoutInMs} millisecond. - * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely - * use {@link #resumePolling()} to resume the polling. + * In case of {@code timeoutInMs} is zero or invalid polling will be stopped indefinitely. + * Use {@link #resumePolling() to resume the polling. * @param timeoutInMs the pause polling duration in millisecond, ranging from 0 to 40000. + * @return status of the operation + * @throws IllegalArgumentException if timeoutInMs value is invalid + * (0 < timeoutInMs < max). */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void pausePolling(@DurationMillisLong int timeoutInMs) { - NfcAdapter.callService(() -> NfcAdapter.sService.pausePolling(timeoutInMs)); + public @PollingStateChangeStatusCode int pausePolling(@DurationMillisLong int timeoutInMs) { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sService.pausePolling(timeoutInMs), + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); } /** * Resumes default NFC tag reader mode polling for the current device state if polling is * paused. Calling this while already in polling is a no-op. + * @return status of the operation */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public void resumePolling() { - NfcAdapter.callService(() -> NfcAdapter.sService.resumePolling()); + public @PollingStateChangeStatusCode int resumePolling() { + return NfcAdapter.callServiceReturn(() -> + NfcAdapter.sService.resumePolling(), + POLLING_STATE_CHANGE_ALREADY_IN_REQUESTED_STATE); } /** -- GitLab From f604fc6aa81bbb42fc9c4bf8842b3012e13a945f Mon Sep 17 00:00:00 2001 From: Harshit Mahajan Date: Sat, 16 Nov 2024 00:00:41 +0000 Subject: [PATCH 098/656] Remove observer specific logic in PackageWatchdog PackageWatchdog should not directly call RescueParty APIs. Moving isRecoveryTriggeredReboot API to PackageWatchdog, as it can return true even if RollbackPackageHealthObserver triggers reboot. Bug: 289203818 Test: TH Flag: android.crashrecovery.flags.synchronous_reboot_in_rescue_party Change-Id: I26efd6731bad1419ccdbeba461b64c36b054425e --- .../com/android/server/PackageWatchdog.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index fbf51fd7cca7..cda2c216fe20 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -1297,7 +1297,7 @@ public class PackageWatchdog { /** Dump status of every observer in mAllObservers. */ public void dump(@NonNull PrintWriter pw) { - if (Flags.synchronousRebootInRescueParty() && RescueParty.isRecoveryTriggeredReboot()) { + if (Flags.synchronousRebootInRescueParty() && isRecoveryTriggeredReboot()) { dumpInternal(pw); } else { synchronized (mLock) { @@ -1306,6 +1306,24 @@ public class PackageWatchdog { } } + /** + * Check if we're currently attempting to reboot during mitigation. This method must return + * true if triggered reboot early during a boot loop, since the device will not be fully booted + * at this time. + * @hide + */ + public static boolean isRecoveryTriggeredReboot() { + return isFactoryResetPropertySet() || isRebootPropertySet(); + } + + private static boolean isFactoryResetPropertySet() { + return CrashRecoveryProperties.attemptingFactoryReset().orElse(false); + } + + private static boolean isRebootPropertySet() { + return CrashRecoveryProperties.attemptingReboot().orElse(false); + } + private void dumpInternal(@NonNull PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("Package Watchdog status"); -- GitLab From f411ed37b35c5ad7c1740d95d33a48e24382a3a5 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Fri, 15 Nov 2024 07:41:58 +0800 Subject: [PATCH 099/656] [Catalyst] Support flags to get preference graph Bug: 373895596 Flag: EXEMPT library Test: manual Change-Id: Ie4f3c7b1f7543c444cbc66877014e3346aa7a3e4 --- .../graph/GetPreferenceGraphApiHandler.kt | 10 +- .../graph/PreferenceGetterFlags.kt | 31 +++ .../graph/PreferenceGraphBuilder.kt | 237 ++++++++++-------- 3 files changed, 176 insertions(+), 102 deletions(-) create mode 100644 packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt index 088bef230374..7aece5185800 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt @@ -42,7 +42,7 @@ abstract class GetPreferenceGraphApiHandler( callingUid: Int, request: GetPreferenceGraphRequest, ): PreferenceGraphProto { - val builder = PreferenceGraphBuilder.of(application, request) + val builder = PreferenceGraphBuilder.of(application, myUid, callingUid, request) if (request.screenKeys.isEmpty()) { for (key in PreferenceScreenRegistry.preferenceScreens.keys) { builder.addPreferenceScreenFromRegistry(key) @@ -68,16 +68,18 @@ constructor( val screenKeys: Set = setOf(), val visitedScreens: Set = setOf(), val locale: Locale? = null, - val includeValue: Boolean = true, + val flags: Int = PreferenceGetterFlags.ALL, + val includeValue: Boolean = true, // TODO: clean up val includeValueDescriptor: Boolean = true, ) object GetPreferenceGraphRequestCodec : MessageCodec { override fun encode(data: GetPreferenceGraphRequest): Bundle = - Bundle(3).apply { + Bundle(4).apply { putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray()) putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray()) putString(KEY_LOCALE, data.locale?.toLanguageTag()) + putInt(KEY_FLAGS, data.flags) } override fun decode(data: Bundle): GetPreferenceGraphRequest { @@ -88,12 +90,14 @@ object GetPreferenceGraphRequestCodec : MessageCodec screenKeys.toSet(), visitedScreens.toSet(), data.getString(KEY_LOCALE).toLocale(), + data.getInt(KEY_FLAGS), ) } private const val KEY_SCREEN_KEYS = "k" private const val KEY_VISITED_KEYS = "v" private const val KEY_LOCALE = "l" + private const val KEY_FLAGS = "f" } object PreferenceGraphProtoCodec : MessageCodec { diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt new file mode 100644 index 000000000000..632f154b72ab --- /dev/null +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.graph + +/** Flags for preference getter operation. */ +object PreferenceGetterFlags { + const val VALUE = 1 shl 0 + const val VALUE_DESCRIPTOR = 1 shl 1 + const val METADATA = 1 shl 2 + const val ALL = (1 shl 3) - 1 + + fun Int.includeValue() = (this and VALUE) != 0 + + fun Int.includeValueDescriptor() = (this and VALUE_DESCRIPTOR) != 0 + + fun Int.includeMetadata() = (this and METADATA) != 0 +} diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt index 6760e72be4a6..a65d24cf3d6f 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt @@ -31,6 +31,9 @@ import androidx.preference.Preference import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import androidx.preference.TwoStatePreference +import com.android.settingslib.graph.PreferenceGetterFlags.includeMetadata +import com.android.settingslib.graph.PreferenceGetterFlags.includeValue +import com.android.settingslib.graph.PreferenceGetterFlags.includeValueDescriptor import com.android.settingslib.graph.proto.PreferenceGraphProto import com.android.settingslib.graph.proto.PreferenceGroupProto import com.android.settingslib.graph.proto.PreferenceProto @@ -41,7 +44,6 @@ import com.android.settingslib.metadata.BooleanValue import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceHierarchy -import com.android.settingslib.metadata.PreferenceHierarchyNode import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceRestrictionProvider import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider @@ -50,6 +52,7 @@ import com.android.settingslib.metadata.PreferenceScreenRegistry import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.metadata.RangeValue +import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.preference.PreferenceScreenFactory import com.android.settingslib.preference.PreferenceScreenProvider import java.util.Locale @@ -60,14 +63,17 @@ private const val TAG = "PreferenceGraphBuilder" /** Builder of preference graph. */ class PreferenceGraphBuilder -private constructor(private val context: Context, private val request: GetPreferenceGraphRequest) { +private constructor( + private val context: Context, + private val myUid: Int, + private val callingUid: Int, + private val request: GetPreferenceGraphRequest, +) { private val preferenceScreenFactory by lazy { PreferenceScreenFactory(context.ofLocale(request.locale)) } private val builder by lazy { PreferenceGraphProto.newBuilder() } private val visitedScreens = mutableSetOf().apply { addAll(request.visitedScreens) } - private val includeValue = request.includeValue - private val includeValueDescriptor = request.includeValueDescriptor private suspend fun init() { for (key in request.screenKeys) { @@ -229,7 +235,7 @@ private constructor(private val context: Context, private val request: GetPrefer enabled = isEnabled available = isVisible persistent = isPersistent - if (includeValue && isPersistent && this@toProto is TwoStatePreference) { + if (request.flags.includeValue() && isPersistent && this@toProto is TwoStatePreference) { value = preferenceValueProto { booleanValue = this@toProto.isChecked } } this@toProto.fragment.toActionTarget(preferenceExtras)?.let { @@ -243,14 +249,14 @@ private constructor(private val context: Context, private val request: GetPrefer screenMetadata: PreferenceScreenMetadata, isRoot: Boolean, ): PreferenceGroupProto = preferenceGroupProto { - preference = toProto(screenMetadata, this@toProto, isRoot) + preference = toProto(screenMetadata, this@toProto.metadata, isRoot) forEachAsync { addPreferences( preferenceOrGroupProto { if (it is PreferenceHierarchy) { group = it.toProto(screenMetadata, false) } else { - preference = toProto(screenMetadata, it, false) + preference = toProto(screenMetadata, it.metadata, false) } } ) @@ -259,93 +265,19 @@ private constructor(private val context: Context, private val request: GetPrefer private suspend fun toProto( screenMetadata: PreferenceScreenMetadata, - node: PreferenceHierarchyNode, + metadata: PreferenceMetadata, isRoot: Boolean, - ) = preferenceProto { - val metadata = node.metadata - key = metadata.key - metadata.getTitleTextProto(isRoot)?.let { title = it } - if (metadata.summary != 0) { - summary = textProto { resourceId = metadata.summary } - } else { - (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let { - summary = textProto { string = it.toString() } - } - } - val metadataIcon = metadata.getPreferenceIcon(context) - if (metadataIcon != 0) icon = metadataIcon - if (metadata.keywords != 0) keywords = metadata.keywords - val preferenceExtras = metadata.extras(context) - preferenceExtras?.let { extras = it.toProto() } - indexable = metadata.isIndexable(context) - enabled = metadata.isEnabled(context) - if (metadata is PreferenceAvailabilityProvider) { - available = metadata.isAvailable(context) - } - if (metadata is PreferenceRestrictionProvider) { - restricted = metadata.isRestricted(context) - } - persistent = metadata.isPersistent(context) - if (persistent) { - if (includeValue && metadata is PersistentPreference<*>) { - value = preferenceValueProto { - when (metadata) { - is BooleanValue -> - metadata - .storage(context) - .getValue(metadata.key, Boolean::class.javaObjectType) - ?.let { booleanValue = it } - is RangeValue -> { - metadata - .storage(context) - .getValue(metadata.key, Int::class.javaObjectType) - ?.let { intValue = it } - } - else -> {} - } - } + ) = + metadata.toProto(context, myUid, callingUid, screenMetadata, isRoot, request.flags).also { + if (metadata is PreferenceScreenMetadata) { + @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata) } - if (includeValueDescriptor) { - valueDescriptor = preferenceValueDescriptorProto { - when (metadata) { - is BooleanValue -> booleanType = true - is RangeValue -> rangeValue = rangeValueProto { - min = metadata.getMinValue(context) - max = metadata.getMaxValue(context) - step = metadata.getIncrementStep(context) - } - else -> {} - } + metadata.intent(context)?.resolveActivity(context.packageManager)?.let { + if (it.packageName == context.packageName) { + add(it.className) } } } - if (metadata is PreferenceScreenMetadata) { - @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata) - } - metadata.intent(context)?.let { actionTarget = it.toActionTarget() } - screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() } - } - - private fun PreferenceMetadata.getTitleTextProto(isRoot: Boolean): TextProto? { - if (isRoot && this is PreferenceScreenMetadata) { - val titleRes = screenTitle - if (titleRes != 0) { - return textProto { resourceId = titleRes } - } else { - getScreenTitle(context)?.let { - return textProto { string = it.toString() } - } - } - } else { - val titleRes = title - if (titleRes != 0) { - return textProto { resourceId = titleRes } - } - } - return (this as? PreferenceTitleProvider)?.getTitle(context)?.let { - textProto { string = it.toString() } - } - } private suspend fun String?.toActionTarget(extras: Bundle?): ActionTarget? { if (this.isNullOrEmpty()) return null @@ -399,22 +331,129 @@ private constructor(private val context: Context, private val request: GetPrefer return null } - private suspend fun Intent.toActionTarget(): ActionTarget { - if (component?.packageName == "") { - setClassName(context, component!!.className) + private suspend fun Intent.toActionTarget() = + toActionTarget(context).also { + resolveActivity(context.packageManager)?.let { + if (it.packageName == context.packageName) { + add(it.className) + } + } } - resolveActivity(context.packageManager)?.let { - if (it.packageName == context.packageName) { - add(it.className) + + companion object { + suspend fun of( + context: Context, + myUid: Int, + callingUid: Int, + request: GetPreferenceGraphRequest, + ) = PreferenceGraphBuilder(context, myUid, callingUid, request).also { it.init() } + } +} + +fun PreferenceMetadata.toProto( + context: Context, + myUid: Int, + callingUid: Int, + screenMetadata: PreferenceScreenMetadata, + isRoot: Boolean, + flags: Int, +) = preferenceProto { + val metadata = this@toProto + key = metadata.key + if (flags.includeMetadata()) { + metadata.getTitleTextProto(context, isRoot)?.let { title = it } + if (metadata.summary != 0) { + summary = textProto { resourceId = metadata.summary } + } else { + (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let { + summary = textProto { string = it.toString() } } } - return actionTargetProto { intent = toProto() } + val metadataIcon = metadata.getPreferenceIcon(context) + if (metadataIcon != 0) icon = metadataIcon + if (metadata.keywords != 0) keywords = metadata.keywords + val preferenceExtras = metadata.extras(context) + preferenceExtras?.let { extras = it.toProto() } + indexable = metadata.isIndexable(context) + enabled = metadata.isEnabled(context) + if (metadata is PreferenceAvailabilityProvider) { + available = metadata.isAvailable(context) + } + if (metadata is PreferenceRestrictionProvider) { + restricted = metadata.isRestricted(context) + } + metadata.intent(context)?.let { actionTarget = it.toActionTarget(context) } + screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() } } + persistent = metadata.isPersistent(context) + if (persistent) { + if ( + flags.includeValue() && + enabled && + (!hasAvailable() || available) && + (!hasRestricted() || !restricted) && + metadata is PersistentPreference<*> && + metadata.getReadPermit(context, myUid, callingUid) == ReadWritePermit.ALLOW + ) { + value = preferenceValueProto { + when (metadata) { + is BooleanValue -> + metadata + .storage(context) + .getValue(metadata.key, Boolean::class.javaObjectType) + ?.let { booleanValue = it } + is RangeValue -> { + metadata + .storage(context) + .getValue(metadata.key, Int::class.javaObjectType) + ?.let { intValue = it } + } + else -> {} + } + } + } + if (flags.includeValueDescriptor()) { + valueDescriptor = preferenceValueDescriptorProto { + when (metadata) { + is BooleanValue -> booleanType = true + is RangeValue -> rangeValue = rangeValueProto { + min = metadata.getMinValue(context) + max = metadata.getMaxValue(context) + step = metadata.getIncrementStep(context) + } + else -> {} + } + } + } + } +} - companion object { - suspend fun of(context: Context, request: GetPreferenceGraphRequest) = - PreferenceGraphBuilder(context, request).also { it.init() } +private fun PreferenceMetadata.getTitleTextProto(context: Context, isRoot: Boolean): TextProto? { + if (isRoot && this is PreferenceScreenMetadata) { + val titleRes = screenTitle + if (titleRes != 0) { + return textProto { resourceId = titleRes } + } else { + getScreenTitle(context)?.let { + return textProto { string = it.toString() } + } + } + } else { + val titleRes = title + if (titleRes != 0) { + return textProto { resourceId = titleRes } + } + } + return (this as? PreferenceTitleProvider)?.getTitle(context)?.let { + textProto { string = it.toString() } + } +} + +private fun Intent.toActionTarget(context: Context): ActionTarget { + if (component?.packageName == "") { + setClassName(context, component!!.className) } + return actionTargetProto { intent = toProto() } } @SuppressLint("AppBundleLocaleChanges") -- GitLab From 26474cbde7646f6ca6e07bb9dd2500a8f52c09eb Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Fri, 15 Nov 2024 08:13:40 +0800 Subject: [PATCH 100/656] [Catalyst] Provide preference Getter API Batch GET is supported. Bug: 373895596 Flag: EXEMPT library Test: manual Change-Id: I89e33e5300ffac16e4e0bc9545ae424f91c826c1 --- .../settingslib/graph/PreferenceCoordinate.kt | 45 ++++++ .../settingslib/graph/PreferenceGetterApi.kt | 148 ++++++++++++++++++ .../graph/PreferenceGetterCodecs.kt | 126 +++++++++++++++ .../settingslib/service/PreferenceService.kt | 6 + .../service/ServiceApiConstants.kt | 3 + 5 files changed, 328 insertions(+) create mode 100644 packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt create mode 100644 packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt create mode 100644 packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt new file mode 100644 index 000000000000..68aa2d258295 --- /dev/null +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceCoordinate.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.graph + +import android.os.Parcel +import android.os.Parcelable + +/** + * Coordinate to locate a preference. + * + * Within an app, the preference screen key (unique among screens) plus preference key (unique on + * the screen) is used to locate a preference. + */ +data class PreferenceCoordinate(val screenKey: String, val key: String) : Parcelable { + + constructor(parcel: Parcel) : this(parcel.readString()!!, parcel.readString()!!) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(screenKey) + parcel.writeString(key) + } + + override fun describeContents() = 0 + + companion object CREATOR : Parcelable.Creator { + + override fun createFromParcel(parcel: Parcel) = PreferenceCoordinate(parcel) + + override fun newArray(size: Int) = arrayOfNulls(size) + } +} diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt new file mode 100644 index 000000000000..c8453efb9161 --- /dev/null +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.graph + +import android.app.Application +import androidx.annotation.IntDef +import com.android.settingslib.graph.proto.PreferenceProto +import com.android.settingslib.ipc.ApiDescriptor +import com.android.settingslib.ipc.ApiHandler +import com.android.settingslib.ipc.ApiPermissionChecker +import com.android.settingslib.metadata.PreferenceHierarchyNode +import com.android.settingslib.metadata.PreferenceScreenRegistry + +/** + * Request to get preference information. + * + * @param preferences coordinate of preferences + * @param flags a combination of constants in [PreferenceGetterFlags] + */ +class PreferenceGetterRequest(val preferences: Array, val flags: Int) + +/** Error code of preference getter request. */ +@Target(AnnotationTarget.TYPE) +@IntDef( + PreferenceGetterErrorCode.NOT_FOUND, + PreferenceGetterErrorCode.DISALLOW, + PreferenceGetterErrorCode.INTERNAL_ERROR, +) +@Retention(AnnotationRetention.SOURCE) +annotation class PreferenceGetterErrorCode { + companion object { + /** Preference is not found. */ + const val NOT_FOUND = 1 + /** Disallow to get preference value (e.g. uid not allowed). */ + const val DISALLOW = 2 + /** Internal error happened when get preference information. */ + const val INTERNAL_ERROR = 3 + + fun getMessage(code: Int) = + when (code) { + NOT_FOUND -> "Preference not found" + DISALLOW -> "Disallow to get preference value" + INTERNAL_ERROR -> "Internal error" + else -> "Unknown error" + } + } +} + +/** Response of the getter API. */ +class PreferenceGetterResponse( + val errors: Map, + val preferences: Map, +) + +/** Preference getter API descriptor. */ +class PreferenceGetterApiDescriptor(override val id: Int) : + ApiDescriptor { + + override val requestCodec = PreferenceGetterRequestCodec() + + override val responseCodec = PreferenceGetterResponseCodec() +} + +/** Preference getter API implementation. */ +class PreferenceGetterApiHandler( + override val id: Int, + private val permissionChecker: ApiPermissionChecker, +) : ApiHandler { + + override fun hasPermission( + application: Application, + myUid: Int, + callingUid: Int, + request: PreferenceGetterRequest, + ) = permissionChecker.hasPermission(application, myUid, callingUid, request) + + override suspend fun invoke( + application: Application, + myUid: Int, + callingUid: Int, + request: PreferenceGetterRequest, + ): PreferenceGetterResponse { + val errors = mutableMapOf() + val preferences = mutableMapOf() + val flags = request.flags + for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) { + val screenMetadata = PreferenceScreenRegistry[screenKey] + if (screenMetadata == null) { + for (coordinate in coordinates) { + errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND + } + continue + } + val nodes = mutableMapOf() + for (coordinate in coordinates) nodes[coordinate.key] = null + screenMetadata.getPreferenceHierarchy(application).forEachRecursively { + val metadata = it.metadata + val key = metadata.key + if (nodes.containsKey(key)) nodes[key] = it + } + for (coordinate in coordinates) { + val node = nodes[coordinate.key] + if (node == null) { + errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND + continue + } + val metadata = node.metadata + try { + val preferenceProto = + metadata.toProto( + application, + myUid, + callingUid, + screenMetadata, + metadata.key == screenMetadata.key, + flags, + ) + if (flags == PreferenceGetterFlags.VALUE && !preferenceProto.hasValue()) { + errors[coordinate] = PreferenceGetterErrorCode.DISALLOW + } else { + preferences[coordinate] = preferenceProto + } + } catch (e: Exception) { + errors[coordinate] = PreferenceGetterErrorCode.INTERNAL_ERROR + } + } + } + return PreferenceGetterResponse(errors, preferences) + } + + override val requestCodec = PreferenceGetterRequestCodec() + + override val responseCodec = PreferenceGetterResponseCodec() +} diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt new file mode 100644 index 000000000000..ff14eb5aae55 --- /dev/null +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterCodecs.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.graph + +import android.os.Bundle +import android.os.Parcel +import com.android.settingslib.graph.proto.PreferenceProto +import com.android.settingslib.ipc.MessageCodec +import java.util.Arrays + +/** Message codec for [PreferenceGetterRequest]. */ +class PreferenceGetterRequestCodec : MessageCodec { + override fun encode(data: PreferenceGetterRequest) = + Bundle(2).apply { + putParcelableArray(null, data.preferences) + putInt(FLAGS, data.flags) + } + + @Suppress("DEPRECATION") + override fun decode(data: Bundle): PreferenceGetterRequest { + data.classLoader = PreferenceCoordinate::class.java.classLoader + val array = data.getParcelableArray(null)!! + + return PreferenceGetterRequest( + Arrays.copyOf(array, array.size, Array::class.java), + data.getInt(FLAGS), + ) + } + + companion object { + private const val FLAGS = "f" + } +} + +/** Message codec for [PreferenceGetterResponse]. */ +class PreferenceGetterResponseCodec : MessageCodec { + override fun encode(data: PreferenceGetterResponse) = + Bundle(2).apply { + data.errors.toErrorsByteArray()?.let { putByteArray(ERRORS, it) } + data.preferences.toPreferencesByteArray()?.let { putByteArray(null, it) } + } + + private fun Map.toErrorsByteArray(): ByteArray? { + if (isEmpty()) return null + val parcel = Parcel.obtain() + parcel.writeInt(size) + for ((coordinate, code) in this) { + coordinate.writeToParcel(parcel, 0) + parcel.writeInt(code) + } + val bytes = parcel.marshall() + parcel.recycle() + return bytes + } + + private fun Map.toPreferencesByteArray(): ByteArray? { + if (isEmpty()) return null + val parcel = Parcel.obtain() + parcel.writeInt(size) + for ((coordinate, preferenceProto) in this) { + coordinate.writeToParcel(parcel, 0) + val data = preferenceProto.toByteArray() + parcel.writeInt(data.size) + parcel.writeByteArray(data) + } + val bytes = parcel.marshall() + parcel.recycle() + return bytes + } + + override fun decode(data: Bundle) = + PreferenceGetterResponse( + data.getByteArray(ERRORS).toErrors(), + data.getByteArray(null).toPreferences(), + ) + + private fun ByteArray?.toErrors(): Map { + if (this == null) return emptyMap() + val parcel = Parcel.obtain() + parcel.unmarshall(this, 0, size) + parcel.setDataPosition(0) + val count = parcel.readInt() + val errors = mutableMapOf() + repeat(count) { + val coordinate = PreferenceCoordinate(parcel) + errors[coordinate] = parcel.readInt() + } + parcel.recycle() + return errors + } + + private fun ByteArray?.toPreferences(): Map { + if (this == null) return emptyMap() + val parcel = Parcel.obtain() + parcel.unmarshall(this, 0, size) + parcel.setDataPosition(0) + val count = parcel.readInt() + val preferences = mutableMapOf() + repeat(count) { + val coordinate = PreferenceCoordinate(parcel) + val bytes = parcel.readInt() + val array = ByteArray(bytes).also { parcel.readByteArray(it) } + preferences[coordinate] = PreferenceProto.parseFrom(array) + } + parcel.recycle() + return preferences + } + + companion object { + private const val ERRORS = "e" + } +} diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt index ed748bb58a9a..7cb36db856eb 100644 --- a/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt +++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/PreferenceService.kt @@ -17,6 +17,8 @@ package com.android.settingslib.service import com.android.settingslib.graph.GetPreferenceGraphRequest +import com.android.settingslib.graph.PreferenceGetterApiHandler +import com.android.settingslib.graph.PreferenceGetterRequest import com.android.settingslib.graph.PreferenceSetterApiHandler import com.android.settingslib.graph.PreferenceSetterRequest import com.android.settingslib.ipc.ApiHandler @@ -37,6 +39,7 @@ open class PreferenceService( preferenceScreenProviders: Set> = setOf(), graphPermissionChecker: ApiPermissionChecker? = null, setterPermissionChecker: ApiPermissionChecker? = null, + getterPermissionChecker: ApiPermissionChecker? = null, vararg apiHandlers: ApiHandler<*, *>, ) : MessengerService( @@ -45,6 +48,9 @@ open class PreferenceService( setterPermissionChecker?.let { add(PreferenceSetterApiHandler(API_PREFERENCE_SETTER, it)) } + getterPermissionChecker?.let { + add(PreferenceGetterApiHandler(API_PREFERENCE_GETTER, it)) + } addAll(apiHandlers) }, permissionChecker, diff --git a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt index 7655daa6cf21..d71405e126ce 100644 --- a/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt +++ b/packages/SettingsLib/Service/src/com/android/settingslib/service/ServiceApiConstants.kt @@ -24,6 +24,9 @@ internal const val API_GET_PREFERENCE_GRAPH = 1 /** API id for preference value setter. */ internal const val API_PREFERENCE_SETTER = 2 +/** API id for preference getter. */ +internal const val API_PREFERENCE_GETTER = 3 + /** * The max API id reserved for internal preference service usages. Custom API id should start with * **1000** to avoid conflict. -- GitLab From b2b716e3db9c80b44a48b027b3bcd136b2970b3d Mon Sep 17 00:00:00 2001 From: Xiaowen Lei Date: Sat, 16 Nov 2024 00:19:44 +0000 Subject: [PATCH 101/656] Constrain Lockscreen smartspace to half screen for wide shade layout. Also applied Kotlin formatting fixes. (Diff patch 1 and 2.) Flag: EXEMPT bugfix Fix: 375243612 Test: trigger sports card, check smartspace when clock is centered. Test: SmartspaceSectionTest Test: KeyguardSmartspaceViewModelTest Change-Id: Ie52269fed4ee0d849068157391c409fb18ce9595 --- .../layout/sections/SmartspaceSectionTest.kt | 26 ++++++++++++++++++- .../KeyguardSmartspaceViewModelTest.kt | 23 ++++++++++++++++ .../view/layout/sections/SmartspaceSection.kt | 25 ++++++++---------- .../viewmodel/KeyguardSmartspaceViewModel.kt | 11 +++++--- .../KeyguardSmartspaceViewModelKosmos.kt | 2 ++ 5 files changed, 68 insertions(+), 19 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt index d94c97af6f14..c0db95f9e5d2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteract import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel +import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.util.mockito.any @@ -68,6 +69,7 @@ class SmartspaceSectionTest : SysuiTestCase() { private val clockShouldBeCentered = MutableStateFlow(false) private val hasCustomWeatherDataDisplay = MutableStateFlow(false) private val isWeatherVisibleFlow = MutableStateFlow(false) + private val isShadeLayoutWide = MutableStateFlow(false) @Before fun setup() { @@ -80,7 +82,7 @@ class SmartspaceSectionTest : SysuiTestCase() { keyguardSmartspaceInteractor, lockscreenSmartspaceController, keyguardUnlockAnimationController, - blueprintInteractor + blueprintInteractor, ) constraintLayout = ConstraintLayout(mContext) whenever(lockscreenSmartspaceController.buildAndConnectView(any())) @@ -93,6 +95,7 @@ class SmartspaceSectionTest : SysuiTestCase() { whenever(keyguardClockViewModel.clockShouldBeCentered).thenReturn(clockShouldBeCentered) whenever(keyguardSmartspaceViewModel.isSmartspaceEnabled).thenReturn(true) whenever(keyguardSmartspaceViewModel.isWeatherVisible).thenReturn(isWeatherVisibleFlow) + whenever(keyguardSmartspaceViewModel.isShadeLayoutWide).thenReturn(isShadeLayoutWide) constraintSet = ConstraintSet() } @@ -124,6 +127,26 @@ class SmartspaceSectionTest : SysuiTestCase() { assert(dateView.parent == null) } + @Test + fun testConstraintsWhenShadeLayoutIsNotWide() { + underTest.addViews(constraintLayout) + underTest.applyConstraints(constraintSet) + + val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id) + assertThat(smartspaceConstraints.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID) + } + + @Test + fun testConstraintsWhenShadeLayoutIsWide() { + isShadeLayoutWide.value = true + + underTest.addViews(constraintLayout) + underTest.applyConstraints(constraintSet) + + val smartspaceConstraints = constraintSet.getConstraint(smartspaceView.id) + assertThat(smartspaceConstraints.layout.endToEnd).isEqualTo(R.id.split_shade_guideline) + } + @Test fun testConstraintsWhenNotHasCustomWeatherDataDisplay() { whenever(keyguardSmartspaceViewModel.isDateWeatherDecoupled).thenReturn(true) @@ -160,6 +183,7 @@ class SmartspaceSectionTest : SysuiTestCase() { assertThat(constraintSet.getVisibility(weatherView.id)).isEqualTo(GONE) assertThat(constraintSet.getVisibility(dateView.id)).isEqualTo(VISIBLE) } + @Test fun testCustomDateWeatherVisibility() { hasCustomWeatherDataDisplay.value = true diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt index 0c3fcb3ef759..adce9d65cbe0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyguard.data.repository.keyguardSmartspaceRepositor import com.android.systemui.keyguard.shared.model.ClockSize import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.clocks.ClockController +import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -96,4 +97,26 @@ class KeyguardSmartspaceViewModelTest : SysuiTestCase() { assertThat(isWeatherVisible).isEqualTo(false) } + + @Test + fun isShadeLayoutWide_withConfigTrue_true() = + with(kosmos) { + testScope.runTest { + val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) + shadeRepository.setShadeLayoutWide(true) + + assertThat(isShadeLayoutWide).isTrue() + } + } + + @Test + fun isShadeLayoutWide_withConfigFalse_false() = + with(kosmos) { + testScope.runTest { + val isShadeLayoutWide by collectLastValue(underTest.isShadeLayoutWide) + shadeRepository.setShadeLayoutWide(false) + + assertThat(isShadeLayoutWide).isFalse() + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt index 6ddcae38ce92..29b64a74bf19 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt @@ -123,7 +123,7 @@ constructor( ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, - horizontalPaddingStart + horizontalPaddingStart, ) // migrate addSmartspaceView from KeyguardClockSwitchController @@ -134,15 +134,15 @@ constructor( ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START, - horizontalPaddingStart + horizontalPaddingStart, ) connect( sharedR.id.bc_smartspace_view, ConstraintSet.END, - if (keyguardClockViewModel.clockShouldBeCentered.value) ConstraintSet.PARENT_ID - else R.id.split_shade_guideline, + if (keyguardSmartspaceViewModel.isShadeLayoutWide.value) R.id.split_shade_guideline + else ConstraintSet.PARENT_ID, ConstraintSet.END, - horizontalPaddingEnd + horizontalPaddingEnd, ) if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) { @@ -151,7 +151,7 @@ constructor( sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM, sharedR.id.bc_smartspace_view, - ConstraintSet.TOP + ConstraintSet.TOP, ) } else { clear(sharedR.id.date_smartspace_view, ConstraintSet.BOTTOM) @@ -159,13 +159,13 @@ constructor( sharedR.id.date_smartspace_view, ConstraintSet.TOP, customR.id.lockscreen_clock_view, - ConstraintSet.BOTTOM + ConstraintSet.BOTTOM, ) connect( sharedR.id.bc_smartspace_view, ConstraintSet.TOP, sharedR.id.date_smartspace_view, - ConstraintSet.BOTTOM + ConstraintSet.BOTTOM, ) } @@ -173,10 +173,7 @@ constructor( R.id.smart_space_barrier_bottom, Barrier.BOTTOM, 0, - *intArrayOf( - sharedR.id.bc_smartspace_view, - sharedR.id.date_smartspace_view, - ) + *intArrayOf(sharedR.id.bc_smartspace_view, sharedR.id.date_smartspace_view), ) } updateVisibility(constraintSet) @@ -211,7 +208,7 @@ constructor( setVisibility(sharedR.id.weather_smartspace_view, weatherVisibility) setAlpha( sharedR.id.weather_smartspace_view, - if (weatherVisibility == View.VISIBLE) 1f else 0f + if (weatherVisibility == View.VISIBLE) 1f else 0f, ) val dateVisibility = if (keyguardClockViewModel.hasCustomWeatherDataDisplay.value) ConstraintSet.GONE @@ -219,7 +216,7 @@ constructor( setVisibility(sharedR.id.date_smartspace_view, dateVisibility) setAlpha( sharedR.id.date_smartspace_view, - if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f + if (dateVisibility == ConstraintSet.VISIBLE) 1f else 0f, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt index e30ddc69b19d..8cc01041e71f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModel.kt @@ -21,6 +21,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor import com.android.systemui.res.R +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -38,6 +39,7 @@ constructor( smartspaceController: LockscreenSmartspaceController, keyguardClockViewModel: KeyguardClockViewModel, smartspaceInteractor: KeyguardSmartspaceInteractor, + shadeInteractor: ShadeInteractor, ) { /** Whether the smartspace section is available in the build. */ val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled @@ -59,10 +61,9 @@ constructor( /** Whether the weather area should be visible. */ val isWeatherVisible: StateFlow = - combine( + combine(isWeatherEnabled, keyguardClockViewModel.hasCustomWeatherDataDisplay) { isWeatherEnabled, - keyguardClockViewModel.hasCustomWeatherDataDisplay, - ) { isWeatherEnabled, clockIncludesCustomWeatherDisplay -> + clockIncludesCustomWeatherDisplay -> isWeatherVisible( clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay, isWeatherEnabled = isWeatherEnabled, @@ -76,7 +77,7 @@ constructor( clockIncludesCustomWeatherDisplay = keyguardClockViewModel.hasCustomWeatherDataDisplay.value, isWeatherEnabled = smartspaceInteractor.isWeatherEnabled.value, - ) + ), ) private fun isWeatherVisible( @@ -89,6 +90,8 @@ constructor( /* trigger clock and smartspace constraints change when smartspace appears */ val bcSmartspaceVisibility: StateFlow = smartspaceInteractor.bcSmartspaceVisibility + val isShadeLayoutWide: StateFlow = shadeInteractor.isShadeLayoutWide + companion object { fun getSmartspaceStartMargin(context: Context): Int { return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) + diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt index d33d594d9e8a..76e2cc8b7bd0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.util.mockito.mock val Kosmos.keyguardSmartspaceViewModel by @@ -28,5 +29,6 @@ val Kosmos.keyguardSmartspaceViewModel by smartspaceController = mock(), keyguardClockViewModel = keyguardClockViewModel, smartspaceInteractor = keyguardSmartspaceInteractor, + shadeInteractor = shadeInteractor, ) } -- GitLab From 41d4ea0d249a5013e22874c58b3d554002761084 Mon Sep 17 00:00:00 2001 From: Harshit Mahajan Date: Sat, 16 Nov 2024 01:01:08 +0000 Subject: [PATCH 102/656] Create isRecoveryTriggeredReboot SystemApi Unhiding isRecoveryTriggeredReboot from PackageWatchdog Bug: 361126781 Test: TH Flag: android.crashrecovery.flags.enable_crashrecovery Change-Id: Ic9e99eefc38dbf093af879715d6681d0c842f7a2 --- .../services/module/java/com/android/server/PackageWatchdog.java | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index cda2c216fe20..b42fcba1b593 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -1310,7 +1310,6 @@ public class PackageWatchdog { * Check if we're currently attempting to reboot during mitigation. This method must return * true if triggered reboot early during a boot loop, since the device will not be fully booted * at this time. - * @hide */ public static boolean isRecoveryTriggeredReboot() { return isFactoryResetPropertySet() || isRebootPropertySet(); -- GitLab From 425c66fcb57b534bf48ade74a2feca2847fd3824 Mon Sep 17 00:00:00 2001 From: Xiaowen Lei Date: Sat, 16 Nov 2024 01:03:55 +0000 Subject: [PATCH 103/656] Also invoke "setProgressTrackerIcon" when mTrackerIcon is null. This is necessary when the app is updating the same notification id. If they want to remove the tracker icon for some reason, we should respect their wish. Flag: android.app.api_rich_ongoing Bug: 379359830 Test: post a ProgressStyle notification with tracker icon, then set the tracker icon to null and update the same notification id. Change-Id: Iafa37cf6b920238d7ede49785785e34411242c41 --- core/java/android/app/Notification.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 3d9c55c0f37a..47cade8a4ca4 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -11527,11 +11527,9 @@ public class Notification implements Parcelable contentView.setBundle(R.id.progress, "setProgressModel", model.toBundle()); - if (mTrackerIcon != null) { - contentView.setIcon(R.id.progress, - "setProgressTrackerIcon", - mTrackerIcon); - } + contentView.setIcon(R.id.progress, + "setProgressTrackerIcon", + mTrackerIcon); return contentView; } -- GitLab From d538c4c0c62b7b7ecceb5868d3cbe3630fc1d0b4 Mon Sep 17 00:00:00 2001 From: Paul Colta Date: Thu, 17 Oct 2024 13:01:41 -0700 Subject: [PATCH 104/656] HDMI: Call CEC API in TIF for TV to assert active source Upon wake-up if TIF did not tune to a different HDMI input for a defined timeout it announces CEC to assert the TV as active source. Test: manual Bug: 373454466 Flag: android.media.tv.flags.hdmi_control_enhanced_behavior Change-Id: I8f5ce84a704a1c7b58e22a2d45e7c20f13519335 --- .../hdmi/RequestActiveSourceAction.java | 10 +- .../server/tv/TvInputManagerService.java | 96 ++++++++++++++++++- .../server/hdmi/HdmiCecLocalDeviceTvTest.java | 12 +-- 3 files changed, 105 insertions(+), 13 deletions(-) diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java index b0e93989d463..f9a1cebc952f 100644 --- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java +++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java @@ -25,8 +25,8 @@ import com.android.internal.annotations.VisibleForTesting; * Feature action that sends message and waits for . * * For TV panels, this action has a delay before sending . This is because it - * should wait for a possible request from LauncherX and can be cancelled if an - * message was received or the TV switched to another input. + * should wait for a possible request from LauncherX or TIF (TV Input Framework) and can be + * cancelled if an message was received or the TV switched to another input. */ public class RequestActiveSourceAction extends HdmiCecFeatureAction { private static final String TAG = "RequestActiveSourceAction"; @@ -40,9 +40,9 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { // Number of retries is sent if no device answers this message. private static final int MAX_SEND_RETRY_COUNT = 1; - // Timeout to wait for the LauncherX API call to be completed. + // Timeout to wait for LauncherX or TIF to call the CEC API. @VisibleForTesting - protected static final int TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS = 10000; + protected static final int TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS = 15000; private int mSendRetryCount = 0; @@ -67,7 +67,7 @@ public class RequestActiveSourceAction extends HdmiCecFeatureAction { // We wait for default timeout to allow the message triggered by the LauncherX API call to // be sent by the TV and another default timeout in case the message has to be answered // (e.g. TV sent a or ). - addTimer(mState, TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + addTimer(mState, TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); return true; } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 4589d26261dc..8bcf1a9be031 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -21,6 +21,7 @@ import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED; import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY; import static android.media.tv.flags.Flags.tifUnbindInactiveTis; import static android.media.tv.flags.Flags.kidsModeTvdbSharing; +import static android.media.tv.flags.Flags.hdmiControlEnhancedBehavior; import android.annotation.NonNull; import android.annotation.Nullable; @@ -44,8 +45,10 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.graphics.Rect; +import android.hardware.hdmi.HdmiClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; +import android.hardware.hdmi.HdmiTvClient; import android.media.AudioPresentation; import android.media.PlaybackParams; import android.media.tv.AdBuffer; @@ -138,6 +141,8 @@ public final class TvInputManagerService extends SystemService { private static final String PERMISSION_ACCESS_WATCHED_PROGRAMS = "com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"; private static final long UPDATE_HARDWARE_TIS_BINDING_DELAY_IN_MILLIS = 10 * 1000; // 10 seconds + private static final long SET_TV_AS_ACTIVE_SOURCE_IF_NO_REQUEST_DELAY_IN_MILLIS + = 10 * 1000; // 10 seconds // There are two different formats of DVB frontend devices. One is /dev/dvb%d.frontend%d, // another one is /dev/dvb/adapter%d/frontend%d. Followings are the patterns for selecting the @@ -185,6 +190,8 @@ public final class TvInputManagerService extends SystemService { private final HashSet mExternalInputLoggingDeviceOnScreenDisplayNames = new HashSet(); private final List mExternalInputLoggingDeviceBrandNames = new ArrayList(); + private HdmiControlManager mHdmiControlManager = null; + private HdmiTvClient mHdmiTvClient = null; public TvInputManagerService(Context context) { super(context); @@ -197,7 +204,12 @@ public final class TvInputManagerService extends SystemService { mActivityManager = (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); - + mHdmiControlManager = mContext.getSystemService(HdmiControlManager.class); + if (mHdmiControlManager == null) { + Slog.w(TAG, "HdmiControlManager is null!"); + } else { + mHdmiTvClient = mHdmiControlManager.getTvClient(); + } synchronized (mLock) { getOrCreateUserStateLocked(mCurrentUserId); } @@ -208,6 +220,42 @@ public final class TvInputManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.TV_INPUT_SERVICE, new BinderService()); + + if (!hdmiControlEnhancedBehavior()) { + return; + } + + // To ensure the TV claims CEC active source status correctly, a receiver is registered to + // monitor wake-up and sleep intents. Upon wake-up, this receiver sends a delayed message + // triggering a TIF call into a CEC API to claim TV as the active source. + // However, the API call is cancelled if the TV switches inputs or goes to sleep. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + switch (action) { + case Intent.ACTION_SCREEN_ON: + Slog.w(TAG, "The TV woke up."); + mMessageHandler.removeMessages( + MessageHandler.MSG_CHECK_TV_AS_ACTIVE_SOURCE); + Message msg = mMessageHandler + .obtainMessage(MessageHandler.MSG_CHECK_TV_AS_ACTIVE_SOURCE); + mMessageHandler.sendMessageDelayed(msg, + SET_TV_AS_ACTIVE_SOURCE_IF_NO_REQUEST_DELAY_IN_MILLIS); + break; + case Intent.ACTION_SCREEN_OFF: + Slog.w(TAG, "The TV turned off."); + mMessageHandler.removeMessages( + MessageHandler.MSG_CHECK_TV_AS_ACTIVE_SOURCE); + break; + default: + return; + } + } + }, filter); } @Override @@ -4503,6 +4551,7 @@ public final class TvInputManagerService extends SystemService { static final int MSG_LOG_WATCH_END = 2; static final int MSG_SWITCH_CONTENT_RESOLVER = 3; static final int MSG_UPDATE_HARDWARE_TIS_BINDING = 4; + static final int MSG_CHECK_TV_AS_ACTIVE_SOURCE = 5; private ContentResolver mContentResolver; @@ -4575,8 +4624,27 @@ public final class TvInputManagerService extends SystemService { args.recycle(); } break; + case MSG_CHECK_TV_AS_ACTIVE_SOURCE: + synchronized (mLock) { + if (mOnScreenInputId == null) { + assertTvAsCecActiveSourceLocked(); + break; + } + // TV that switched to a different input, but not an HDMI input + // (e.g. composite) can also assert active source. + UserState userState = getOrCreateUserStateLocked(mCurrentUserId); + TvInputState inputState = userState.inputMap.get(mOnScreenInputId); + if (inputState == null) { + Slog.w(TAG, "Unexpected null TvInputState."); + break; + } + if (inputState.info.getType() != TvInputInfo.TYPE_HDMI) { + assertTvAsCecActiveSourceLocked(); + } + } + break; default: { - Slog.w(TAG, "unhandled message code: " + msg.what); + Slog.w(TAG, "Unhandled message code: " + msg.what); break; } } @@ -4822,6 +4890,30 @@ public final class TvInputManagerService extends SystemService { } } + @GuardedBy("mLock") + private void assertTvAsCecActiveSourceLocked() { + if (mHdmiTvClient == null) { + Slog.w(TAG, "HdmiTvClient is null!"); + return; + } + mHdmiTvClient.selectDevice(HdmiDeviceInfo.DEVICE_TV, + mContext.getMainExecutor(), + new HdmiClient.OnDeviceSelectedListener() { + @Override + public void onDeviceSelected(int result, + int logicalAddress) { + if (result == HdmiControlManager.RESULT_SUCCESS) { + Slog.w(TAG, + "Setting TV as the active CEC device was successful."); + } else { + Slog.w(TAG, + "Setting TV as the active CEC device failed with result " + + result); + } + } + }); + } + private static class SessionNotFoundException extends IllegalArgumentException { public SessionNotFoundException(String name) { super(name); diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java index 51276a4db883..0816e7b61165 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java @@ -28,7 +28,7 @@ import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE; import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF; import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON; -import static com.android.server.hdmi.RequestActiveSourceAction.TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS; +import static com.android.server.hdmi.RequestActiveSourceAction.TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS; import static com.android.server.hdmi.RoutingControlAction.TIMEOUT_ROUTING_INFORMATION_MS; import static com.android.server.hdmi.RequestSadAction.RETRY_COUNTER_MAX; @@ -1877,7 +1877,7 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // Skip the LauncherX API timeout. - mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1910,7 +1910,7 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // Skip the LauncherX API timeout. - mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1946,7 +1946,7 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // Skip the LauncherX API timeout. - mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -1989,7 +1989,7 @@ public class HdmiCecLocalDeviceTvTest { mTestLooper.dispatchAll(); // Skip the LauncherX API timeout. - mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource); @@ -2026,7 +2026,7 @@ public class HdmiCecLocalDeviceTvTest { mHdmiControlService.sendCecCommand(setStreamPathFromTv); // Skip the LauncherX API timeout. - mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_LAUNCHERX_API_CALL_MS); + mTestLooper.moveTimeForward(TIMEOUT_WAIT_FOR_TV_ASSERT_ACTIVE_SOURCE_MS); mTestLooper.dispatchAll(); assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource); -- GitLab From fe1ab98b555c17564677dee013b14bd03051ba86 Mon Sep 17 00:00:00 2001 From: Steven Terrell Date: Wed, 13 Nov 2024 20:31:28 +0000 Subject: [PATCH 105/656] Add Merging Logic to Jank Processor This change will take JankStats that are collected from outside the platform and merge them into the list of pending stats. When a stat is being merged, it will either be merged into an existing stat that is currently waiting to be logged or create a net new stat. The determination to merge into an existing stat or create a new one is based off of a key which is a concatenated string of widget category, widget id and widget state. Bug: 377572463 Flag: android.app.jank.detailed_app_jank_metrics_api Test: atest CoreAppJankTestCases Change-Id: I15b261076107cf7192652eabaf037da46a750d12 --- .../app/jank/FrameOverrunHistogram.java | 2 +- .../android/app/jank/JankDataProcessor.java | 74 ++++++++++++++- core/java/android/app/jank/JankTracker.java | 7 +- core/java/android/app/jank/StateTracker.java | 6 +- .../app/jank/tests/JankDataProcessorTest.java | 92 +++++++++++++++++++ 5 files changed, 173 insertions(+), 8 deletions(-) diff --git a/core/java/android/app/jank/FrameOverrunHistogram.java b/core/java/android/app/jank/FrameOverrunHistogram.java index e28ac126a90a..3ad6531a46bf 100644 --- a/core/java/android/app/jank/FrameOverrunHistogram.java +++ b/core/java/android/app/jank/FrameOverrunHistogram.java @@ -39,7 +39,7 @@ public class FrameOverrunHistogram { * Create a new instance of FrameOverrunHistogram. */ public FrameOverrunHistogram() { - mBucketCounts = new int[sBucketEndpoints.length - 1]; + mBucketCounts = new int[sBucketEndpoints.length]; } /** diff --git a/core/java/android/app/jank/JankDataProcessor.java b/core/java/android/app/jank/JankDataProcessor.java index 7525d0402ee4..7ceaeb3fb070 100644 --- a/core/java/android/app/jank/JankDataProcessor.java +++ b/core/java/android/app/jank/JankDataProcessor.java @@ -59,7 +59,6 @@ public class JankDataProcessor { * @param appUid the uid of the app. */ public void processJankData(List jankData, String activityName, int appUid) { - mCurrentBatchCount++; // add all the previous and active states to the pending states list. mStateTracker.retrieveAllStates(mPendingStates); @@ -79,9 +78,8 @@ public class JankDataProcessor { } } // At this point we have attributed all frames to a state. - if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) { - logMetricCounts(); - } + incrementBatchCountAndMaybeLogStats(); + // return the StatData object back to the pool to be reused. jankDataProcessingComplete(); } @@ -91,7 +89,73 @@ public class JankDataProcessor { * stats */ public void mergeJankStats(AppJankStats jankStats, String activityName) { - // TODO b/377572463 Add Merging Logic + // Each state has a key which is a combination of widget category, widget id and widget + // state, this key is also used to identify pending stats, a pending stat is essentially a + // state with frames associated with it. + String stateKey = mStateTracker.getStateKey(jankStats.getWidgetCategory(), + jankStats.getWidgetId(), jankStats.getWidgetState()); + + if (mPendingJankStats.containsKey(stateKey)) { + mergeExistingStat(stateKey, jankStats); + } else { + mergeNewStat(stateKey, activityName, jankStats); + } + + incrementBatchCountAndMaybeLogStats(); + } + + private void mergeExistingStat(String stateKey, AppJankStats jankStat) { + PendingJankStat pendingStat = mPendingJankStats.get(stateKey); + + pendingStat.mJankyFrames += jankStat.getJankyFrameCount(); + pendingStat.mTotalFrames += jankStat.getTotalFrameCount(); + + mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets, + jankStat.getFrameOverrunHistogram().getBucketCounters()); + } + + private void mergeNewStat(String stateKey, String activityName, AppJankStats jankStats) { + // Check if we have space for a new stat + if (mPendingJankStats.size() > MAX_IN_MEMORY_STATS) { + return; + } + + PendingJankStat pendingStat = mPendingJankStatsPool.acquire(); + if (pendingStat == null) { + pendingStat = new PendingJankStat(); + + } + pendingStat.clearStats(); + + pendingStat.mActivityName = activityName; + pendingStat.mUid = jankStats.getUid(); + pendingStat.mWidgetId = jankStats.getWidgetId(); + pendingStat.mWidgetCategory = jankStats.getWidgetCategory(); + pendingStat.mWidgetState = jankStats.getWidgetState(); + pendingStat.mTotalFrames = jankStats.getTotalFrameCount(); + pendingStat.mJankyFrames = jankStats.getJankyFrameCount(); + + mergeOverrunHistograms(pendingStat.mFrameOverrunBuckets, + jankStats.getFrameOverrunHistogram().getBucketCounters()); + + mPendingJankStats.put(stateKey, pendingStat); + } + + private void mergeOverrunHistograms(int[] mergeTarget, int[] mergeSource) { + // The length of each histogram should be identical, if they are not then its possible the + // buckets are not in sync, these records should not be recorded. + if (mergeTarget.length != mergeSource.length) return; + + for (int i = 0; i < mergeTarget.length; i++) { + mergeTarget[i] += mergeSource[i]; + } + } + + private void incrementBatchCountAndMaybeLogStats() { + mCurrentBatchCount++; + if (mCurrentBatchCount >= LOG_BATCH_FREQUENCY) { + logMetricCounts(); + } } /** diff --git a/core/java/android/app/jank/JankTracker.java b/core/java/android/app/jank/JankTracker.java index 202281f98c97..469521668d25 100644 --- a/core/java/android/app/jank/JankTracker.java +++ b/core/java/android/app/jank/JankTracker.java @@ -89,7 +89,12 @@ public class JankTracker { * stats */ public void mergeAppJankStats(AppJankStats appJankStats) { - mJankDataProcessor.mergeJankStats(appJankStats, mActivityName); + getHandler().post(new Runnable() { + @Override + public void run() { + mJankDataProcessor.mergeJankStats(appJankStats, mActivityName); + } + }); } public void setActivityName(@NonNull String activityName) { diff --git a/core/java/android/app/jank/StateTracker.java b/core/java/android/app/jank/StateTracker.java index c86d5a5cff20..21bb5e8280ee 100644 --- a/core/java/android/app/jank/StateTracker.java +++ b/core/java/android/app/jank/StateTracker.java @@ -180,7 +180,11 @@ public class StateTracker { } } - private String getStateKey(String widgetCategory, String widgetId, String widgetState) { + /** + * Returns a concatenated string of the inputs. This key can be used to retrieve both pending + * stats and the state that was used to create the pending stat. + */ + public String getStateKey(String widgetCategory, String widgetId, String widgetState) { return widgetCategory + widgetId + widgetState; } diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java index 2cd625eec032..4d495adf727b 100644 --- a/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java +++ b/tests/AppJankTest/src/android/app/jank/tests/JankDataProcessorTest.java @@ -18,7 +18,9 @@ package android.app.jank.tests; import static org.junit.Assert.assertEquals; +import android.app.jank.AppJankStats; import android.app.jank.Flags; +import android.app.jank.FrameOverrunHistogram; import android.app.jank.JankDataProcessor; import android.app.jank.StateTracker; import android.platform.test.annotations.RequiresFlagsEnabled; @@ -39,6 +41,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; @RunWith(AndroidJUnit4.class) @@ -154,6 +157,73 @@ public class JankDataProcessorTest { assertEquals(totalFrames, histogramFrames); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API) + public void mergeAppJankStats_confirmStatAddedToPendingStats() { + HashMap pendingStats = + mJankDataProcessor.getPendingJankStats(); + + assertEquals(pendingStats.size(), 0); + + AppJankStats jankStats = getAppJankStats(); + mJankDataProcessor.mergeJankStats(jankStats, sActivityName); + + pendingStats = mJankDataProcessor.getPendingJankStats(); + + assertEquals(pendingStats.size(), 1); + } + + /** + * This test confirms matching states are combined into one pending stat. When JankStats are + * merged from outside the platform they will contain widget category, widget id and widget + * state. If an incoming JankStats matches a pending stat on all those fields the incoming + * JankStat will be merged into the existing stat. + */ + @Test + @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API) + public void mergeAppJankStats_confirmStatsWithMatchingStatesAreCombinedIntoOnePendingStat() { + AppJankStats jankStats = getAppJankStats(); + mJankDataProcessor.mergeJankStats(jankStats, sActivityName); + + HashMap pendingStats = + mJankDataProcessor.getPendingJankStats(); + assertEquals(pendingStats.size(), 1); + + AppJankStats secondJankStat = getAppJankStats(); + mJankDataProcessor.mergeJankStats(secondJankStat, sActivityName); + + pendingStats = mJankDataProcessor.getPendingJankStats(); + + assertEquals(pendingStats.size(), 1); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DETAILED_APP_JANK_METRICS_API) + public void mergeAppJankStats_whenStatsWithMatchingStatesMerge_confirmFrameCountsAdded() { + AppJankStats jankStats = getAppJankStats(); + mJankDataProcessor.mergeJankStats(jankStats, sActivityName); + mJankDataProcessor.mergeJankStats(jankStats, sActivityName); + + HashMap pendingStats = + mJankDataProcessor.getPendingJankStats(); + + String statKey = pendingStats.keySet().iterator().next(); + JankDataProcessor.PendingJankStat pendingStat = pendingStats.get(statKey); + + assertEquals(pendingStats.size(), 1); + // The same jankStats objects are merged twice, this should result in the frame counts being + // doubled. + assertEquals(jankStats.getJankyFrameCount() * 2, pendingStat.getJankyFrames()); + assertEquals(jankStats.getTotalFrameCount() * 2, pendingStat.getTotalFrames()); + + int[] originalHistogramBuckets = jankStats.getFrameOverrunHistogram().getBucketCounters(); + int[] frameOverrunBuckets = pendingStat.getFrameOverrunBuckets(); + + for (int i = 0; i < frameOverrunBuckets.length; i++) { + assertEquals(originalHistogramBuckets[i] * 2, frameOverrunBuckets[i]); + } + } + // TODO b/375005277 add tests that cover logging and releasing resources back to pool. private long getTotalFramesCounted() { @@ -276,4 +346,26 @@ public class JankDataProcessorTest { return mockData; } + private AppJankStats getAppJankStats() { + AppJankStats jankStats = new AppJankStats( + /*App Uid*/APP_ID, + /*Widget Id*/"test widget id", + /*Widget Category*/AppJankStats.SCROLL, + /*Widget State*/AppJankStats.SCROLLING, + /*Total Frames*/100, + /*Janky Frames*/25, + getOverrunHistogram() + ); + return jankStats; + } + + private FrameOverrunHistogram getOverrunHistogram() { + FrameOverrunHistogram overrunHistogram = new FrameOverrunHistogram(); + overrunHistogram.addFrameOverrunMillis(-2); + overrunHistogram.addFrameOverrunMillis(1); + overrunHistogram.addFrameOverrunMillis(5); + overrunHistogram.addFrameOverrunMillis(25); + return overrunHistogram; + } + } -- GitLab From ae505195b376c95f2c42505c9356ed19294cf714 Mon Sep 17 00:00:00 2001 From: Sherry Zhou Date: Wed, 13 Nov 2024 05:40:07 +0000 Subject: [PATCH 106/656] Fix large clock is too low in new customization picker Bug: 378795771 Flag: NONE bugfix Test: manual test, observe vertical position of large clock is same with lockscreen Change-Id: I16afafa7b7515606a0dbf9e45e19afb614cc6c0c --- .../ui/composable/section/LockSection.kt | 3 ++- .../customization/res/values-land/dimens.xml | 19 +++++++++++++++++++ .../res/values-sw600dp-land/dimens.xml | 1 + .../customization/res/values/dimens.xml | 1 + packages/SystemUI/res/values-land/dimens.xml | 1 - .../res/values-sw600dp-land/dimens.xml | 1 - packages/SystemUI/res/values/dimens.xml | 1 - .../sections/DefaultDeviceEntrySection.kt | 3 ++- 8 files changed, 25 insertions(+), 5 deletions(-) create mode 100644 packages/SystemUI/customization/res/values-land/dimens.xml diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt index 9390664d1283..597cbf24729b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.viewinterop.AndroidView import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.SceneScope import com.android.systemui.biometrics.AuthController +import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags @@ -147,7 +148,7 @@ constructor( } else { val scaleFactor = authController.scaleFactor val bottomPaddingPx = - context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) + context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom) val heightPx = windowViewBounds.bottom.toFloat() Pair( diff --git a/packages/SystemUI/customization/res/values-land/dimens.xml b/packages/SystemUI/customization/res/values-land/dimens.xml new file mode 100644 index 000000000000..50f220c882bd --- /dev/null +++ b/packages/SystemUI/customization/res/values-land/dimens.xml @@ -0,0 +1,19 @@ + + + + 24dp + \ No newline at end of file diff --git a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml index 18073adf9e7a..876028195ff3 100644 --- a/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/customization/res/values-sw600dp-land/dimens.xml @@ -17,4 +17,5 @@ 0dp 8dp + 60dp \ No newline at end of file diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index 041ae62670e5..21b4c7165226 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -40,4 +40,5 @@ 24dp 104dp 0dp + 74dp \ No newline at end of file diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 235015b5286f..70ae5c1832af 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -91,7 +91,6 @@ 128dp 8dp - 24dp 48dp diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 75bee9f9266a..055c3a641d1b 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -20,7 +20,6 @@ 25dp 115dp - 60dp 8dp diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 4954f9122788..b14257229119 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -936,7 +936,6 @@ -200dp 32dp - 74dp 71dp diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index b51bb7b229ee..aa7eb2933992 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -28,6 +28,7 @@ import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.biometrics.AuthController +import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -115,7 +116,7 @@ constructor( val scaleFactor: Float = authController.scaleFactor val mBottomPaddingPx = - context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom) + context.resources.getDimensionPixelSize(customR.dimen.lock_icon_margin_bottom) val bounds = windowManager.currentWindowMetrics.bounds var widthPixels = bounds.right.toFloat() if (featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) { -- GitLab From c59dfa99c7f0db28d7d352e1f6e0289498bafd59 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 15 Nov 2024 06:09:11 +0000 Subject: [PATCH 107/656] Remove unused legacy/hidden RecentTaskInfo fields - Nothing references a recent task's childTaskInfos or lastSnapshotData so we can remove this code and skip the work of storing the last snapshot metadata or computing task infos for all children Bug: 346588978 Flag: EXEMPT internal cleanup Test: atest RecentTasksTest Change-Id: Ie45b4056ae6d0fed20d27e24ae655927e6d79cf2 --- core/java/android/app/ActivityManager.java | 87 ------------------- .../com/android/server/wm/RecentTasks.java | 13 --- .../core/java/com/android/server/wm/Task.java | 59 ++----------- .../android/server/wm/RecentTasksTest.java | 58 ------------- 4 files changed, 8 insertions(+), 209 deletions(-) diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index ab75069cc5d8..33ba05865042 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2678,62 +2678,6 @@ public class ActivityManager { * started or visited. */ public static class RecentTaskInfo extends TaskInfo implements Parcelable { - /** - * @hide - */ - public static class PersistedTaskSnapshotData { - /** - * The bounds of the task when the last snapshot was taken, may be null if the task is - * not yet attached to the hierarchy. - * @see {@link android.window.TaskSnapshot#mTaskSize}. - * @hide - */ - public @Nullable Point taskSize; - - /** - * The content insets of the task when the task snapshot was taken. - * @see {@link android.window.TaskSnapshot#mContentInsets}. - * @hide - */ - public @Nullable Rect contentInsets; - - /** - * The size of the last snapshot taken, may be null if there is no associated snapshot. - * @see {@link android.window.TaskSnapshot#mSnapshot}. - * @hide - */ - public @Nullable Point bufferSize; - - /** - * Sets the data from the other data. - * @hide - */ - public void set(PersistedTaskSnapshotData other) { - taskSize = other.taskSize; - contentInsets = other.contentInsets; - bufferSize = other.bufferSize; - } - - /** - * Sets the data from the provided {@param snapshot}. - * @hide - */ - public void set(TaskSnapshot snapshot) { - if (snapshot == null) { - taskSize = null; - contentInsets = null; - bufferSize = null; - return; - } - final HardwareBuffer buffer = snapshot.getHardwareBuffer(); - taskSize = new Point(snapshot.getTaskSize()); - contentInsets = new Rect(snapshot.getContentInsets()); - bufferSize = buffer != null - ? new Point(buffer.getWidth(), buffer.getHeight()) - : null; - } - } - /** * If this task is currently running, this is the identifier for it. * If it is not running, this will be -1. @@ -2770,24 +2714,6 @@ public class ActivityManager { @Deprecated public int affiliatedTaskId; - /** - * Information of organized child tasks. - * - * @deprecated No longer used - * @hide - */ - @Deprecated - public ArrayList childrenTaskInfos = new ArrayList<>(); - - /** - * Information about the last snapshot taken for this task. - * - * @deprecated No longer used - * @hide - */ - @Deprecated - public PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); - public RecentTaskInfo() { } @@ -2803,10 +2729,6 @@ public class ActivityManager { public void readFromParcel(Parcel source) { id = source.readInt(); persistentId = source.readInt(); - childrenTaskInfos = source.readArrayList(RecentTaskInfo.class.getClassLoader(), android.app.ActivityManager.RecentTaskInfo.class); - lastSnapshotData.taskSize = source.readTypedObject(Point.CREATOR); - lastSnapshotData.contentInsets = source.readTypedObject(Rect.CREATOR); - lastSnapshotData.bufferSize = source.readTypedObject(Point.CREATOR); super.readTaskFromParcel(source); } @@ -2814,10 +2736,6 @@ public class ActivityManager { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeInt(persistentId); - dest.writeList(childrenTaskInfos); - dest.writeTypedObject(lastSnapshotData.taskSize, flags); - dest.writeTypedObject(lastSnapshotData.contentInsets, flags); - dest.writeTypedObject(lastSnapshotData.bufferSize, flags); super.writeTaskToParcel(dest, flags); } @@ -2884,11 +2802,6 @@ public class ActivityManager { pw.println(" }"); } pw.print(" "); - pw.print(" lastSnapshotData {"); - pw.print(" taskSize=" + lastSnapshotData.taskSize); - pw.print(" contentInsets=" + lastSnapshotData.contentInsets); - pw.print(" bufferSize=" + lastSnapshotData.bufferSize); - pw.println(" }"); } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 81a04af17703..f0e12fec3107 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -2025,22 +2025,9 @@ class RecentTasks { // Fill in some deprecated values. rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; - rti.lastSnapshotData.set(tr.mLastTaskSnapshotData); if (!getTasksAllowed) { Task.trimIneffectiveInfo(tr, rti); } - - // Fill in organized child task info for the task created by organizer. - if (tr.mCreatedByOrganizer) { - for (int i = tr.getChildCount() - 1; i >= 0; i--) { - final Task childTask = tr.getChildAt(i).asTask(); - if (childTask != null && childTask.isOrganized()) { - final ActivityManager.RecentTaskInfo cti = new ActivityManager.RecentTaskInfo(); - childTask.fillTaskInfo(cti, true /* stripExtras */, tda); - rti.childrenTaskInfos.add(cti); - } - } - } return rti; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index dbc3b76c22a1..a29d41d2b9f5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -128,7 +128,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData; import android.app.ActivityManager.TaskDescription; import android.app.ActivityOptions; import android.app.ActivityTaskManager; @@ -256,9 +255,6 @@ class Task extends TaskFragment { private static final String ATTR_MIN_HEIGHT = "min_height"; private static final String ATTR_PERSIST_TASK_VERSION = "persist_task_version"; private static final String ATTR_WINDOW_LAYOUT_AFFINITY = "window_layout_affinity"; - private static final String ATTR_LAST_SNAPSHOT_TASK_SIZE = "last_snapshot_task_size"; - private static final String ATTR_LAST_SNAPSHOT_CONTENT_INSETS = "last_snapshot_content_insets"; - private static final String ATTR_LAST_SNAPSHOT_BUFFER_SIZE = "last_snapshot_buffer_size"; // How long to wait for all background Activities to redraw following a call to // convertToTranslucent(). @@ -472,10 +468,6 @@ class Task extends TaskFragment { // NOTE: This value needs to be persisted with each task private TaskDescription mTaskDescription; - // Information about the last snapshot that should be persisted with the task to allow SystemUI - // to layout without loading all the task snapshots - final PersistedTaskSnapshotData mLastTaskSnapshotData; - /** @see #setCanAffectSystemUiFlags */ private boolean mCanAffectSystemUiFlags = true; @@ -635,14 +627,13 @@ class Task extends TaskFragment { ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents, int _userId, int _effectiveUid, String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity, - TaskDescription _lastTaskDescription, PersistedTaskSnapshotData _lastSnapshotData, - int taskAffiliation, int prevTaskId, int nextTaskId, int callingUid, - String callingPackage, @Nullable String callingFeatureId, int resizeMode, - boolean supportsPictureInPicture, boolean _realActivitySuspended, - boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info, - IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor, - boolean _createdByOrganizer, IBinder _launchCookie, boolean _deferTaskAppear, - boolean _removeWithTaskOrganizer) { + TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId, + int nextTaskId, int callingUid, String callingPackage, + @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture, + boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight, + ActivityInfo info, IVoiceInteractionSession _voiceSession, + IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer, IBinder _launchCookie, + boolean _deferTaskAppear, boolean _removeWithTaskOrganizer) { super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */); mTaskId = _taskId; @@ -652,9 +643,6 @@ class Task extends TaskFragment { mTaskDescription = _lastTaskDescription != null ? _lastTaskDescription : new TaskDescription(); - mLastTaskSnapshotData = _lastSnapshotData != null - ? _lastSnapshotData - : new PersistedTaskSnapshotData(); affinityIntent = _affinityIntent; affinity = _affinity; rootAffinity = _rootAffinity; @@ -3111,7 +3099,6 @@ class Task extends TaskFragment { } void onSnapshotChanged(TaskSnapshot snapshot) { - mLastTaskSnapshotData.set(snapshot); mAtmService.getTaskChangeNotificationController().notifyTaskSnapshotChanged( mTaskId, snapshot); } @@ -3989,19 +3976,6 @@ class Task extends TaskFragment { out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight); out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION); - if (mLastTaskSnapshotData.taskSize != null) { - out.attribute(null, ATTR_LAST_SNAPSHOT_TASK_SIZE, - mLastTaskSnapshotData.taskSize.flattenToString()); - } - if (mLastTaskSnapshotData.contentInsets != null) { - out.attribute(null, ATTR_LAST_SNAPSHOT_CONTENT_INSETS, - mLastTaskSnapshotData.contentInsets.flattenToString()); - } - if (mLastTaskSnapshotData.bufferSize != null) { - out.attribute(null, ATTR_LAST_SNAPSHOT_BUFFER_SIZE, - mLastTaskSnapshotData.bufferSize.flattenToString()); - } - if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); affinityIntent.saveToXml(out); @@ -4068,7 +4042,6 @@ class Task extends TaskFragment { int taskId = INVALID_TASK_ID; final int outerDepth = in.getDepth(); TaskDescription taskDescription = new TaskDescription(); - PersistedTaskSnapshotData lastSnapshotData = new PersistedTaskSnapshotData(); int taskAffiliation = INVALID_TASK_ID; int prevTaskId = INVALID_TASK_ID; int nextTaskId = INVALID_TASK_ID; @@ -4175,15 +4148,6 @@ class Task extends TaskFragment { case ATTR_PERSIST_TASK_VERSION: persistTaskVersion = Integer.parseInt(attrValue); break; - case ATTR_LAST_SNAPSHOT_TASK_SIZE: - lastSnapshotData.taskSize = Point.unflattenFromString(attrValue); - break; - case ATTR_LAST_SNAPSHOT_CONTENT_INSETS: - lastSnapshotData.contentInsets = Rect.unflattenFromString(attrValue); - break; - case ATTR_LAST_SNAPSHOT_BUFFER_SIZE: - lastSnapshotData.bufferSize = Point.unflattenFromString(attrValue); - break; default: if (!attrName.startsWith(TaskDescription.ATTR_TASKDESCRIPTION_PREFIX)) { Slog.w(TAG, "Task: Unknown attribute=" + attrName); @@ -4277,7 +4241,6 @@ class Task extends TaskFragment { .setLastTimeMoved(lastTimeOnTop) .setNeverRelinquishIdentity(neverRelinquishIdentity) .setLastTaskDescription(taskDescription) - .setLastSnapshotData(lastSnapshotData) .setTaskAffiliation(taskAffiliation) .setPrevAffiliateTaskId(prevTaskId) .setNextAffiliateTaskId(nextTaskId) @@ -6443,7 +6406,6 @@ class Task extends TaskFragment { private long mLastTimeMoved; private boolean mNeverRelinquishIdentity; private TaskDescription mLastTaskDescription; - private PersistedTaskSnapshotData mLastSnapshotData; private int mTaskAffiliation; private int mPrevAffiliateTaskId = INVALID_TASK_ID; private int mNextAffiliateTaskId = INVALID_TASK_ID; @@ -6671,11 +6633,6 @@ class Task extends TaskFragment { return this; } - private Builder setLastSnapshotData(PersistedTaskSnapshotData lastSnapshotData) { - mLastSnapshotData = lastSnapshotData; - return this; - } - private Builder setOrigActivity(ComponentName origActivity) { mOrigActivity = origActivity; return this; @@ -6824,7 +6781,7 @@ class Task extends TaskFragment { return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity, mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved, - mNeverRelinquishIdentity, mLastTaskDescription, mLastSnapshotData, + mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation, mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage, mCallingFeatureId, mResizeMode, mSupportsPictureInPicture, mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight, diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index df17cd1d24b7..7ed8283efffd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -495,24 +495,6 @@ public class RecentTasksTest extends WindowTestsBase { true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); } - @Test - public void testAppendOrganizedChildTaskInfo() { - final Task root = createTaskBuilder(".CreatedByOrganizerRoot").build(); - root.mCreatedByOrganizer = true; - // Add organized and non-organized child. - final Task child1 = createTaskBuilder(".Task1").setParentTask(root).build(); - final Task child2 = createTaskBuilder(".Task2").setParentTask(root).build(); - doReturn(true).when(child1).isOrganized(); - doReturn(false).when(child2).isOrganized(); - mRecentTasks.add(root); - - // Make sure only organized child will be appended. - final List infos = getRecentTasks(0 /* flags */); - final List childrenTaskInfos = infos.get(0).childrenTaskInfos; - assertEquals(childrenTaskInfos.size(), 1); - assertEquals(childrenTaskInfos.get(0).taskId, child1.mTaskId); - } - @Test public void testAddTasksHomeClearUntrackedTasks_expectFinish() { // There may be multiple tasks with the same base intent by flags (FLAG_ACTIVITY_NEW_TASK | @@ -1419,46 +1401,6 @@ public class RecentTasksTest extends WindowTestsBase { } } - @Test - public void testLastSnapshotData_snapshotSaved() { - final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), new Point(80, 80)); - final Task task1 = createTaskBuilder(".Task").build(); - task1.onSnapshotChanged(snapshot); - - mRecentTasks.add(task1); - final List infos = getRecentTasks(0 /* flags */); - final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = - infos.get(0).lastSnapshotData; - assertTrue(lastSnapshotData.taskSize.equals(100, 100)); - assertTrue(lastSnapshotData.bufferSize.equals(80, 80)); - } - - @Test - public void testLastSnapshotData_noBuffer() { - final Task task1 = createTaskBuilder(".Task").build(); - final TaskSnapshot snapshot = createSnapshot(new Point(100, 100), null); - task1.onSnapshotChanged(snapshot); - - mRecentTasks.add(task1); - final List infos = getRecentTasks(0 /* flags */); - final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = - infos.get(0).lastSnapshotData; - assertTrue(lastSnapshotData.taskSize.equals(100, 100)); - assertNull(lastSnapshotData.bufferSize); - } - - @Test - public void testLastSnapshotData_notSet() { - final Task task1 = createTaskBuilder(".Task").build(); - - mRecentTasks.add(task1); - final List infos = getRecentTasks(0 /* flags */); - final RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData = - infos.get(0).lastSnapshotData; - assertNull(lastSnapshotData.taskSize); - assertNull(lastSnapshotData.bufferSize); - } - @Test public void testCreateRecentTaskInfo_detachedTask() { final Task task = createTaskBuilder(".Task").build(); -- GitLab From 8739382f1576ac1951f5fd66e2976f6f6635ab62 Mon Sep 17 00:00:00 2001 From: Matt Buckley Date: Sun, 17 Nov 2024 01:58:16 +0000 Subject: [PATCH 108/656] Add support for converting java hint sessions to native hint sessions Add an API called "APerformanceHint_getSessionFromJava" for Java clients to access the native APerformanceHintSession objects that Java hint sessions wrap. This should will make the Java and Native APIs substantially more interoperable. This patch also makes the native code aware of when it is being used in Java, to allow it to have slightly different behavior, especially when it is being directly accessed through JNI. The main place where this mostly matters is in preventing clients from closing Java-owned sessions through the JNI, as that could cause weird behavior otherwise. Bug: 367803904 Test: atest HintManagerServiceTest Test: atest PerformanceHintNativeTestCases Flag: EXEMPT NDK API Change-Id: I126e8663fc2719d9b7c866cf56f9827c25c2a53c --- .../android/os/PerformanceHintManager.java | 5 +- .../jni/android_os_PerformanceHintManager.cpp | 9 ++- native/android/libandroid.map.txt | 3 + native/android/performance_hint.cpp | 74 +++++++++++++++++-- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/core/java/android/os/PerformanceHintManager.java b/core/java/android/os/PerformanceHintManager.java index 224b10d0eaca..2b0042d653b1 100644 --- a/core/java/android/os/PerformanceHintManager.java +++ b/core/java/android/os/PerformanceHintManager.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.TestApi; +import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import com.android.internal.util.Preconditions; @@ -106,7 +107,9 @@ public final class PerformanceHintManager { * All timings should be in {@link SystemClock#uptimeNanos()}. */ public static class Session implements Closeable { - private long mNativeSessionPtr; + /** @hide */ + @UnsupportedAppUsage + public long mNativeSessionPtr; /** @hide */ public Session(long nativeSessionPtr) { diff --git a/core/jni/android_os_PerformanceHintManager.cpp b/core/jni/android_os_PerformanceHintManager.cpp index aebe7ea7ee61..0f78c9e93a00 100644 --- a/core/jni/android_os_PerformanceHintManager.cpp +++ b/core/jni/android_os_PerformanceHintManager.cpp @@ -88,9 +88,10 @@ void ensureAPerformanceHintBindingInitialized() { "Failed to find required symbol " "APerformanceHint_getPreferredUpdateRateNanos!"); - gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession"); + gAPH_createSessionFn = + (APH_createSession)dlsym(handle_, "APerformanceHint_createSessionFromJava"); LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr, - "Failed to find required symbol APerformanceHint_createSession!"); + "Failed to find required symbol APerformanceHint_createSessionFromJava!"); gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(handle_, @@ -106,9 +107,9 @@ void ensureAPerformanceHintBindingInitialized() { "Failed to find required symbol " "APerformanceHint_reportActualWorkDuration!"); - gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession"); + gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSessionFromJava"); LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr, - "Failed to find required symbol APerformanceHint_closeSession!"); + "Failed to find required symbol APerformanceHint_closeSessionFromJava!"); gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint"); LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr, diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 2d1fbf9e7f66..6b41ddde5cc8 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -363,6 +363,7 @@ LIBANDROID { APerformanceHint_reportActualWorkDuration2; # introduced=VanillaIceCream APerformanceHint_notifyWorkloadIncrease; # introduced=36 APerformanceHint_notifyWorkloadReset; # introduced=36 + APerformanceHint_borrowSessionFromJava; # introduced=36 AWorkDuration_create; # introduced=VanillaIceCream AWorkDuration_release; # introduced=VanillaIceCream AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream @@ -383,6 +384,8 @@ LIBANDROID_PLATFORM { APerformanceHint_setUseFMQForTesting; APerformanceHint_getRateLimiterPropertiesForTesting; APerformanceHint_setUseNewLoadHintBehaviorForTesting; + APerformanceHint_closeSessionFromJava; + APerformanceHint_createSessionFromJava; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; ASurfaceControl_unregisterSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index e2fa94dd39bb..bc1945e37072 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -137,10 +138,14 @@ public: APerformanceHintSession* createSession(const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos, - hal::SessionTag tag = hal::SessionTag::APP); + hal::SessionTag tag = hal::SessionTag::APP, + bool isJava = false); + APerformanceHintSession* getSessionFromJava(JNIEnv* _Nonnull env, jobject _Nonnull sessionObj); + int64_t getPreferredRateNanos() const; FMQWrapper& getFMQWrapper(); bool canSendLoadHints(std::vector& hints, int64_t now) REQUIRES(sHintMutex); + void initJava(JNIEnv* _Nonnull env); private: // Necessary to create an empty binder object @@ -161,13 +166,16 @@ private: FMQWrapper mFMQWrapper; double mHintBudget = kMaxLoadHintsPerInterval; int64_t mLastBudgetReplenish = 0; + bool mJavaInitialized = false; + jclass mJavaSessionClazz; + jfieldID mJavaSessionNativePtr; }; struct APerformanceHintSession { public: APerformanceHintSession(std::shared_ptr hintManager, std::shared_ptr session, int64_t preferredRateNanos, - int64_t targetDurationNanos, + int64_t targetDurationNanos, bool isJava, std::optional sessionConfig); APerformanceHintSession() = delete; ~APerformanceHintSession(); @@ -181,6 +189,7 @@ public: int getThreadIds(int32_t* const threadIds, size_t* size); int setPreferPowerEfficiency(bool enabled); int reportActualWorkDuration(AWorkDuration* workDuration); + bool isJava(); private: friend struct APerformanceHintManager; @@ -203,6 +212,8 @@ private: std::vector mLastHintSentTimestamp GUARDED_BY(sHintMutex); // Cached samples std::vector mActualWorkDurations GUARDED_BY(sHintMutex); + // Is this session backing an SDK wrapper object + const bool mIsJava; std::string mSessionName; static int64_t sIDCounter GUARDED_BY(sHintMutex); // The most recent set of thread IDs @@ -299,7 +310,7 @@ bool APerformanceHintManager::canSendLoadHints(std::vector& hi APerformanceHintSession* APerformanceHintManager::createSession( const int32_t* threadIds, size_t size, int64_t initialTargetWorkDurationNanos, - hal::SessionTag tag) { + hal::SessionTag tag, bool isJava) { std::vector tids(threadIds, threadIds + size); std::shared_ptr session; ndk::ScopedAStatus ret; @@ -312,7 +323,7 @@ APerformanceHintSession* APerformanceHintManager::createSession( return nullptr; } auto out = new APerformanceHintSession(mHintManager, std::move(session), mPreferredRateNanos, - initialTargetWorkDurationNanos, + initialTargetWorkDurationNanos, isJava, sessionConfig.id == -1 ? std::nullopt : std::make_optional( @@ -324,6 +335,18 @@ APerformanceHintSession* APerformanceHintManager::createSession( return out; } +APerformanceHintSession* APerformanceHintManager::getSessionFromJava(JNIEnv* env, + jobject sessionObj) { + initJava(env); + LOG_ALWAYS_FATAL_IF(!env->IsInstanceOf(sessionObj, mJavaSessionClazz), + "Wrong java type passed to APerformanceHint_getSessionFromJava"); + APerformanceHintSession* out = reinterpret_cast( + env->GetLongField(sessionObj, mJavaSessionNativePtr)); + LOG_ALWAYS_FATAL_IF(out == nullptr, "Java-wrapped native hint session is nullptr"); + LOG_ALWAYS_FATAL_IF(!out->isJava(), "Unmanaged native hint session returned from Java SDK"); + return out; +} + int64_t APerformanceHintManager::getPreferredRateNanos() const { return mPreferredRateNanos; } @@ -332,13 +355,23 @@ FMQWrapper& APerformanceHintManager::getFMQWrapper() { return mFMQWrapper; } +void APerformanceHintManager::initJava(JNIEnv* _Nonnull env) { + if (mJavaInitialized) { + return; + } + jclass sessionClazz = FindClassOrDie(env, "android/os/PerformanceHintManager$Session"); + mJavaSessionClazz = MakeGlobalRefOrDie(env, sessionClazz); + mJavaSessionNativePtr = GetFieldIDOrDie(env, mJavaSessionClazz, "mNativeSessionPtr", "J"); + mJavaInitialized = true; +} + // ===================================== APerformanceHintSession implementation constexpr int kNumEnums = enum_size(); APerformanceHintSession::APerformanceHintSession(std::shared_ptr hintManager, std::shared_ptr session, int64_t preferredRateNanos, - int64_t targetDurationNanos, + int64_t targetDurationNanos, bool isJava, std::optional sessionConfig) : mHintManager(hintManager), mHintSession(std::move(session)), @@ -347,6 +380,7 @@ APerformanceHintSession::APerformanceHintSession(std::shared_ptr h mFirstTargetMetTimestamp(0), mLastTargetMetTimestamp(0), mLastHintSentTimestamp(std::vector(kNumEnums, 0)), + mIsJava(isJava), mSessionConfig(sessionConfig) { if (sessionConfig->id > INT32_MAX) { ALOGE("Session ID too large, must fit 32-bit integer"); @@ -401,6 +435,10 @@ int APerformanceHintSession::reportActualWorkDuration(int64_t actualDurationNano return reportActualWorkDurationInternal(static_cast(&workDuration)); } +bool APerformanceHintSession::isJava() { + return mIsJava; +} + int APerformanceHintSession::sendHints(std::vector& hints, int64_t now, const char*) { std::scoped_lock lock(sHintMutex); @@ -826,6 +864,22 @@ APerformanceHintSession* APerformanceHint_createSessionInternal( static_cast(tag)); } +APerformanceHintSession* APerformanceHint_createSessionFromJava( + APerformanceHintManager* manager, const int32_t* threadIds, size_t size, + int64_t initialTargetWorkDurationNanos) { + VALIDATE_PTR(manager) + VALIDATE_PTR(threadIds) + return manager->createSession(threadIds, size, initialTargetWorkDurationNanos, + hal::SessionTag::APP, true); +} + +APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env, + jobject sessionObj) { + VALIDATE_PTR(env) + VALIDATE_PTR(sessionObj) + return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj); +} + int64_t APerformanceHint_getPreferredUpdateRateNanos(APerformanceHintManager* manager) { VALIDATE_PTR(manager) return manager->getPreferredRateNanos(); @@ -845,6 +899,16 @@ int APerformanceHint_reportActualWorkDuration(APerformanceHintSession* session, } void APerformanceHint_closeSession(APerformanceHintSession* session) { + VALIDATE_PTR(session) + if (session->isJava()) { + LOG_ALWAYS_FATAL("%s: Java-owned PerformanceHintSession cannot be closed in native", + __FUNCTION__); + return; + } + delete session; +} + +void APerformanceHint_closeSessionFromJava(APerformanceHintSession* session) { VALIDATE_PTR(session) delete session; } -- GitLab From c6bc983c3f78e0d59885d716f7c981fff85431ab Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Sun, 17 Nov 2024 14:20:44 +0800 Subject: [PATCH 109/656] [Catalyst] Clean up SharedPreferencesObservable Bug: 378795106 Flag: EXEMPT clean up Test: N/A Change-Id: Iaac9f819da5e2286b170920b664157c13124d6a3 --- .../datastore/SharedPreferencesObservable.kt | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt deleted file mode 100644 index e70ec5b2e38e..000000000000 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesObservable.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.datastore - -import android.content.SharedPreferences - -/** [SharedPreferences] based [KeyedDataObservable]. */ -class SharedPreferencesObservable(private val sharedPreferences: SharedPreferences) : - KeyedDataObservable(), AutoCloseable { - - private val listener = createSharedPreferenceListener() - - init { - sharedPreferences.registerOnSharedPreferenceChangeListener(listener) - } - - override fun close() { - sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener) - } -} - -/** Creates [SharedPreferences.OnSharedPreferenceChangeListener] for [KeyedObservable]. */ -internal fun KeyedObservable.createSharedPreferenceListener() = - SharedPreferences.OnSharedPreferenceChangeListener { _, key -> - if (key != null) { - notifyChange(key, DataChangeReason.UPDATE) - } else { - // On Android >= R, SharedPreferences.Editor.clear() will trigger this case - notifyChange(DataChangeReason.DELETE) - } - } -- GitLab From a5a0e867484a99de7f5fd364f49f2f0273e82e36 Mon Sep 17 00:00:00 2001 From: Sunny Shao Date: Sat, 16 Nov 2024 15:42:06 +0800 Subject: [PATCH 110/656] [Catalyst] Migrate Adaptive Battery Category Add order for PreferenceHierarchy Test: atest BatterySaverScreenTest Bug: 378754980 Flag: com.android.settings.flags.catalyst_battery_saver_screen Change-Id: Ib07ba497a53f2688d228cb1fcd52df92dd06284b --- .../com/android/settingslib/metadata/PreferenceHierarchy.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt index a2b826a50e58..9179f8ffc082 100644 --- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt +++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt @@ -58,6 +58,9 @@ class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) : /** Specifies preference order in the hierarchy. */ infix fun PreferenceHierarchyNode.order(order: Int) = apply { this.order = order } + /** Specifies preference order in the hierarchy for group. */ + infix fun PreferenceHierarchy.order(order: Int) = apply { this.order = order } + /** Adds a preference to the hierarchy. */ @JvmOverloads fun add(metadata: PreferenceMetadata, order: Int? = null) { -- GitLab From dbaba7a2dfd3d89dac2954972c3ece06393887f9 Mon Sep 17 00:00:00 2001 From: Jorge Gil Date: Sun, 17 Nov 2024 07:38:23 +0000 Subject: [PATCH 111/656] Update Maximize Menu UI 1) Change "Snap Screen" test to "Resize" 2) Change menu's corner radius to 16dp 3) Fix RTL spacing in snap left/right options 4) Update immersive button fill 5) Update restore button fill 6) Use different border color for inactive options Fix: 376075104 Fix: 356210491 Flag: EXEMPT bug fix Test: atest WMShellUnitTests Change-Id: I3b887cd6d8f4a33af748e423ef80c2faf64b7153 --- ...esktop_mode_window_decor_maximize_menu.xml | 2 +- libs/WindowManager/Shell/res/values/dimen.xml | 8 +++----- .../Shell/res/values/strings.xml | 2 +- .../wm/shell/windowdecor/MaximizeMenu.kt | 20 ++++++++++--------- .../wm/shell/windowdecor/common/ThemeUtils.kt | 1 + 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml index 5e41865cd31e..375968ab0ad2 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml @@ -113,7 +113,7 @@ style="?android:attr/buttonBarButtonStyle" android:layout_width="41dp" android:layout_height="@dimen/desktop_mode_maximize_menu_button_height" - android:layout_marginRight="4dp" + android:layout_marginEnd="4dp" android:background="@drawable/desktop_mode_maximize_menu_button_background" android:importantForAccessibility="yes" android:contentDescription="@string/desktop_mode_maximize_menu_snap_left_button_text" diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 249e9a26e6a6..abe2dad7bf6f 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -504,17 +504,15 @@ 4dp 4dp - - 8dp 13dp - 21dp + 15dp - 4dp + 0dp - 8dp + 16dp 8dp diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index 012579a6d40c..468c345259d0 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -318,7 +318,7 @@ Maximize Screen - Snap Screen + Resize App can\'t be moved here diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt index 4bb1e7b6cc05..11a7cf8da8d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt @@ -70,6 +70,7 @@ import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHo import com.android.wm.shell.windowdecor.common.DecorThemeUtil import com.android.wm.shell.windowdecor.common.OPACITY_12 import com.android.wm.shell.windowdecor.common.OPACITY_40 +import com.android.wm.shell.windowdecor.common.OPACITY_60 import com.android.wm.shell.windowdecor.common.withAlpha import java.util.function.Supplier @@ -310,8 +311,6 @@ class MaximizeMenu( .desktop_mode_maximize_menu_immersive_button_fill_padding) private val maximizeFillPaddingDefault = context.resources.getDimensionPixelSize(R.dimen .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding) - private val maximizeFillPaddingBottom = context.resources.getDimensionPixelSize(R.dimen - .desktop_mode_maximize_menu_snap_and_maximize_buttons_fill_padding_bottom) private val maximizeRestoreFillPaddingVertical = context.resources.getDimensionPixelSize( R.dimen.desktop_mode_maximize_menu_restore_button_fill_vertical_padding) private val maximizeRestoreFillPaddingHorizontal = context.resources.getDimensionPixelSize( @@ -320,7 +319,7 @@ class MaximizeMenu( maximizeFillPaddingDefault, maximizeFillPaddingDefault, maximizeFillPaddingDefault, - maximizeFillPaddingBottom + maximizeFillPaddingDefault ) private val maximizeRestoreFillPaddingRect = Rect( maximizeRestoreFillPaddingHorizontal, @@ -684,7 +683,7 @@ class MaximizeMenu( inactiveSnapSideColor = colorScheme.outlineVariant.toArgb(), semiActiveSnapSideColor = colorScheme.primary.toArgb().withAlpha(OPACITY_40), activeSnapSideColor = colorScheme.primary.toArgb(), - inactiveStrokeColor = colorScheme.outlineVariant.toArgb(), + inactiveStrokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60), activeStrokeColor = colorScheme.primary.toArgb(), inactiveBackgroundColor = menuBackgroundColor, activeBackgroundColor = colorScheme.primary.toArgb().withAlpha(OPACITY_12) @@ -753,7 +752,8 @@ class MaximizeMenu( val activeStrokeAndFill = colorScheme.primary.toArgb() val activeBackground = colorScheme.primary.toArgb().withAlpha(OPACITY_12) val activeDrawable = createMaximizeOrImmersiveButtonDrawable( - strokeAndFillColor = activeStrokeAndFill, + strokeColor = activeStrokeAndFill, + fillColor = activeStrokeAndFill, backgroundColor = activeBackground, // Add a mask with the menu background's color because the active background color is // semi transparent, otherwise the transparency will reveal the stroke/fill color @@ -770,7 +770,8 @@ class MaximizeMenu( addState( StateSet.WILD_CARD, createMaximizeOrImmersiveButtonDrawable( - strokeAndFillColor = colorScheme.outlineVariant.toArgb(), + strokeColor = colorScheme.outlineVariant.toArgb().withAlpha(OPACITY_60), + fillColor = colorScheme.outlineVariant.toArgb(), backgroundColor = colorScheme.surfaceContainerLow.toArgb(), backgroundMask = null, // not needed because the bg color is fully opaque fillPadding = fillPadding, @@ -780,7 +781,8 @@ class MaximizeMenu( } private fun createMaximizeOrImmersiveButtonDrawable( - @ColorInt strokeAndFillColor: Int, + @ColorInt strokeColor: Int, + @ColorInt fillColor: Int, @ColorInt backgroundColor: Int, @ColorInt backgroundMask: Int?, fillPadding: Rect, @@ -794,7 +796,7 @@ class MaximizeMenu( null /* inset */, null /* innerRadii */ ) - paint.color = strokeAndFillColor + paint.color = strokeColor paint.style = Paint.Style.FILL }) // Second layer, a mask for the next (background) layer if needed because of @@ -829,7 +831,7 @@ class MaximizeMenu( null /* inset */, null /* innerRadii */ ) - paint.color = strokeAndFillColor + paint.color = fillColor paint.style = Paint.Style.FILL }) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt index f7cfbfa88485..c5057aa3cc18 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt @@ -52,6 +52,7 @@ const val OPACITY_12 = 31 const val OPACITY_15 = 38 const val OPACITY_40 = 102 const val OPACITY_55 = 140 +const val OPACITY_60 = 153 const val OPACITY_65 = 166 /** -- GitLab From ba000ae7a73f43e504ca7ac84cb008fef6025102 Mon Sep 17 00:00:00 2001 From: Harshit Mahajan Date: Sun, 17 Nov 2024 12:20:35 +0000 Subject: [PATCH 112/656] Make mLock static for accessibility Bug: 289203818 Test: TH Flag: EXEMPT refactor Change-Id: I6ce310738ddfb41a77348ac897ef56e5e6971e88 --- .../com/android/server/PackageWatchdog.java | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index fbf51fd7cca7..fd5bab89481a 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -218,7 +218,7 @@ public class PackageWatchdog { @GuardedBy("sPackageWatchdogLock") private static PackageWatchdog sPackageWatchdog; - private final Object mLock = new Object(); + private static final Object sLock = new Object(); // System server context private final Context mContext; // Handler to run short running tasks @@ -228,7 +228,7 @@ public class PackageWatchdog { // Contains (observer-name -> observer-handle) that have ever been registered from // previous boots. Observers with all packages expired are periodically pruned. // It is saved to disk on system shutdown and repouplated on startup so it survives reboots. - @GuardedBy("mLock") + @GuardedBy("sLock") private final ArrayMap mAllObservers = new ArrayMap<>(); // File containing the XML data of monitored packages /data/system/package-watchdog.xml private final AtomicFile mPolicyFile; @@ -244,26 +244,26 @@ public class PackageWatchdog { private final Set mPackagesExemptFromImpactLevelThreshold = new ArraySet<>(); // The set of packages that have been synced with the ExplicitHealthCheckController - @GuardedBy("mLock") + @GuardedBy("sLock") private Set mRequestedHealthCheckPackages = new ArraySet<>(); - @GuardedBy("mLock") + @GuardedBy("sLock") private boolean mIsPackagesReady; // Flag to control whether explicit health checks are supported or not - @GuardedBy("mLock") + @GuardedBy("sLock") private boolean mIsHealthCheckEnabled = DEFAULT_EXPLICIT_HEALTH_CHECK_ENABLED; - @GuardedBy("mLock") + @GuardedBy("sLock") private int mTriggerFailureDurationMs = DEFAULT_TRIGGER_FAILURE_DURATION_MS; - @GuardedBy("mLock") + @GuardedBy("sLock") private int mTriggerFailureCount = DEFAULT_TRIGGER_FAILURE_COUNT; // SystemClock#uptimeMillis when we last executed #syncState // 0 if no prune is scheduled. - @GuardedBy("mLock") + @GuardedBy("sLock") private long mUptimeAtLastStateSync; // If true, sync explicit health check packages with the ExplicitHealthCheckController. - @GuardedBy("mLock") + @GuardedBy("sLock") private boolean mSyncRequired = false; - @GuardedBy("mLock") + @GuardedBy("sLock") private long mLastMitigation = -1000000; @FunctionalInterface @@ -319,7 +319,7 @@ public class PackageWatchdog { * @hide */ public void onPackagesReady() { - synchronized (mLock) { + synchronized (sLock) { mIsPackagesReady = true; mHealthCheckController.setCallbacks(packageName -> onHealthCheckPassed(packageName), packages -> onSupportedPackages(packages), @@ -338,7 +338,7 @@ public class PackageWatchdog { * @hide */ public void registerHealthObserver(PackageHealthObserver observer) { - synchronized (mLock) { + synchronized (sLock) { ObserverInternal internalObserver = mAllObservers.get(observer.getUniqueIdentifier()); if (internalObserver != null) { internalObserver.registeredObserver = observer; @@ -405,7 +405,7 @@ public class PackageWatchdog { mLongTaskHandler.post(() -> { syncState("observing new packages"); - synchronized (mLock) { + synchronized (sLock) { ObserverInternal oldObserver = mAllObservers.get(observer.getUniqueIdentifier()); if (oldObserver == null) { Slog.d(TAG, observer.getUniqueIdentifier() + " started monitoring health " @@ -437,7 +437,7 @@ public class PackageWatchdog { */ public void unregisterHealthObserver(PackageHealthObserver observer) { mLongTaskHandler.post(() -> { - synchronized (mLock) { + synchronized (sLock) { mAllObservers.remove(observer.getUniqueIdentifier()); } syncState("unregistering observer: " + observer.getUniqueIdentifier()); @@ -458,7 +458,7 @@ public class PackageWatchdog { Slog.w(TAG, "Could not resolve a list of failing packages"); return; } - synchronized (mLock) { + synchronized (sLock) { final long now = mSystemClock.uptimeMillis(); if (Flags.recoverabilityDetection()) { if (now >= mLastMitigation @@ -469,7 +469,7 @@ public class PackageWatchdog { } } mLongTaskHandler.post(() -> { - synchronized (mLock) { + synchronized (sLock) { if (mAllObservers.isEmpty()) { return; } @@ -569,7 +569,7 @@ public class PackageWatchdog { int currentObserverImpact, int mitigationCount) { if (allowMitigations(currentObserverImpact, versionedPackage)) { - synchronized (mLock) { + synchronized (sLock) { mLastMitigation = mSystemClock.uptimeMillis(); } currentObserverToNotify.onExecuteHealthCheckMitigation(versionedPackage, failureReason, @@ -599,7 +599,7 @@ public class PackageWatchdog { */ @SuppressWarnings("GuardedBy") public void noteBoot() { - synchronized (mLock) { + synchronized (sLock) { // if boot count has reached threshold, start mitigation. // We wait until threshold number of restarts only for the first time. Perform // mitigations for every restart after that. @@ -652,7 +652,7 @@ public class PackageWatchdog { // This currently adds about 7ms extra to shutdown thread /** @hide Writes the package information to file during shutdown. */ public void writeNow() { - synchronized (mLock) { + synchronized (sLock) { // Must only run synchronous tasks as this runs on the ShutdownThread and no other // thread is guaranteed to run during shutdown. if (!mAllObservers.isEmpty()) { @@ -671,7 +671,7 @@ public class PackageWatchdog { * passed and the health check service is stopped. */ private void setExplicitHealthCheckEnabled(boolean enabled) { - synchronized (mLock) { + synchronized (sLock) { mIsHealthCheckEnabled = enabled; mHealthCheckController.setEnabled(enabled); mSyncRequired = true; @@ -853,14 +853,14 @@ public class PackageWatchdog { @VisibleForTesting long getTriggerFailureCount() { - synchronized (mLock) { + synchronized (sLock) { return mTriggerFailureCount; } } @VisibleForTesting long getTriggerFailureDurationMs() { - synchronized (mLock) { + synchronized (sLock) { return mTriggerFailureDurationMs; } } @@ -881,7 +881,7 @@ public class PackageWatchdog { */ private void syncRequests() { boolean syncRequired = false; - synchronized (mLock) { + synchronized (sLock) { if (mIsPackagesReady) { Set packages = getPackagesPendingHealthChecksLocked(); if (mSyncRequired || !packages.equals(mRequestedHealthCheckPackages) @@ -918,7 +918,7 @@ public class PackageWatchdog { Slog.i(TAG, "Health check passed for package: " + packageName); boolean isStateChanged = false; - synchronized (mLock) { + synchronized (sLock) { for (int observerIdx = 0; observerIdx < mAllObservers.size(); observerIdx++) { ObserverInternal observer = mAllObservers.valueAt(observerIdx); MonitoredPackage monitoredPackage = observer.getMonitoredPackage(packageName); @@ -946,7 +946,7 @@ public class PackageWatchdog { supportedPackageTimeouts.put(info.getPackageName(), info.getHealthCheckTimeoutMillis()); } - synchronized (mLock) { + synchronized (sLock) { Slog.d(TAG, "Received supported packages " + supportedPackages); Iterator oit = mAllObservers.values().iterator(); while (oit.hasNext()) { @@ -977,13 +977,13 @@ public class PackageWatchdog { } private void onSyncRequestNotified() { - synchronized (mLock) { + synchronized (sLock) { mSyncRequired = true; syncRequestsAsync(); } } - @GuardedBy("mLock") + @GuardedBy("sLock") private Set getPackagesPendingHealthChecksLocked() { Set packages = new ArraySet<>(); Iterator oit = mAllObservers.values().iterator(); @@ -1009,7 +1009,7 @@ public class PackageWatchdog { * health check service and schedules the next state sync. */ private void syncState(String reason) { - synchronized (mLock) { + synchronized (sLock) { Slog.i(TAG, "Syncing state, reason: " + reason); pruneObserversLocked(); @@ -1025,7 +1025,7 @@ public class PackageWatchdog { syncState("scheduled"); } - @GuardedBy("mLock") + @GuardedBy("sLock") private void scheduleNextSyncStateLocked() { long durationMs = getNextStateSyncMillisLocked(); mShortTaskHandler.removeCallbacks(mSyncStateWithScheduledReason); @@ -1043,7 +1043,7 @@ public class PackageWatchdog { * * @returns Long#MAX_VALUE if there are no observed packages. */ - @GuardedBy("mLock") + @GuardedBy("sLock") private long getNextStateSyncMillisLocked() { long shortestDurationMs = Long.MAX_VALUE; for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) { @@ -1064,7 +1064,7 @@ public class PackageWatchdog { * Removes {@code elapsedMs} milliseconds from all durations on monitored packages * and updates other internal state. */ - @GuardedBy("mLock") + @GuardedBy("sLock") private void pruneObserversLocked() { long elapsedMs = mUptimeAtLastStateSync == 0 ? 0 : mSystemClock.uptimeMillis() - mUptimeAtLastStateSync; @@ -1092,7 +1092,7 @@ public class PackageWatchdog { private void onHealthCheckFailed(ObserverInternal observer, Set failedPackages) { mLongTaskHandler.post(() -> { - synchronized (mLock) { + synchronized (sLock) { PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null) { Iterator it = failedPackages.iterator(); @@ -1201,7 +1201,7 @@ public class PackageWatchdog { */ @VisibleForTesting void updateConfigs() { - synchronized (mLock) { + synchronized (sLock) { mTriggerFailureCount = DeviceConfig.getInt( DeviceConfig.NAMESPACE_ROLLBACK, PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT, @@ -1230,7 +1230,7 @@ public class PackageWatchdog { */ private boolean saveToFile() { Slog.i(TAG, "Saving observer state to file"); - synchronized (mLock) { + synchronized (sLock) { FileOutputStream stream; try { stream = mPolicyFile.startWrite(); @@ -1300,7 +1300,7 @@ public class PackageWatchdog { if (Flags.synchronousRebootInRescueParty() && RescueParty.isRecoveryTriggeredReboot()) { dumpInternal(pw); } else { - synchronized (mLock) { + synchronized (sLock) { dumpInternal(pw); } } @@ -1310,7 +1310,7 @@ public class PackageWatchdog { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("Package Watchdog status"); ipw.increaseIndent(); - synchronized (mLock) { + synchronized (sLock) { for (String observerName : mAllObservers.keySet()) { ipw.println("Observer name: " + observerName); ipw.increaseIndent(); @@ -1324,7 +1324,7 @@ public class PackageWatchdog { } @VisibleForTesting - @GuardedBy("mLock") + @GuardedBy("sLock") void registerObserverInternal(ObserverInternal observerInternal) { mAllObservers.put(observerInternal.name, observerInternal); } @@ -1333,15 +1333,15 @@ public class PackageWatchdog { * Represents an observer monitoring a set of packages along with the failure thresholds for * each package. * - *

Note, the PackageWatchdog#mLock must always be held when reading or writing + *

Note, the PackageWatchdog#sLock must always be held when reading or writing * instances of this class. */ static class ObserverInternal { public final String name; - @GuardedBy("mLock") + @GuardedBy("sLock") private final ArrayMap mPackages = new ArrayMap<>(); @Nullable - @GuardedBy("mLock") + @GuardedBy("sLock") public PackageHealthObserver registeredObserver; private int mMitigationCount; @@ -1359,7 +1359,7 @@ public class PackageWatchdog { * Writes important {@link MonitoredPackage} details for this observer to file. * Does not persist any package failure thresholds. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean writeLocked(XmlSerializer out) { try { out.startTag(null, TAG_OBSERVER); @@ -1387,7 +1387,7 @@ public class PackageWatchdog { mMitigationCount = mitigationCount; } - @GuardedBy("mLock") + @GuardedBy("sLock") public void updatePackagesLocked(List packages) { for (int pIndex = 0; pIndex < packages.size(); pIndex++) { MonitoredPackage p = packages.get(pIndex); @@ -1410,7 +1410,7 @@ public class PackageWatchdog { * health check passing, or an empty list if no package expired for which an explicit health * check was still pending */ - @GuardedBy("mLock") + @GuardedBy("sLock") private Set prunePackagesLocked(long elapsedMs) { Set failedPackages = new ArraySet<>(); Iterator it = mPackages.values().iterator(); @@ -1435,7 +1435,7 @@ public class PackageWatchdog { * @returns {@code true} if failure threshold is exceeded, {@code false} otherwise * @hide */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean onPackageFailureLocked(String packageName) { if (getMonitoredPackage(packageName) == null && registeredObserver.isPersistent() && registeredObserver.mayObservePackage(packageName)) { @@ -1454,7 +1454,7 @@ public class PackageWatchdog { * * @return a mapping of package names to {@link MonitoredPackage} objects. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public ArrayMap getMonitoredPackages() { return mPackages; } @@ -1467,7 +1467,7 @@ public class PackageWatchdog { * @return the {@link MonitoredPackage} object associated with the package name if one * exists, {@code null} otherwise. */ - @GuardedBy("mLock") + @GuardedBy("sLock") @Nullable public MonitoredPackage getMonitoredPackage(String packageName) { return mPackages.get(packageName); @@ -1478,7 +1478,7 @@ public class PackageWatchdog { * * @param p: the {@link MonitoredPackage} to store. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public void putMonitoredPackage(MonitoredPackage p) { mPackages.put(p.getName(), p); } @@ -1601,17 +1601,17 @@ public class PackageWatchdog { * Represents a package and its health check state along with the time * it should be monitored for. * - *

Note, the PackageWatchdog#mLock must always be held when reading or writing + *

Note, the PackageWatchdog#sLock must always be held when reading or writing * instances of this class. */ class MonitoredPackage { private final String mPackageName; // Times when package failures happen sorted in ascending order - @GuardedBy("mLock") + @GuardedBy("sLock") private final LongArrayQueue mFailureHistory = new LongArrayQueue(); // Times when an observer was called to mitigate this package's failure. Sorted in // ascending order. - @GuardedBy("mLock") + @GuardedBy("sLock") private final LongArrayQueue mMitigationCalls; // One of STATE_[ACTIVE|INACTIVE|PASSED|FAILED]. Updated on construction and after // methods that could change the health check state: handleElapsedTimeLocked and @@ -1620,17 +1620,17 @@ public class PackageWatchdog { // Whether an explicit health check has passed. // This value in addition with mHealthCheckDurationMs determines the health check state // of the package, see #getHealthCheckStateLocked - @GuardedBy("mLock") + @GuardedBy("sLock") private boolean mHasPassedHealthCheck; // System uptime duration to monitor package. - @GuardedBy("mLock") + @GuardedBy("sLock") private long mDurationMs; // System uptime duration to check the result of an explicit health check // Initially, MAX_VALUE until we get a value from the health check service // and request health checks. // This value in addition with mHasPassedHealthCheck determines the health check state // of the package, see #getHealthCheckStateLocked - @GuardedBy("mLock") + @GuardedBy("sLock") private long mHealthCheckDurationMs = Long.MAX_VALUE; MonitoredPackage(String packageName, long durationMs, @@ -1647,7 +1647,7 @@ public class PackageWatchdog { /** Writes the salient fields to disk using {@code out}. * @hide */ - @GuardedBy("mLock") + @GuardedBy("sLock") public void writeLocked(XmlSerializer out) throws IOException { out.startTag(null, TAG_PACKAGE); out.attribute(null, ATTR_NAME, getName()); @@ -1665,7 +1665,7 @@ public class PackageWatchdog { * * @return {@code true} if failure count exceeds a threshold, {@code false} otherwise */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean onFailureLocked() { // Sliding window algorithm: find out if there exists a window containing failures >= // mTriggerFailureCount. @@ -1685,7 +1685,7 @@ public class PackageWatchdog { /** * Notes the timestamp of a mitigation call into the observer. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public void noteMitigationCallLocked() { mMitigationCalls.addLast(mSystemClock.uptimeMillis()); } @@ -1696,7 +1696,7 @@ public class PackageWatchdog { * * @return the number of mitigation calls made in the de-escalation window. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public int getMitigationCountLocked() { try { final long now = mSystemClock.uptimeMillis(); @@ -1716,7 +1716,7 @@ public class PackageWatchdog { * * @return a LongArrayQueue of the mitigation calls relative to the current system uptime. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public LongArrayQueue normalizeMitigationCalls() { LongArrayQueue normalized = new LongArrayQueue(); final long now = mSystemClock.uptimeMillis(); @@ -1731,7 +1731,7 @@ public class PackageWatchdog { * * @return the new health check state */ - @GuardedBy("mLock") + @GuardedBy("sLock") public int setHealthCheckActiveLocked(long initialHealthCheckDurationMs) { if (initialHealthCheckDurationMs <= 0) { Slog.wtf(TAG, "Cannot set non-positive health check duration " @@ -1751,7 +1751,7 @@ public class PackageWatchdog { * * @return the new health check state */ - @GuardedBy("mLock") + @GuardedBy("sLock") public int handleElapsedTimeLocked(long elapsedMs) { if (elapsedMs <= 0) { Slog.w(TAG, "Cannot handle non-positive elapsed time for package " + getName()); @@ -1769,7 +1769,7 @@ public class PackageWatchdog { } /** Explicitly update the monitoring duration of the package. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public void updateHealthCheckDuration(long newDurationMs) { mDurationMs = newDurationMs; } @@ -1780,7 +1780,7 @@ public class PackageWatchdog { * * @return the new {@link HealthCheckState health check state} */ - @GuardedBy("mLock") + @GuardedBy("sLock") @HealthCheckState public int tryPassHealthCheckLocked() { if (mHealthCheckState != HealthCheckState.FAILED) { @@ -1799,7 +1799,7 @@ public class PackageWatchdog { /** * Returns the current {@link HealthCheckState health check state}. */ - @GuardedBy("mLock") + @GuardedBy("sLock") @HealthCheckState public int getHealthCheckStateLocked() { return mHealthCheckState; @@ -1810,7 +1810,7 @@ public class PackageWatchdog { * * @return the duration or {@link Long#MAX_VALUE} if the package should not be scheduled */ - @GuardedBy("mLock") + @GuardedBy("sLock") public long getShortestScheduleDurationMsLocked() { // Consider health check duration only if #isPendingHealthChecksLocked is true return Math.min(toPositive(mDurationMs), @@ -1822,7 +1822,7 @@ public class PackageWatchdog { * Returns {@code true} if the total duration left to monitor the package is less than or * equal to 0 {@code false} otherwise. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean isExpiredLocked() { return mDurationMs <= 0; } @@ -1831,7 +1831,7 @@ public class PackageWatchdog { * Returns {@code true} if the package, {@link #getName} is expecting health check results * {@code false} otherwise. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean isPendingHealthChecksLocked() { return mHealthCheckState == HealthCheckState.ACTIVE || mHealthCheckState == HealthCheckState.INACTIVE; @@ -1843,7 +1843,7 @@ public class PackageWatchdog { * * @return the new {@link HealthCheckState health check state} */ - @GuardedBy("mLock") + @GuardedBy("sLock") @HealthCheckState private int updateHealthCheckStateLocked() { int oldState = mHealthCheckState; @@ -1898,7 +1898,7 @@ public class PackageWatchdog { } } - @GuardedBy("mLock") + @GuardedBy("sLock") @SuppressWarnings("GuardedBy") void saveAllObserversBootMitigationCountToMetadata(String filePath) { HashMap bootMitigationCounts = new HashMap<>(); @@ -2001,7 +2001,7 @@ public class PackageWatchdog { /** Increments the boot counter, and returns whether the device is bootlooping. */ - @GuardedBy("mLock") + @GuardedBy("sLock") public boolean incrementAndTest() { if (Flags.recoverabilityDetection()) { readAllObserversBootMitigationCountIfNecessary(METADATA_FILE); @@ -2042,7 +2042,7 @@ public class PackageWatchdog { } } - @GuardedBy("mLock") + @GuardedBy("sLock") private boolean performedMitigationsDuringWindow() { for (ObserverInternal observerInternal: mAllObservers.values()) { if (observerInternal.getBootMitigationCount() > 0) { @@ -2052,7 +2052,7 @@ public class PackageWatchdog { return false; } - @GuardedBy("mLock") + @GuardedBy("sLock") private void resetAllObserversBootMitigationCount() { for (int i = 0; i < mAllObservers.size(); i++) { final ObserverInternal observer = mAllObservers.valueAt(i); @@ -2061,7 +2061,7 @@ public class PackageWatchdog { saveAllObserversBootMitigationCountToMetadata(METADATA_FILE); } - @GuardedBy("mLock") + @GuardedBy("sLock") @SuppressWarnings("GuardedBy") void readAllObserversBootMitigationCountIfNecessary(String filePath) { File metadataFile = new File(filePath); -- GitLab From 8b8aca054afc33159f2056d3febbdd4f2066b416 Mon Sep 17 00:00:00 2001 From: Harshit Mahajan Date: Fri, 15 Nov 2024 19:58:05 +0000 Subject: [PATCH 113/656] Updating javadoc for few APIs 1. PackageWatchdog#getInstance add context details 2. Details regarding packages in mayObservePackage API Bug: 377635591 Test: NA Flag: android.crashrecovery.flags.enable_crashrecovery Change-Id: Ifabf662d08aeb09afb737b6334758bd2928d79f4 --- .../java/com/android/server/PackageWatchdog.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java index fbf51fd7cca7..10050d69c94b 100644 --- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java @@ -303,7 +303,11 @@ public class PackageWatchdog { sPackageWatchdog = this; } - /** Creates or gets singleton instance of PackageWatchdog. */ + /** + * Creates or gets singleton instance of PackageWatchdog. + * + * @param context The system server context. + */ public static @NonNull PackageWatchdog getInstance(@NonNull Context context) { synchronized (sPackageWatchdogLock) { if (sPackageWatchdog == null) { @@ -841,7 +845,10 @@ public class PackageWatchdog { /** * Returns {@code true} if this observer wishes to observe the given package, {@code false} - * otherwise + * otherwise. + * Any failing package can be passed on to the observer. Currently the packages that have + * ANRs and perform {@link android.service.watchdog.ExplicitHealthCheckService} are being + * passed to observers in these API. * *

A persistent observer may choose to start observing certain failing packages, even if * it has not explicitly asked to watch the package with {@link #startObservingHealth}. -- GitLab From 20c6c55244b23478035a935cb3a7a043c5ea838c Mon Sep 17 00:00:00 2001 From: Toshiki Kikuchi Date: Thu, 14 Nov 2024 11:39:33 +0900 Subject: [PATCH 114/656] Define a flicker test for no-desktop-task-limit This CL adds a flicker test to open many apps on a device without desktop task limit. Bug: 376802328 Flag: EXEMPT add test Test: atest WMShellFlickerTestsDesktopMode:com.android.wm.shell.flicker.OpenUnlimitedApps Change-Id: If5488984f42d57417f23d7e55c3f5411013a7acd --- .../flicker/DesktopModeFlickerScenarios.kt | 21 +++++++++ .../wm/shell/flicker/OpenUnlimitedApps.kt | 47 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt index 88dc5489b404..0cc8b0c7ba8d 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt @@ -50,6 +50,7 @@ import android.tools.flicker.config.common.Components.LAUNCHER import android.tools.flicker.config.desktopmode.Components.DESKTOP_MODE_APP import android.tools.flicker.config.desktopmode.Components.DESKTOP_WALLPAPER import android.tools.flicker.config.desktopmode.Components.NON_RESIZABLE_APP +import android.tools.flicker.config.desktopmode.Components.SIMPLE_APP import android.tools.flicker.extractors.ITransitionMatcher import android.tools.flicker.extractors.ShellTransitionScenarioExtractor import android.tools.flicker.extractors.TaggedCujTransitionMatcher @@ -444,5 +445,25 @@ class DesktopModeFlickerScenarios { AppWindowOnTopAtEnd(LAUNCHER), ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }) ) + val OPEN_UNLIMITED_APPS = + FlickerConfigEntry( + scenarioId = ScenarioId("OPEN_UNLIMITED_APPS"), + extractor = + ShellTransitionScenarioExtractor( + transitionMatcher = + object : ITransitionMatcher { + override fun findAll( + transitions: Collection + ): Collection { + return transitions.filter { it.type == TransitionType.OPEN } + } + } + ), + assertions = + listOf( + AppWindowBecomesVisible(DESKTOP_MODE_APP), + AppWindowIsVisibleAlways(SIMPLE_APP) + ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }), + ) } } diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt new file mode 100644 index 000000000000..0a3984612987 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/OpenUnlimitedApps.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker + +import android.tools.flicker.FlickerConfig +import android.tools.flicker.annotation.ExpectedScenarios +import android.tools.flicker.annotation.FlickerConfigProvider +import android.tools.flicker.config.FlickerConfig +import android.tools.flicker.config.FlickerServiceConfig +import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner +import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.OPEN_UNLIMITED_APPS +import com.android.wm.shell.scenarios.OpenUnlimitedApps +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Open many apps on the device without the window limit. + * + * Assert that the desktop task limit is not triggered. + */ +@RunWith(FlickerServiceJUnit4ClassRunner::class) +class OpenUnlimitedApps : OpenUnlimitedApps() { + @ExpectedScenarios(["OPEN_UNLIMITED_APPS"]) + @Test + override fun openUnlimitedApps() = super.openUnlimitedApps() + + companion object { + @JvmStatic + @FlickerConfigProvider + fun flickerConfigProvider(): FlickerConfig = + FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(OPEN_UNLIMITED_APPS) + } +} -- GitLab From 1f3201efc34a65a8bb6718ba0fb247b19d2d9a67 Mon Sep 17 00:00:00 2001 From: Toshiki Kikuchi Date: Mon, 11 Nov 2024 10:53:39 +0900 Subject: [PATCH 115/656] Generalize MinimizeWindowOnAppOpen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This CL generalizes MinimizeWindowOnAppOpen to perform verifications based on well-defined “max task limit”. The test currently implicitly assumes Tangor whose max task limit is 4. Bug: 376802328 Flag: EXEMPT add test Test: MinimizeWindowOnAppOpen Change-Id: I28fcea4cf09b128f4b51f0e97ffc8a1fc0a80a29 --- .../scenarios/MinimizeWindowOnAppOpen.kt | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt index 7987f7ec59fa..a246326f1137 100644 --- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeWindowOnAppOpen.kt @@ -23,12 +23,10 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.helpers.DesktopModeAppHelper -import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.LetterboxAppHelper import com.android.server.wm.flicker.helpers.MailAppHelper -import com.android.server.wm.flicker.helpers.NewTasksAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.window.flags.Flags +import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import org.junit.After import org.junit.Assume import org.junit.Before @@ -51,32 +49,30 @@ open class MinimizeWindowOnAppOpen() private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) private val mailApp = DesktopModeAppHelper(MailAppHelper(instrumentation)) - private val newTasksApp = DesktopModeAppHelper(NewTasksAppHelper(instrumentation)) - private val imeApp = DesktopModeAppHelper(ImeAppHelper(instrumentation)) - private val letterboxAppHelper = DesktopModeAppHelper(LetterboxAppHelper(instrumentation)) + + private val maxNum = DesktopModeStatus.getMaxTaskLimit(instrumentation.context) @Before fun setup() { Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + Assume.assumeTrue(maxNum > 0) testApp.enterDesktopMode(wmHelper, device) - mailApp.launchViaIntent(wmHelper) - newTasksApp.launchViaIntent(wmHelper) - imeApp.launchViaIntent(wmHelper) + // Launch new [maxNum-1] tasks, which ends up opening [maxNum] tasks in total. + for (i in 1..maxNum - 1) { + mailApp.launchViaIntent(wmHelper) + } } @Test open fun openAppToMinimizeWindow() { - // Launch a new app while 4 apps are already open on desktop. This should result in the - // first app we opened to be minimized. - letterboxAppHelper.launchViaIntent(wmHelper) + // Launch a new tasks, which ends up opening [maxNum]+1 tasks in total. This should + // result in the first app we opened to be minimized. + mailApp.launchViaIntent(wmHelper) } @After fun teardown() { testApp.exit(wmHelper) mailApp.exit(wmHelper) - newTasksApp.exit(wmHelper) - imeApp.exit(wmHelper) - letterboxAppHelper.exit(wmHelper) } } -- GitLab From eaf8dd7b2dd9888d96119c98caec1acd9f7f9136 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 15 Oct 2024 15:35:53 -0700 Subject: [PATCH 116/656] Add StageOrderOperator to keep track of stages indicies * Brings parity with everything except double tap to swap for using StageOrderOperator instead of directly using mMainStage and mSideStage Bug: 349828130 Flag: com.android.wm.shell.enable_flexible_split Change-Id: I660c1636601381f324e5978b18d2c207dece7579 --- .../desktopmode/DesktopTasksController.kt | 11 +- .../wm/shell/draganddrop/SplitDragPolicy.java | 45 +- .../anim/TwoFiftyFiftyTargetAnimator.kt | 12 +- .../wm/shell/splitscreen/SplitScreen.java | 22 +- .../splitscreen/SplitScreenController.java | 45 +- .../splitscreen/SplitScreenTransitions.java | 26 +- .../shell/splitscreen/StageCoordinator.java | 905 ++++++++++++++---- .../shell/splitscreen/StageOrderOperator.kt | 185 ++++ .../shell/splitscreen/StageTaskListener.java | 64 +- .../desktopmode/DesktopTasksControllerTest.kt | 5 +- .../draganddrop/SplitDragPolicyTest.java | 11 +- .../SplitScreenControllerTests.java | 28 +- .../splitscreen/SplitTransitionTests.java | 5 +- .../splitscreen/StageCoordinatorTests.java | 10 +- .../splitscreen/StageTaskListenerTests.java | 5 +- 15 files changed, 1100 insertions(+), 279 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 0a39076a8768..6fc6eb783a17 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -70,6 +70,8 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.policy.ScreenDecorationsUtils import com.android.internal.protolog.ProtoLog import com.android.window.flags.Flags +import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut +import com.android.wm.shell.Flags.enableFlexibleSplit import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer @@ -102,6 +104,7 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus.useDesktopOverrideDensity import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT import com.android.wm.shell.splitscreen.SplitScreenController @@ -1512,11 +1515,15 @@ class DesktopTasksController( WINDOWING_MODE_MULTI_WINDOW -> { val splitPosition = splitScreenController .determineNewInstancePosition(callingTaskInfo) + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // specific cases in the future. + val splitIndex = if (enableFlexibleSplit()) + splitScreenController.determineNewInstanceIndex(callingTaskInfo) else + SPLIT_INDEX_UNDEFINED splitScreenController.startIntent( launchIntent, context.userId, fillIn, splitPosition, options.toBundle(), null /* hideTaskToken */, - true /* forceLaunchNewTask */ - ) + true /* forceLaunchNewTask */, splitIndex) } WINDOWING_MODE_FREEFORM -> { val wct = WindowContainerTransaction() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java index 5d22c1edf8fe..ae9d21f621de 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/SplitDragPolicy.java @@ -41,6 +41,9 @@ import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT import static com.android.wm.shell.draganddrop.SplitDragPolicy.Target.TYPE_SPLIT_TOP; import static com.android.wm.shell.shared.draganddrop.DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -81,6 +84,7 @@ import com.android.wm.shell.draganddrop.anim.HoverAnimProps; import com.android.wm.shell.draganddrop.anim.TwoFiftyFiftyTargetAnimator; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.split.SplitScreenConstants; +import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -219,8 +223,10 @@ public class SplitDragPolicy implements DropTarget { displayRegion.splitHorizontally(startHitRegion, endHitRegion); } - mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds, -1)); - mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_LEFT, startHitRegion, startBounds, + SPLIT_INDEX_0)); + mTargets.add(new Target(TYPE_SPLIT_RIGHT, endHitRegion, endBounds, + SPLIT_INDEX_1)); } else { // TODO(b/349828130), move this into init function and/or the insets updating // callback @@ -287,9 +293,10 @@ public class SplitDragPolicy implements DropTarget { displayRegion.splitVertically(leftHitRegion, rightHitRegion); } - mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds, + SPLIT_INDEX_UNDEFINED)); mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds, - -1)); + SPLIT_INDEX_UNDEFINED)); } else { final Rect topHitRegion = new Rect(); final Rect bottomHitRegion = new Rect(); @@ -308,9 +315,10 @@ public class SplitDragPolicy implements DropTarget { displayRegion.splitHorizontally(topHitRegion, bottomHitRegion); } - mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds, -1)); + mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds, + SPLIT_INDEX_UNDEFINED)); mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds, - -1)); + SPLIT_INDEX_UNDEFINED)); } } } else { @@ -378,9 +386,9 @@ public class SplitDragPolicy implements DropTarget { ? mFullscreenStarter : mSplitscreenStarter; if (mSession.appData != null) { - launchApp(mSession, starter, position, hideTaskToken); + launchApp(mSession, starter, position, hideTaskToken, target.index); } else { - launchIntent(mSession, starter, position, hideTaskToken); + launchIntent(mSession, starter, position, hideTaskToken, target.index); } if (enableFlexibleSplit()) { @@ -392,9 +400,10 @@ public class SplitDragPolicy implements DropTarget { * Launches an app provided by SysUI. */ private void launchApp(DragSession session, Starter starter, @SplitPosition int position, - @Nullable WindowContainerToken hideTaskToken) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d", - position); + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int splitIndex) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Launching app data at position=%d index=%d", + position, splitIndex); final ClipDescription description = session.getClipDescription(); final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); @@ -429,7 +438,7 @@ public class SplitDragPolicy implements DropTarget { } } starter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */, - position, opts, hideTaskToken); + position, opts, hideTaskToken, splitIndex); } } @@ -437,7 +446,7 @@ public class SplitDragPolicy implements DropTarget { * Launches an intent sender provided by an application. */ private void launchIntent(DragSession session, Starter starter, @SplitPosition int position, - @Nullable WindowContainerToken hideTaskToken) { + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d", position); final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic(); @@ -452,7 +461,7 @@ public class SplitDragPolicy implements DropTarget { final Bundle opts = baseActivityOpts.toBundle(); starter.startIntent(session.launchableIntent, session.launchableIntent.getCreatorUserHandle().getIdentifier(), - null /* fillIntent */, position, opts, hideTaskToken); + null /* fillIntent */, position, opts, hideTaskToken, index); } @Override @@ -541,7 +550,7 @@ public class SplitDragPolicy implements DropTarget { @Nullable Bundle options, UserHandle user); void startIntent(PendingIntent intent, int userId, Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken); + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index); void enterSplitScreen(int taskId, boolean leftOrTop); /** @@ -592,7 +601,7 @@ public class SplitDragPolicy implements DropTarget { @Override public void startIntent(PendingIntent intent, int userId, @Nullable Intent fillInIntent, int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken) { + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) { if (hideTaskToken != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Default starter does not support hide task token"); @@ -641,13 +650,13 @@ public class SplitDragPolicy implements DropTarget { final Rect hitRegion; // The approximate visual region for where the task will start final Rect drawRegion; - int index; + @SplitIndex int index; /** * @param index 0-indexed, represents which position of drop target this object represents, * 0 to N for left to right, top to bottom */ - public Target(@Type int t, Rect hit, Rect draw, int index) { + public Target(@Type int t, Rect hit, Rect draw, @SplitIndex int index) { type = t; hitRegion = hit; drawRegion = draw; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt index 9f532f57961d..54619528f2bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/anim/TwoFiftyFiftyTargetAnimator.kt @@ -22,6 +22,10 @@ import android.graphics.Rect import com.android.wm.shell.R import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.draganddrop.SplitDragPolicy.Target +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_2 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_3 /** * Represents Drop Zone targets and animations for when the system is currently in a 2 app 50/50 @@ -98,7 +102,7 @@ class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { farStartBounds.right + halfDividerWidth, farStartBounds.bottom ), - farStartBounds, 0 + farStartBounds, SPLIT_INDEX_0 ) ) targets.add( @@ -110,7 +114,7 @@ class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { startBounds.right + halfDividerWidth, startBounds.bottom ), - startBounds, 1 + startBounds, SPLIT_INDEX_1 ) ) targets.add( @@ -120,7 +124,7 @@ class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { endBounds.left - halfDividerWidth, endBounds.top, endBounds.right, endBounds.bottom ), - endBounds, 2 + endBounds, SPLIT_INDEX_2 ) ) targets.add( @@ -130,7 +134,7 @@ class TwoFiftyFiftyTargetAnimator : DropTargetAnimSupplier { farEndBounds.left - halfDividerWidth, farEndBounds.top, farEndBounds.right, farEndBounds.bottom ), - farEndBounds, 3 + farEndBounds, SPLIT_INDEX_3 ) ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 3e6d36ce0ca3..39ed9abcfd26 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -54,10 +54,27 @@ public interface SplitScreen { */ int STAGE_TYPE_SIDE = 1; + /** + * Position independent stage identifier for a given Stage + */ + int STAGE_TYPE_A = 2; + /** + * Position independent stage identifier for a given Stage + */ + int STAGE_TYPE_B = 3; + /** + * Position independent stage identifier for a given Stage + */ + int STAGE_TYPE_C = 4; + @IntDef(prefix = { "STAGE_TYPE_" }, value = { STAGE_TYPE_UNDEFINED, STAGE_TYPE_MAIN, - STAGE_TYPE_SIDE + STAGE_TYPE_SIDE, + // Used for flexible split + STAGE_TYPE_A, + STAGE_TYPE_B, + STAGE_TYPE_C }) @interface StageType {} @@ -128,6 +145,9 @@ public interface SplitScreen { case STAGE_TYPE_UNDEFINED: return "UNDEFINED"; case STAGE_TYPE_MAIN: return "MAIN"; case STAGE_TYPE_SIDE: return "SIDE"; + case STAGE_TYPE_A: return "STAGE_A"; + case STAGE_TYPE_B: return "STAGE_B"; + case STAGE_TYPE_C: return "STAGE_C"; default: return "UNKNOWN(" + stage + ")"; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 6398d31b4f82..4f0f6760a951 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -24,6 +24,7 @@ import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.common.MultiInstanceHelper.getComponent; import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent; import static com.android.wm.shell.common.MultiInstanceHelper.samePackage; @@ -33,6 +34,9 @@ import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMes import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -98,6 +102,7 @@ import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.annotations.ExternalThread; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; +import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.sysui.KeyguardChangeListener; @@ -325,7 +330,6 @@ public class SplitScreenController implements SplitDragPolicy.Starter, /** * @return an Array of RunningTaskInfo's ordered by leftToRight or topTopBottom */ - @Nullable public ActivityManager.RunningTaskInfo[] getAllTaskInfos() { // TODO(b/349828130) Add the third stage task info and not rely on positions ActivityManager.RunningTaskInfo topLeftTask = getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); @@ -335,7 +339,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, return new ActivityManager.RunningTaskInfo[]{topLeftTask, bottomRightTask}; } - return null; + return new ActivityManager.RunningTaskInfo[0]; } /** Check task is under split or not by taskId. */ @@ -405,7 +409,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, public void prepareEnterSplitScreen(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo, int startPosition) { mStageCoordinator.prepareEnterSplitScreen(wct, taskInfo, startPosition, - false /* resizeAnim */); + false /* resizeAnim */, SPLIT_INDEX_UNDEFINED); } /** @@ -451,6 +455,24 @@ public class SplitScreenController implements SplitDragPolicy.Starter, } } + /** + * Determines which split index a new instance of a task should take. + * @param callingTask The task requesting a new instance. + * @return the split index of the new instance + */ + @SplitIndex + public int determineNewInstanceIndex(@NonNull ActivityManager.RunningTaskInfo callingTask) { + if (!enableFlexibleSplit()) { + throw new IllegalStateException("Use determineNewInstancePosition"); + } + if (callingTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + || getSplitPosition(callingTask.taskId) == SPLIT_POSITION_TOP_OR_LEFT) { + return SPLIT_INDEX_1; + } else { + return SPLIT_INDEX_0; + } + } + public void enterSplitScreen(int taskId, boolean leftOrTop) { enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); } @@ -685,7 +707,10 @@ public class SplitScreenController implements SplitDragPolicy.Starter, ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntentWithInstanceId: reason=%d", ENTER_REASON_LAUNCHER); mStageCoordinator.getLogger().enterRequested(instanceId, ENTER_REASON_LAUNCHER); - startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */); + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // specific cases in the future. Only focusing on parity with starting intent/task + startIntent(intent, userId, fillInIntent, position, options, null /* hideTaskToken */, + SPLIT_INDEX_UNDEFINED); } private void startIntentAndTask(PendingIntent pendingIntent, int userId1, @@ -775,9 +800,9 @@ public class SplitScreenController implements SplitDragPolicy.Starter, @Override public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken) { + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) { startIntent(intent, userId1, fillInIntent, position, options, hideTaskToken, - false /* forceLaunchNewTask */); + false /* forceLaunchNewTask */, index); } /** @@ -790,7 +815,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter, */ public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask) { + @Nullable WindowContainerToken hideTaskToken, boolean forceLaunchNewTask, + @SplitIndex int index) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1, fillInIntent, position); @@ -816,7 +842,7 @@ public class SplitScreenController implements SplitDragPolicy.Starter, if (taskInfo != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Found suitable background task=%s", taskInfo); - mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken); + mStageCoordinator.startTask(taskInfo.taskId, position, options, hideTaskToken, index); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Start task in background"); return; @@ -841,7 +867,8 @@ public class SplitScreenController implements SplitDragPolicy.Starter, } } - mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken); + mStageCoordinator.startIntent(intent, fillInIntent, position, options, hideTaskToken, + index); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 840049412db4..3091be574a53 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; import static com.android.wm.shell.shared.animation.Interpolators.ALPHA_IN; @@ -55,6 +56,9 @@ import com.android.wm.shell.transition.OneShotRemoteHandler; import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; /** Manages transition animations for split-screen. */ @@ -268,22 +272,21 @@ class SplitScreenTransitions { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, - @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) { + @NonNull Map rootDecorMap) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId()); initTransition(transition, finishTransaction, finishCallback); + Set rootDecorKeys = rootDecorMap.keySet(); for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) { + if (rootDecorKeys.contains(change.getContainer())) { final SurfaceControl leash = change.getLeash(); startTransaction.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top); startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(), change.getEndAbsBounds().height()); - SplitDecorManager decor = mainRoot.equals(change.getContainer()) - ? mainDecor : sideDecor; + SplitDecorManager decor = rootDecorMap.get(change.getContainer()); // This is to ensure onFinished be called after all animations ended. ValueAnimator va = new ValueAnimator(); @@ -433,15 +436,22 @@ class SplitScreenTransitions { Transitions.TransitionHandler handler, @Nullable TransitionConsumedCallback consumedCallback, @Nullable TransitionFinishedCallback finishCallback, - @NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) { + @Nullable SplitDecorManager mainDecor, @Nullable SplitDecorManager sideDecor, + @Nullable List decorManagers) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition deduced Resize split screen."); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b", mPendingResize != null); if (mPendingResize != null) { mPendingResize.cancel(null); - mainDecor.cancelRunningAnimations(); - sideDecor.cancelRunningAnimations(); + if (enableFlexibleSplit()) { + for (SplitDecorManager stage : decorManagers) { + stage.cancelRunningAnimations(); + } + } else { + mainDecor.cancelRunningAnimations(); + sideDecor.cancelRunningAnimations(); + } mAnimations.clear(); onFinish(null /* wct */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index e692c61cd493..f07ae4465341 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -33,6 +33,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenUtils.isPartiallyOffscreen; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; @@ -43,6 +44,7 @@ import static com.android.wm.shell.shared.TransitionUtil.isOpeningType; import static com.android.wm.shell.shared.TransitionUtil.isOrderOnly; import static com.android.wm.shell.shared.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1; @@ -51,9 +53,12 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.shared.split.SplitScreenConstants.splitPositionToString; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_A; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_B; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; +import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED; @@ -142,6 +147,7 @@ import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.TransitionUtil; import com.android.wm.shell.shared.split.SplitBounds; import com.android.wm.shell.shared.split.SplitScreenConstants.PersistentSnapPosition; +import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex; import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason; @@ -153,11 +159,15 @@ import dalvik.annotation.optimization.NeverCompile; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Predicate; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen stages. @@ -178,10 +188,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // entered private static final int DISABLE_LAUNCH_ADJACENT_AFTER_ENTER_TIMEOUT_MS = 1000; - private final StageTaskListener mMainStage; - private final StageTaskListener mSideStage; + private StageTaskListener mMainStage; + private StageTaskListener mSideStage; @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; + private StageOrderOperator mStageOrderOperator; private final int mDisplayId; private SplitLayout mSplitLayout; @@ -335,22 +346,32 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task"); - mMainStage = new StageTaskListener( - mContext, - mTaskOrganizer, - mDisplayId, - this /*stageListenerCallbacks*/, - mSyncQueue, - iconProvider, - mWindowDecorViewModel); - mSideStage = new StageTaskListener( - mContext, - mTaskOrganizer, - mDisplayId, - this /*stageListenerCallbacks*/, - mSyncQueue, - iconProvider, - mWindowDecorViewModel); + if (enableFlexibleSplit()) { + mStageOrderOperator = new StageOrderOperator(mContext, + mTaskOrganizer, + mDisplayId, + this /*stageListenerCallbacks*/, + mSyncQueue, + iconProvider, + mWindowDecorViewModel); + } else { + mMainStage = new StageTaskListener( + mContext, + mTaskOrganizer, + mDisplayId, + this /*stageListenerCallbacks*/, + mSyncQueue, + iconProvider, + mWindowDecorViewModel, STAGE_TYPE_MAIN); + mSideStage = new StageTaskListener( + mContext, + mTaskOrganizer, + mDisplayId, + this /*stageListenerCallbacks*/, + mSyncQueue, + iconProvider, + mWindowDecorViewModel, STAGE_TYPE_SIDE); + } mDisplayController = displayController; mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; @@ -422,24 +443,63 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } public boolean isSplitScreenVisible() { - return mSideStage.mVisible && mMainStage.mVisible; + if (enableFlexibleSplit()) { + return runForActiveStagesAllMatch((stage) -> stage.mVisible); + } else { + return mSideStage.mVisible && mMainStage.mVisible; + } } - private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) { - mMainStage.activate(wct, includingTopTask); + /** + * @param includingTopTask reparents the current top task into the stage defined by index + * (or mainStage in legacy split) + * @param index the index to move the current visible task into, if undefined will arbitrarily + * choose a stage to launch into + */ + private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask, + int index) { + if (enableFlexibleSplit()) { + mStageOrderOperator.onEnteringSplit(SNAP_TO_2_50_50); + if (index == SPLIT_INDEX_UNDEFINED || !includingTopTask) { + // If we aren't includingTopTask, then the call to activate on the stage is + // effectively a no-op. Previously the stage kept track of the "isActive" state, + // but now that gets set in the "onEnteringSplit" call above. + // + // index == UNDEFINED case might change, but as of now no use case where we activate + // without an index specified. + return; + } + @SplitIndex int oppositeIndex = index == SPLIT_INDEX_0 ? SPLIT_INDEX_1 : SPLIT_INDEX_0; + StageTaskListener activatingStage = mStageOrderOperator.getStageForIndex(oppositeIndex); + activatingStage.activate(wct, includingTopTask); + } else { + mMainStage.activate(wct, includingTopTask); + } } public boolean isSplitActive() { - return mMainStage.isActive(); + if (enableFlexibleSplit()) { + return mStageOrderOperator.isActive(); + } else { + return mMainStage.isActive(); + } } /** * Deactivates main stage by removing the stage from the top level split root (usually when a * task underneath gets removed from the stage root). - * @param reparentToTop whether we want to put the stage root back on top + * @param stageToTop stage which we want to put on top */ - private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) { - mMainStage.deactivate(wct, reparentToTop); + private void deactivateSplit(WindowContainerTransaction wct, @StageType int stageToTop) { + if (enableFlexibleSplit()) { + StageTaskListener stageToDeactivate = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.getId() == stageToTop) + .findFirst().orElseThrow(); + stageToDeactivate.deactivate(wct, true /*toTop*/); + mStageOrderOperator.onExitingSplit(); + } else { + mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); + } } /** @return whether this transition-request has the launch-adjacent flag. */ @@ -463,11 +523,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If one of the splitting tasks support auto-pip, wm-core might reparent the task to TDA // and file a TRANSIT_PIP transition when finishing transitions. // @see com.android.server.wm.RootWindowContainer#moveActivityToPinnedRootTask - if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { - return true; + if (enableFlexibleSplit()) { + return mStageOrderOperator.getActiveStages().stream() + .anyMatch(stage -> stage.getChildCount() == 0); + } else { + return mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0; } - - return false; } /** Checks if `transition` is a pending enter-split transition. */ @@ -477,10 +538,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @StageType int getStageOfTask(int taskId) { - if (mMainStage.containsTask(taskId)) { - return STAGE_TYPE_MAIN; - } else if (mSideStage.containsTask(taskId)) { - return STAGE_TYPE_SIDE; + if (enableFlexibleSplit()) { + StageTaskListener stageTaskListener = mStageOrderOperator.getActiveStages().stream() + .filter(stage -> stage.containsTask(taskId)) + .findFirst().orElse(null); + if (stageTaskListener != null) { + return stageTaskListener.getId(); + } + } else { + if (mMainStage.containsTask(taskId)) { + return STAGE_TYPE_MAIN; + } else if (mSideStage.containsTask(taskId)) { + return STAGE_TYPE_SIDE; + } } return STAGE_TYPE_UNDEFINED; @@ -490,14 +560,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskId) { return true; } - return mMainStage.isRootTaskId(taskId) || mSideStage.isRootTaskId(taskId); + if (enableFlexibleSplit()) { + return mStageOrderOperator.getActiveStages().stream() + .anyMatch((stage) -> stage.isRootTaskId(taskId)); + } else { + return mMainStage.isRootTaskId(taskId) || mSideStage.isRootTaskId(taskId); + } } boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition, WindowContainerTransaction wct) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId, stagePosition); - prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */); + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // specific cases in the future. Only focusing on parity with starting intent/task + prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */, + SPLIT_INDEX_UNDEFINED); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, isSplitScreenVisible() @@ -595,11 +673,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * same window container transaction as the starting of the intent. */ void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options, - @Nullable WindowContainerToken hideTaskToken) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position); + @Nullable WindowContainerToken hideTaskToken, @SplitIndex int index) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d index=%d", + taskId, position, index); mSplitRequest = new SplitRequest(taskId, position); final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + options = enableFlexibleSplit() + ? resolveStartStageForIndex(options, null /*wct*/, index) + : resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); if (hideTaskToken != null) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom"); wct.reorder(hideTaskToken, false /* onTop */); @@ -623,7 +704,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; - prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); + prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, extraTransitType, !mIsDropEntering); @@ -635,13 +716,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * same window container transaction as the starting of the intent. */ void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, - @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken) { + @Nullable Bundle options, @Nullable WindowContainerToken hideTaskToken, + @SplitIndex int index) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(), position); mSplitRequest = new SplitRequest(intent.getIntent(), position); final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); + options = enableFlexibleSplit() + ? resolveStartStageForIndex(options, null /*wct*/, index) + : resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); if (hideTaskToken != null) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Reordering hide-task to bottom"); wct.reorder(hideTaskToken, false /* onTop */); @@ -666,13 +750,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; - prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); + prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering, index); mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct, null, this, extraTransitType, !mIsDropEntering); } - /** Starts 2 tasks in one transition. */ + /** + * Starts 2 tasks in one transition. + * @param taskId1 starts in the mSideStage + * @param taskId2 starts in the mainStage #startWithTask() + */ void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2, @SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { @@ -687,11 +775,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); - addActivityOptions(options1, mSideStage); + StageTaskListener stageForTask1; + if (enableFlexibleSplit()) { + stageForTask1 = mStageOrderOperator.getStageForLegacyPosition(splitPosition, + true /*checkAllStagesIfNotActive*/); + } else { + stageForTask1 = mSideStage; + } + addActivityOptions(options1, stageForTask1); prepareTasksForSplitScreen(new int[] {taskId1, taskId2}, wct); wct.startTask(taskId1, options1); - startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId); + startWithTask(wct, taskId2, options2, snapPosition, remoteTransition, instanceId, + splitPosition); } /** Start an intent and a task to a split pair in one transition. */ @@ -721,7 +817,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.sendPendingIntent(pendingIntent, fillInIntent, options1); prepareTasksForSplitScreen(new int[] {taskId}, wct); - startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); + startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId, + splitPosition); } /** @@ -765,7 +862,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1); prepareTasksForSplitScreen(new int[] {taskId}, wct); - startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId); + startWithTask(wct, taskId, options2, snapPosition, remoteTransition, instanceId, + splitPosition); } /** @@ -795,11 +893,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ private void startWithTask(WindowContainerTransaction wct, int mainTaskId, @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition, - @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { + @Nullable RemoteTransition remoteTransition, InstanceId instanceId, + @SplitPosition int splitPosition) { if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. - activateSplit(wct, false /* reparentToTop */); + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // specific cases in the future. Only focusing on parity with starting intent/task + activateSplit(wct, false /* reparentToTop */, SPLIT_INDEX_UNDEFINED); } mSplitLayout.setDivideRatio(snapPosition); updateWindowBounds(mSplitLayout, wct); @@ -807,10 +908,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false /* reparentLeafTaskIfRelaunch */); setRootForceTranslucent(false, wct); - + // All callers of this method set the correct activity options on mSideStage, + // so we choose the opposite stage for this method + StageTaskListener stage; + if (enableFlexibleSplit()) { + stage = mStageOrderOperator + .getStageForLegacyPosition(reverseSplitPosition(splitPosition), + false /*checkAllStagesIfNotActive*/); + } else { + stage = mMainStage; + } // Make sure the launch options will put tasks in the corresponding split roots mainOptions = mainOptions != null ? mainOptions : new Bundle(); - addActivityOptions(mainOptions, mMainStage); + addActivityOptions(mainOptions, stage); // Add task launch requests wct.startTask(mainTaskId, mainOptions); @@ -866,7 +976,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. - activateSplit(wct, false /* reparentToTop */); + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // specific cases in the future. Only focusing on parity with starting intent/task + activateSplit(wct, false /* reparentToTop */, SPLIT_INDEX_UNDEFINED); } setSideStagePosition(splitPosition, wct); @@ -974,6 +1086,31 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.evictInvisibleChildren(wct); } + /** + * @param index for the new stage that will be opening. Ex. if app is dragged to + * index=1, then this will tell the stage at index=1 to launch the task + * in the wct in that stage. This doesn't verify that the non-specified + * indices' stages have their tasks correctly set/re-parented. + */ + Bundle resolveStartStageForIndex(@Nullable Bundle options, + @Nullable WindowContainerTransaction wct, + @SplitIndex int index) { + StageTaskListener oppositeStage; + if (index == SPLIT_INDEX_UNDEFINED) { + // Arbitrarily choose a stage + oppositeStage = mStageOrderOperator.getStageForIndex(SPLIT_INDEX_1); + } else { + oppositeStage = mStageOrderOperator.getStageForIndex(index); + } + if (options == null) { + options = new Bundle(); + } + updateStageWindowBoundsForIndex(wct, index); + addActivityOptions(options, oppositeStage); + + return options; + } + Bundle resolveStartStage(@StageType int stage, @SplitPosition int position, @Nullable Bundle options, @Nullable WindowContainerTransaction wct) { switch (stage) { @@ -1041,20 +1178,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return INVALID_TASK_ID; } - return mSideStagePosition == splitPosition - ? mSideStage.getTopVisibleChildTaskId() - : mMainStage.getTopVisibleChildTaskId(); + if (enableFlexibleSplit()) { + StageTaskListener stage = mStageOrderOperator.getStageForLegacyPosition(splitPosition, + true /*checkAllStagesIfNotActive*/); + return stage != null ? stage.getTopVisibleChildTaskId() : INVALID_TASK_ID; + } else { + return mSideStagePosition == splitPosition + ? mSideStage.getTopVisibleChildTaskId() + : mMainStage.getTopVisibleChildTaskId(); + } } void switchSplitPosition(String reason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition"); final SurfaceControl.Transaction t = mTransactionPool.acquire(); mTempRect1.setEmpty(); - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - + final StageTaskListener topLeftStage; + final StageTaskListener bottomRightStage; + if (enableFlexibleSplit()) { + topLeftStage = mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT, + false /*checkAllStagesIfNotActive*/); + bottomRightStage = mStageOrderOperator + .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, + false /*checkAllStagesIfNotActive*/); + } else { + topLeftStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; + bottomRightStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + } // Don't allow windows or divider to be focused during animation (mRootTaskInfo is the // parent of all 3 leaves). We don't want the user to be able to tap and focus a window // while it is moving across the screen, because granting focus also recalculates the @@ -1091,9 +1243,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason); - mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLeftRightSplit()); + if (enableFlexibleSplit()) { + // TODO(b/374825718) update logging for 2+ apps + } else { + mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), + getSideStagePosition(), mSideStage.getTopChildTaskUid(), + mSplitLayout.isLeftRightSplit()); + } } void setSideStagePosition(@SplitPosition int sideStagePosition, @@ -1101,8 +1257,26 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mSideStagePosition == sideStagePosition) return; mSideStagePosition = sideStagePosition; sendOnStagePositionChanged(); + StageTaskListener stage = enableFlexibleSplit() + ? mStageOrderOperator.getStageForLegacyPosition(mSideStagePosition, + true /*checkAllStagesIfNotActive*/) + : mSideStage; - if (mSideStage.mVisible) { + if (stage.mVisible) { + if (wct == null) { + // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds. + onLayoutSizeChanged(mSplitLayout); + } else { + updateWindowBounds(mSplitLayout, wct); + sendOnBoundsChanged(); + } + } + } + + private void updateStageWindowBoundsForIndex(@Nullable WindowContainerTransaction wct, + @SplitIndex int index) { + StageTaskListener stage = mStageOrderOperator.getStageForIndex(index); + if (stage.mVisible) { if (wct == null) { // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds. onLayoutSizeChanged(mSplitLayout); @@ -1152,10 +1326,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void recordLastActiveStage() { if (!isSplitActive() || !isSplitScreenVisible()) { mLastActiveStage = STAGE_TYPE_UNDEFINED; - } else if (mMainStage.isFocused()) { - mLastActiveStage = STAGE_TYPE_MAIN; - } else if (mSideStage.isFocused()) { - mLastActiveStage = STAGE_TYPE_SIDE; + } else if (enableFlexibleSplit()) { + mStageOrderOperator.getActiveStages().stream() + .filter(StageTaskListener::isFocused) + .findFirst() + .ifPresent(stage -> mLastActiveStage = stage.getId()); + } else { + if (mMainStage.isFocused()) { + mLastActiveStage = STAGE_TYPE_MAIN; + } else if (mSideStage.isFocused()) { + mLastActiveStage = STAGE_TYPE_SIDE; + } } } @@ -1212,7 +1393,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.getInvisibleBounds(mTempRect1); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { mSideStage.removeAllTasks(wct, false /* toTop */); - deactivateSplit(wct, false /* reparentToTop */); + deactivateSplit(wct, STAGE_TYPE_UNDEFINED); wct.reorder(mRootTaskInfo.token, false /* onTop */); setRootForceTranslucent(true, wct); wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); @@ -1241,7 +1422,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, childrenToTop.fadeOutDecor(() -> { WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); mIsExiting = false; - deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */); + deactivateSplit(finishedWCT, childrenToTop.getId()); mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */); finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */); setRootForceTranslucent(true, finishedWCT); @@ -1369,8 +1550,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again - mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); - mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); + if (enableFlexibleSplit()) { + runForActiveStages((stage) -> + stage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId))); + } else { + mMainStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); + mSideStage.doForAllChildTasks(taskId -> recentTasks.removeSplitPair(taskId)); + } }); logExit(exitReason); } @@ -1383,15 +1569,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!isSplitActive()) return; - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop); - mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); - deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%s", + stageTypeToString(stageToTop)); + if (enableFlexibleSplit()) { + mStageOrderOperator.getActiveStages().stream() + .filter(stage -> stage.getId() != stageToTop) + .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/)); + } else { + mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); + } + deactivateSplit(wct, stageToTop); } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen"); prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED, - !mIsDropEntering); + !mIsDropEntering, SPLIT_INDEX_UNDEFINED); } /** @@ -1400,7 +1593,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void prepareEnterSplitScreen(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, - boolean resizeAnim) { + boolean resizeAnim, @SplitIndex int index) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b", startPosition, resizeAnim); onSplitScreenEnter(); @@ -1412,7 +1605,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isSplitActive()) { prepareBringSplit(wct, taskInfo, startPosition, resizeAnim); } else { - prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim); + prepareActiveSplit(wct, taskInfo, startPosition, resizeAnim, index); } } @@ -1433,14 +1626,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!mSkipEvictingMainStageChildren) { mMainStage.evictAllChildren(wct); } - mMainStage.reparentTopTask(wct); + // TODO(b/349828130) revisit bring split from BG to FG scenarios + if (enableFlexibleSplit()) { + runForActiveStages(stage -> stage.reparentTopTask(wct)); + } else { + mMainStage.reparentTopTask(wct); + } prepareSplitLayout(wct, resizeAnim); } } + /** + * @param index The index that has already been assigned a stage + */ private void prepareActiveSplit(WindowContainerTransaction wct, @Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition, - boolean resizeAnim) { + boolean resizeAnim, @SplitIndex int index) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b", taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible()); // We handle split visibility itself on shell transition, but sometimes we didn't @@ -1450,7 +1651,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(startPosition, wct); mSideStage.addTask(taskInfo, wct); } - activateSplit(wct, true /* reparentToTop */); + activateSplit(wct, true /* reparentToTop */, index); prepareSplitLayout(wct, resizeAnim); } @@ -1477,8 +1678,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void finishEnterSplitScreen(SurfaceControl.Transaction finishT) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen"); mSplitLayout.update(null, true /* resetImePosition */); - mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash); - mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash); + if (enableFlexibleSplit()) { + runForActiveStages((stage) -> + stage.getSplitDecorManager().inflate(mContext, stage.mRootLeash)); + } else { + mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash); + mSideStage.getSplitDecorManager().inflate(mContext, mSideStage.mRootLeash); + } setDividerVisibility(true, finishT); // Ensure divider surface are re-parented back into the hierarchy at the end of the // transition. See Transition#buildFinishTransaction for more detail. @@ -1492,6 +1698,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitRequest = null; updateRecentTasksSplitPair(); + if (enableFlexibleSplit()) { + // TODO(b/374825718) log 2+ apps + return; + } mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), getMainStagePosition(), mMainStage.getTopChildTaskUid(), getSideStagePosition(), mSideStage.getTopChildTaskUid(), @@ -1503,6 +1713,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, outBottomOrRightBounds.set(mSplitLayout.getBounds2()); } + private void runForActiveStages(Consumer consumer) { + mStageOrderOperator.getActiveStages().forEach(consumer); + } + + private boolean runForActiveStagesAllMatch(Predicate predicate) { + List activeStages = mStageOrderOperator.getActiveStages(); + return !activeStages.isEmpty() && activeStages.stream().allMatch(predicate); + } + @SplitPosition int getSplitPosition(int taskId) { if (mSideStage.getTopVisibleChildTaskId() == taskId) { @@ -1516,6 +1735,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) { ActivityOptions options = ActivityOptions.fromBundle(opts); if (launchTarget != null) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "addActivityOptions setting launch root for stage=%s", + stageTypeToString(launchTarget.getId())); options.setLaunchRootTask(launchTarget.mRootTaskInfo.token); } // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split @@ -1559,8 +1781,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, listener.onSplitBoundsChanged(mSplitLayout.getRootBounds(), getMainStageBounds(), getSideStageBounds()); } - mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE); - mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN); + if (enableFlexibleSplit()) { + // TODO(b/349828130) replace w/ stageID + mStageOrderOperator.getAllStages().forEach( + stage -> stage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_UNDEFINED) + ); + } else { + mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE); + mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN); + } } private void sendOnStagePositionChanged() { @@ -1584,17 +1813,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean present, boolean visible) { int stage; if (present) { - stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; + if (enableFlexibleSplit()) { + stage = stageListener.getId(); + } else { + stage = stageListener == mSideStage ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; + } } else { // No longer on any stage stage = STAGE_TYPE_UNDEFINED; } - if (stage == STAGE_TYPE_MAIN) { - mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - mSplitLayout.isLeftRightSplit()); - } else if (stage == STAGE_TYPE_SIDE) { - mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLeftRightSplit()); + if (!enableFlexibleSplit()) { + if (stage == STAGE_TYPE_MAIN) { + mLogger.logMainStageAppChange(getMainStagePosition(), + mMainStage.getTopChildTaskUid(), + mSplitLayout.isLeftRightSplit()); + } else if (stage == STAGE_TYPE_SIDE) { + mLogger.logSideStageAppChange(getSideStagePosition(), + mSideStage.getTopChildTaskUid(), + mSplitLayout.isLeftRightSplit()); + } } if (present) { updateRecentTasksSplitPair(); @@ -1622,17 +1859,35 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> { Rect topLeftBounds = mSplitLayout.getBounds1(); Rect bottomRightBounds = mSplitLayout.getBounds2(); - int mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId(); - int sideStageTopTaskId = mSideStage.getTopVisibleChildTaskId(); + int sideStageTopTaskId; + int mainStageTopTaskId; + if (enableFlexibleSplit()) { + List activeStages = mStageOrderOperator.getActiveStages(); + if (activeStages.size() != 2) { + sideStageTopTaskId = mainStageTopTaskId = INVALID_TASK_ID; + } else { + // doesn't matter which one we assign to? What matters is the order of 0 and 1? + mainStageTopTaskId = activeStages.get(0).getTopVisibleChildTaskId(); + sideStageTopTaskId = activeStages.get(1).getTopVisibleChildTaskId(); + } + } else { + mainStageTopTaskId = mMainStage.getTopVisibleChildTaskId(); + sideStageTopTaskId= mSideStage.getTopVisibleChildTaskId(); + } boolean sideStageTopLeft = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; int leftTopTaskId; int rightBottomTaskId; - if (sideStageTopLeft) { - leftTopTaskId = sideStageTopTaskId; - rightBottomTaskId = mainStageTopTaskId; - } else { + if (enableFlexibleSplit()) { leftTopTaskId = mainStageTopTaskId; rightBottomTaskId = sideStageTopTaskId; + } else { + if (sideStageTopLeft) { + leftTopTaskId = sideStageTopTaskId; + rightBottomTaskId = mainStageTopTaskId; + } else { + leftTopTaskId = mainStageTopTaskId; + rightBottomTaskId = sideStageTopTaskId; + } } if (Flags.enableFlexibleTwoAppSplit()) { @@ -1741,29 +1996,59 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting @Override public void onRootTaskAppeared() { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b", - mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask); + if (enableFlexibleSplit()) { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s", + mRootTaskInfo); + mStageOrderOperator.getAllStages().forEach(stage -> { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + " onRootStageAppeared stageId=%s hasRoot=%b", + stageTypeToString(stage.getId()), stage.mHasRootTask); + }); + } else { + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, + "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b", + mRootTaskInfo, mMainStage.mHasRootTask, mSideStage.mHasRootTask); + } + boolean notAllStagesHaveRootTask; + if (enableFlexibleSplit()) { + notAllStagesHaveRootTask = mStageOrderOperator.getAllStages().stream() + .anyMatch((stage) -> !stage.mHasRootTask); + } else { + notAllStagesHaveRootTask = !mMainStage.mHasRootTask + || !mSideStage.mHasRootTask; + } // Wait unit all root tasks appeared. - if (mRootTaskInfo == null - || !mMainStage.mHasRootTask - || !mSideStage.mHasRootTask) { + if (mRootTaskInfo == null || notAllStagesHaveRootTask) { return; } final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); - wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); + if (enableFlexibleSplit()) { + mStageOrderOperator.getAllStages().forEach(stage -> + wct.reparent(stage.mRootTaskInfo.token, mRootTaskInfo.token, true)); + } else { + wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); + wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); + } - // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); setRootForceTranslucent(true, wct); - mSplitLayout.getInvisibleBounds(mTempRect1); - wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); + if (!enableFlexibleSplit()) { + //TODO(b/373709676) Need to figure out how adjacentRoots work for flex split + + // Make the stages adjacent to each other so they occlude what's behind them. + wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + mSplitLayout.getInvisibleBounds(mTempRect1); + wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); + } mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top); - }); - mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token); + if (!enableFlexibleSplit()) { + mSyncQueue.runInSync(t -> { + t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top); + }); + mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token); + } else { + // TODO(b/373709676) Need to figure out how adjacentRoots work for flex split + } } @Override @@ -1958,15 +2243,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) { + public void onSnappedToDismiss(boolean closedBottomRightStage, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s", - bottomOrRight, exitReasonToString(exitReason)); - final boolean mainStageToTop = - bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT + closedBottomRightStage, exitReasonToString(exitReason)); + boolean mainStageToTop = + closedBottomRightStage ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; - final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage; - - final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage; + int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + if (enableFlexibleSplit()) { + toTopStage = mStageOrderOperator.getStageForLegacyPosition(closedBottomRightStage + ? SPLIT_POSITION_TOP_OR_LEFT + : SPLIT_POSITION_BOTTOM_OR_RIGHT, + false /*checkAllStagesIfNotActive*/); + dismissTop = toTopStage.getId(); + } final WindowContainerTransaction wct = new WindowContainerTransaction(); toTopStage.resetBounds(wct); prepareExitSplitScreen(dismissTop, wct); @@ -1998,8 +2289,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateSurfaceBounds(layout, t, shouldUseParallaxEffect); getMainStageBounds(mTempRect1); getSideStageBounds(mTempRect2); - mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately); - mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately); + if (enableFlexibleSplit()) { + StageTaskListener ltStage = + mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT, + false /*checkAllStagesIfNotActive*/); + StageTaskListener brStage = + mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, + false /*checkAllStagesIfNotActive*/); + ltStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately); + brStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately); + } else { + mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, + mShowDecorImmediately); + mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, + mShowDecorImmediately); + } t.apply(); mTransactionPool.release(t); } @@ -2015,19 +2319,33 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!sizeChanged) { // We still need to resize on decor for ensure all current status clear. final SurfaceControl.Transaction t = mTransactionPool.acquire(); - mMainStage.onResized(t); - mSideStage.onResized(t); + if (enableFlexibleSplit()) { + runForActiveStages(stage -> stage.onResized(t)); + } else { + mMainStage.onResized(t); + mSideStage.onResized(t); + } mTransactionPool.release(t); return; } - + List decorManagers = new ArrayList<>(); + SplitDecorManager mainDecor = null; + SplitDecorManager sideDecor = null; + if (enableFlexibleSplit()) { + decorManagers = mStageOrderOperator.getActiveStages().stream() + .map(StageTaskListener::getSplitDecorManager) + .toList(); + } else { + mainDecor = mMainStage.getSplitDecorManager(); + sideDecor = mSideStage.getSplitDecorManager(); + } sendOnBoundsChanged(); mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart"); mSplitTransitions.startResizeTransition(wct, this, (aborted) -> { mSplitLayout.setDividerInteractive(true, false, "onSplitResizeConsumed"); }, (finishWct, t) -> { mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish"); - }, mMainStage.getSplitDecorManager(), mSideStage.getSplitDecorManager()); + }, mainDecor, sideDecor, decorManagers); if (Flags.enableFlexibleTwoAppSplit()) { switch (layout.calculateCurrentSnapPosition()) { @@ -2054,10 +2372,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * @return true if stage bounds actually . */ private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + final StageTaskListener topLeftStage; + final StageTaskListener bottomRightStage; + if (enableFlexibleSplit()) { + topLeftStage = mStageOrderOperator + .getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT, + true /*checkAllStagesIfNotActive*/); + bottomRightStage = mStageOrderOperator + .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, + true /*checkAllStagesIfNotActive*/); + } else { + topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? mSideStage + : mMainStage; + bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? mMainStage + : mSideStage; + } boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s", @@ -2067,10 +2398,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, boolean applyResizingOffset) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + final StageTaskListener topLeftStage; + final StageTaskListener bottomRightStage; + if (enableFlexibleSplit()) { + topLeftStage = mStageOrderOperator + .getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT, + true /*checkAllStagesIfNotActive*/); + bottomRightStage = mStageOrderOperator + .getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, + true /*checkAllStagesIfNotActive*/); + } else { + topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? mSideStage + : mMainStage; + bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? mMainStage + : mSideStage; + } (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, applyResizingOffset); @@ -2085,10 +2429,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return SPLIT_POSITION_UNDEFINED; } - if (mMainStage.containsToken(token)) { - return getMainStagePosition(); - } else if (mSideStage.containsToken(token)) { - return getSideStagePosition(); + if (enableFlexibleSplit()) { + // We could migrate to/return the new INDEX enums here since most callers just care that + // this value isn't SPLIT_POSITION_UNDEFINED, but + // ImePositionProcessor#getImeTargetPosition actually uses the leftTop/bottomRight value + StageTaskListener stageForToken = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.containsToken(token)) + .findFirst().orElse(null); + return stageForToken == null + ? SPLIT_POSITION_UNDEFINED + : mStageOrderOperator.getLegacyPositionForStage(stageForToken); + } else { + if (mMainStage.containsToken(token)) { + return getMainStagePosition(); + } else if (mSideStage.containsToken(token)) { + return getSideStagePosition(); + } } return SPLIT_POSITION_UNDEFINED; @@ -2193,19 +2549,41 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1(); } + /** + * TODO(b/349828130) Currently the way this is being used is only to to get the bottomRight + * stage. Eventually we'll need to rename and for now we'll repurpose the method to return + * the bottomRight bounds under the flex split flag + */ private void getSideStageBounds(Rect rect) { - if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { + if (enableFlexibleSplit()) { + // Split Layout doesn't actually keep track of the bounds based on the stage, + // it only knows that bounds1 is leftTop position and bounds2 is bottomRight position + // We'll then assume this method is to get bounds of bottomRight stage + mSplitLayout.getBounds2(rect); + } else if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { mSplitLayout.getBounds1(rect); } else { mSplitLayout.getBounds2(rect); } } + /** + * TODO(b/349828130) Currently the way this is being used is only to to get the leftTop + * stage. Eventually we'll need to rename and for now we'll repurpose the method to return + * the leftTop bounds under the flex split flag + */ private void getMainStageBounds(Rect rect) { - if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { - mSplitLayout.getBounds2(rect); - } else { + if (enableFlexibleSplit()) { + // Split Layout doesn't actually keep track of the bounds based on the stage, + // it only knows that bounds1 is leftTop position and bounds2 is bottomRight position + // We'll then assume this method is to get bounds of topLeft stage mSplitLayout.getBounds1(rect); + } else { + if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { + mSplitLayout.getBounds2(rect); + } else { + mSplitLayout.getBounds1(rect); + } } } @@ -2214,14 +2592,23 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * this task (yet) so this can also be used to identify which stage to put a task into. */ private StageTaskListener getStageOfTask(ActivityManager.RunningTaskInfo taskInfo) { - // TODO(b/184679596): Find a way to either include task-org information in the transition, - // or synchronize task-org callbacks so we can use stage.containsTask - if (mMainStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) { - return mMainStage; - } else if (mSideStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) { - return mSideStage; + if (enableFlexibleSplit()) { + return mStageOrderOperator.getActiveStages().stream() + .filter((stage) -> stage.mRootTaskInfo != null && + taskInfo.parentTaskId == stage.mRootTaskInfo.taskId + ) + .findFirst() + .orElse(null); + } else { + // TODO(b/184679596): Find a way to either include task-org information in the + // transition, or synchronize task-org callbacks so we can use stage.containsTask + if (mMainStage.mRootTaskInfo != null + && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) { + return mMainStage; + } else if (mSideStage.mRootTaskInfo != null + && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) { + return mSideStage; + } } return null; } @@ -2229,7 +2616,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @StageType private int getStageType(StageTaskListener stage) { if (stage == null) return STAGE_TYPE_UNDEFINED; - return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + if (enableFlexibleSplit()) { + return stage.getId(); + } else { + return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; + } } @Override @@ -2276,11 +2667,17 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (isSplitActive()) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active", request.getDebugId()); + StageTaskListener primaryStage = enableFlexibleSplit() + ? mStageOrderOperator.getActiveStages().get(0) + : mMainStage; + StageTaskListener secondaryStage = enableFlexibleSplit() + ? mStageOrderOperator.getActiveStages().get(1) + : mSideStage; // Try to handle everything while in split-screen, so return a WCT even if it's empty. ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" + " sideChildren=%d", triggerTask.taskId, transitTypeToString(type), - mMainStage.getChildCount(), mSideStage.getChildCount()); + primaryStage.getChildCount(), secondaryStage.getChildCount()); out = new WindowContainerTransaction(); if (stage != null) { if (isClosingType(type) && stage.getChildCount() == 1) { @@ -2320,11 +2717,21 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // the remote handler. return null; } - - if ((mMainStage.containsTask(triggerTask.taskId) - && mMainStage.getChildCount() == 1) - || (mSideStage.containsTask(triggerTask.taskId) - && mSideStage.getChildCount() == 1)) { + boolean anyStageContainsSingleFullscreenTask; + if (enableFlexibleSplit()) { + anyStageContainsSingleFullscreenTask = + mStageOrderOperator.getActiveStages().stream() + .anyMatch(stageListener -> + stageListener.containsTask(triggerTask.taskId) + && stageListener.getChildCount() == 1); + } else { + anyStageContainsSingleFullscreenTask = + (mMainStage.containsTask(triggerTask.taskId) + && mMainStage.getChildCount() == 1) + || (mSideStage.containsTask(triggerTask.taskId) + && mSideStage.getChildCount() == 1); + } + if (anyStageContainsSingleFullscreenTask) { // A splitting task is opening to fullscreen causes one side of the split empty, // so appends operations to exit split. prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); @@ -2344,11 +2751,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // One of the cases above handled it return out; } else if (isSplitScreenVisible()) { + boolean allStagesHaveChildren; + if (enableFlexibleSplit()) { + allStagesHaveChildren = runForActiveStagesAllMatch(stageTaskListener -> + stageTaskListener.getChildCount() != 0); + } else { + allStagesHaveChildren = mMainStage.getChildCount() != 0 + && mSideStage.getChildCount() != 0; + } // If split is visible, only defer handling this transition if it's launching // adjacent while there is already a split pair -- this may trigger PIP and // that should be handled by the mixed handler. final boolean deferTransition = requestHasLaunchAdjacentFlag(request) - && mMainStage.getChildCount() != 0 && mSideStage.getChildCount() != 0; + && allStagesHaveChildren; return !deferTransition ? out : null; } // Don't intercept the transition if we are not handling it as a part of one of the @@ -2588,8 +3003,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final ArraySet dismissStages = record.getShouldDismissedStage(); - if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0 - || dismissStages.size() == 1) { + boolean anyStageHasNoChildren; + if (enableFlexibleSplit()) { + anyStageHasNoChildren = mStageOrderOperator.getActiveStages().stream() + .anyMatch(stage -> stage.getChildCount() == 0); + } else { + anyStageHasNoChildren = mMainStage.getChildCount() == 0 + || mSideStage.getChildCount() == 0; + } + if (anyStageHasNoChildren || dismissStages.size() == 1) { // If the size of dismissStages == 1, one of the task is closed without prepare // pending transition, which could happen if all activities were finished after // finish top activity in a task, so the trigger task is null when handleRequest. @@ -2735,24 +3157,48 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, shouldAnimate = startPendingDismissAnimation( dismiss, info, startTransaction, finishTransaction); if (shouldAnimate && dismiss.mReason == EXIT_REASON_DRAG_DIVIDER) { - final StageTaskListener toTopStage = - dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage; + StageTaskListener toTopStage; + if (enableFlexibleSplit()) { + toTopStage = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.getId() == dismiss.mDismissTop) + .findFirst().orElseThrow(); + } else { + toTopStage = dismiss.mDismissTop == STAGE_TYPE_MAIN ? mMainStage : mSideStage; + } mSplitTransitions.playDragDismissAnimation(transition, info, startTransaction, finishTransaction, finishCallback, toTopStage.mRootTaskInfo.token, toTopStage.getSplitDecorManager(), mRootTaskInfo.token); return true; } } else if (mSplitTransitions.isPendingResize(transition)) { + Map tokenDecorMap = new HashMap<>(); + if (enableFlexibleSplit()) { + runForActiveStages(stageTaskListener -> + tokenDecorMap.put(stageTaskListener.mRootTaskInfo.getToken(), + stageTaskListener.getSplitDecorManager())); + } else { + tokenDecorMap.put(mMainStage.mRootTaskInfo.getToken(), + mMainStage.getSplitDecorManager()); + tokenDecorMap.put(mSideStage.mRootTaskInfo.getToken(), + mSideStage.getSplitDecorManager()); + } mSplitTransitions.playResizeAnimation(transition, info, startTransaction, - finishTransaction, finishCallback, mMainStage.mRootTaskInfo.token, - mSideStage.mRootTaskInfo.token, mMainStage.getSplitDecorManager(), - mSideStage.getSplitDecorManager()); + finishTransaction, finishCallback, tokenDecorMap); return true; } if (!shouldAnimate) return false; + WindowContainerToken mainToken; + WindowContainerToken sideToken; + if (enableFlexibleSplit()) { + mainToken = mStageOrderOperator.getActiveStages().get(0).mRootTaskInfo.token; + sideToken = mStageOrderOperator.getActiveStages().get(1).mRootTaskInfo.token; + } else { + mainToken = mMainStage.mRootTaskInfo.token; + sideToken = mSideStage.mRootTaskInfo.token; + } mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction, - finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, + finishCallback, mainToken, sideToken, mRootTaskInfo.token); return true; } @@ -2777,6 +3223,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // First, verify that we actually have opened apps in both splits. TransitionInfo.Change mainChild = null; TransitionInfo.Change sideChild = null; + StageTaskListener firstAppStage = null; + StageTaskListener secondAppStage = null; final WindowContainerTransaction evictWct = new WindowContainerTransaction(); for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); @@ -2785,14 +3233,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mPausingTasks.contains(taskInfo.taskId)) { continue; } - final @StageType int stageType = getStageType(getStageOfTask(taskInfo)); - if (mainChild == null && stageType == STAGE_TYPE_MAIN + StageTaskListener stage = getStageOfTask(taskInfo); + final @StageType int stageType = getStageType(stage); + if (mainChild == null + && stageType == (enableFlexibleSplit() ? STAGE_TYPE_A : STAGE_TYPE_MAIN) && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) { // Includes TRANSIT_CHANGE to cover reparenting top-most task to split. mainChild = change; - } else if (sideChild == null && stageType == STAGE_TYPE_SIDE + firstAppStage = getStageOfTask(taskInfo); + } else if (sideChild == null + && stageType == (enableFlexibleSplit() ? STAGE_TYPE_B : STAGE_TYPE_SIDE) && (isOpeningType(change.getMode()) || change.getMode() == TRANSIT_CHANGE)) { sideChild = change; + secondAppStage = stage; } else if (stageType != STAGE_TYPE_UNDEFINED && change.getMode() == TRANSIT_TO_BACK) { // Collect all to back task's and evict them when transition finished. evictWct.reparent(taskInfo.token, null /* parent */, false /* onTop */); @@ -2848,9 +3301,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // TODO(b/184679596): Find a way to either include task-org information in // the transition, or synchronize task-org callbacks. final boolean mainNotContainOpenTask = - mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId); + mainChild != null && !firstAppStage.containsTask(mainChild.getTaskInfo().taskId); final boolean sideNotContainOpenTask = - sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId); + sideChild != null && !secondAppStage.containsTask(sideChild.getTaskInfo().taskId); if (mainNotContainOpenTask) { Log.w(TAG, "Expected onTaskAppeared on " + mMainStage + " to have been called with " + mainChild.getTaskInfo().taskId @@ -2863,6 +3316,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final TransitionInfo.Change finalMainChild = mainChild; final TransitionInfo.Change finalSideChild = sideChild; + final StageTaskListener finalFirstAppStage = firstAppStage; + final StageTaskListener finalSecondAppStage = secondAppStage; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { if (!enterTransition.mResizeAnim) { // If resizing, we'll call notify at the end of the resizing animation (below) @@ -2870,16 +3325,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (finalMainChild != null) { if (!mainNotContainOpenTask) { - mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId); + finalFirstAppStage.evictOtherChildren(callbackWct, + finalMainChild.getTaskInfo().taskId); } else { - mMainStage.evictInvisibleChildren(callbackWct); + finalFirstAppStage.evictInvisibleChildren(callbackWct); } } if (finalSideChild != null) { if (!sideNotContainOpenTask) { - mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId); + finalSecondAppStage.evictOtherChildren(callbackWct, + finalSideChild.getTaskInfo().taskId); } else { - mSideStage.evictInvisibleChildren(callbackWct); + finalSecondAppStage.evictInvisibleChildren(callbackWct); } } if (!evictWct.isEmpty()) { @@ -2961,8 +3418,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onPipExpandToSplit(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo taskInfo) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo); + // TODO(b/349828130) currently pass in index_undefined until we can revisit these + // flex split + pip interactions in the future prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo), - false /*resizeAnim*/); + false /*resizeAnim*/, SPLIT_INDEX_UNDEFINED); if (!isSplitScreenVisible() || mSplitRequest == null) { return; @@ -3067,13 +3526,28 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Wait until after animation to update divider // Reset crops so they don't interfere with subsequent launches - t.setCrop(mMainStage.mRootLeash, null); - t.setCrop(mSideStage.mRootLeash, null); + if (enableFlexibleSplit()) { + runForActiveStages(stage -> t.setCrop(stage.mRootLeash, null /*crop*/)); + } else { + t.setCrop(mMainStage.mRootLeash, null); + t.setCrop(mSideStage.mRootLeash, null); + } // Hide the non-top stage and set the top one to the fullscreen position. if (toStage != STAGE_TYPE_UNDEFINED) { - t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash); - t.setPosition(toStage == STAGE_TYPE_MAIN - ? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0); + if (enableFlexibleSplit()) { + StageTaskListener stageToKeep = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.getId() == toStage) + .findFirst().orElseThrow(); + List stagesToHide = mStageOrderOperator.getAllStages().stream() + .filter(stage -> stage.getId() != toStage) + .toList(); + stagesToHide.forEach(stage -> t.hide(stage.mRootLeash)); + t.setPosition(stageToKeep.mRootLeash, 0, 0); + } else { + t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash); + t.setPosition(toStage == STAGE_TYPE_MAIN + ? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0); + } } else { for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) { finishT.hide(dismissingTasks.valueAt(i)); @@ -3088,8 +3562,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Hide divider and dim layer on transition finished. setDividerVisibility(false, t); - finishT.hide(mMainStage.mDimLayer); - finishT.hide(mSideStage.mDimLayer); + if (enableFlexibleSplit()) { + runForActiveStages(stage -> finishT.hide(stage.mRootLeash)); + } else { + finishT.hide(mMainStage.mDimLayer); + finishT.hide(mSideStage.mDimLayer); + } } private boolean startPendingDismissAnimation( @@ -3110,8 +3588,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return false; } dismissTransition.setFinishedCallback((callbackWct, callbackT) -> { - mMainStage.getSplitDecorManager().release(callbackT); - mSideStage.getSplitDecorManager().release(callbackT); + if (enableFlexibleSplit()) { + runForActiveStages(stage -> stage.getSplitDecorManager().release(callbackT)); + } else { + mMainStage.getSplitDecorManager().release(callbackT); + mSideStage.getSplitDecorManager().release(callbackT); + } callbackWct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token, false); }); return true; @@ -3128,8 +3610,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (TransitionUtil.isClosingType(change.getMode()) && change.getTaskInfo() != null) { final int taskId = change.getTaskInfo().taskId; - if (mMainStage.getTopVisibleChildTaskId() == taskId - || mSideStage.getTopVisibleChildTaskId() == taskId) { + boolean anyStagesHaveTask; + if (enableFlexibleSplit()) { + anyStagesHaveTask = mStageOrderOperator.getActiveStages().stream() + .anyMatch(stage -> stage.getTopVisibleChildTaskId() == taskId); + } else { + anyStagesHaveTask = mMainStage.getTopVisibleChildTaskId() == taskId + || mSideStage.getTopVisibleChildTaskId() == taskId; + } + if (anyStagesHaveTask) { mPausingTasks.add(taskId); } } @@ -3161,9 +3650,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction.HierarchyOp op = finishWct.getHierarchyOps().get(i); final IBinder container = op.getContainer(); + boolean anyStageContainsContainer; + if (enableFlexibleSplit()) { + anyStageContainsContainer = mStageOrderOperator.getActiveStages().stream() + .anyMatch(stage -> stage.containsContainer(container)); + } else { + anyStageContainsContainer = mMainStage.containsContainer(container) + || mSideStage.containsContainer(container); + } if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() - && (mMainStage.containsContainer(container) - || mSideStage.containsContainer(container))) { + && anyStageContainsContainer) { updateSurfaceBounds(mSplitLayout, finishT, false /* applyResizingOffset */); finishT.reparent(mSplitLayout.getDividerLeash(), mRootTaskLeash); @@ -3190,10 +3686,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // user entering recents. for (int i = mPausingTasks.size() - 1; i >= 0; --i) { final int taskId = mPausingTasks.get(i); - if (mMainStage.containsTask(taskId)) { - mMainStage.evictChild(finishWct, taskId, "recentsPairToPair"); - } else if (mSideStage.containsTask(taskId)) { - mSideStage.evictChild(finishWct, taskId, "recentsPairToPair"); + if (enableFlexibleSplit()) { + mStageOrderOperator.getActiveStages().stream() + .filter(stage -> stage.containsTask(taskId)) + .findFirst() + .ifPresent(stageToEvict -> + stageToEvict.evictChild(finishWct, taskId, "recentsPairToPair")); + } else { + if (mMainStage.containsTask(taskId)) { + mMainStage.evictChild(finishWct, taskId, "recentsPairToPair"); + } else if (mSideStage.containsTask(taskId)) { + mSideStage.evictChild(finishWct, taskId, "recentsPairToPair"); + } } } // If pending enter hasn't consumed, the mix handler will invoke start pending @@ -3256,8 +3760,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ private void setSplitsVisible(boolean visible) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible); - mMainStage.mVisible = mSideStage.mVisible = visible; - mMainStage.mHasChildren = mSideStage.mHasChildren = visible; + if (enableFlexibleSplit()) { + runForActiveStages(stage -> { + stage.mVisible = visible; + stage.mHasChildren = visible; + }); + } else { + mMainStage.mVisible = mSideStage.mVisible = visible; + mMainStage.mHasChildren = mSideStage.mHasChildren = visible; + } } /** @@ -3300,6 +3811,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * executed. */ private void logExitToStage(@ExitReason int exitReason, boolean toMainStage) { + if (enableFlexibleSplit()) { + // TODO(b/374825718) update logging for 2+ apps + return; + } mLogger.logExit(exitReason, toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED, toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt new file mode 100644 index 000000000000..b7b3c9b62a58 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.splitscreen + +import android.content.Context +import com.android.internal.protolog.ProtoLog +import com.android.launcher3.icons.IconProvider +import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.shared.split.SplitScreenConstants +import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_NONE +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_1 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_2 +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED +import com.android.wm.shell.shared.split.SplitScreenConstants.SnapPosition +import com.android.wm.shell.shared.split.SplitScreenConstants.SplitIndex +import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition +import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_A +import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_B +import com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_C +import com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString +import com.android.wm.shell.windowdecor.WindowDecorViewModel +import java.util.Optional + +/** + * Responsible for creating [StageTaskListener]s and maintaining their ordering on screen. + * Must be notified whenever stages positions change via swapping or starting/ending tasks + */ +class StageOrderOperator ( + context: Context, + taskOrganizer: ShellTaskOrganizer, + displayId: Int, + stageCallbacks: StageTaskListener.StageListenerCallbacks, + syncQueue: SyncTransactionQueue, + iconProvider: IconProvider, + windowDecorViewModel: Optional + ) { + + private val MAX_STAGES = 3 + /** + * This somewhat acts as a replacement to stageTypes in the intermediary, so we want to start + * it after the @StageType constant values just to be safe and avoid potentially subtle bugs. + */ + private var stageIds = listOf(STAGE_TYPE_A, STAGE_TYPE_B, STAGE_TYPE_C) + + /** + * Active Stages, this list represent the current, ordered list of stages that are + * currently visible to the user. This map should be empty if the user is currently + * not in split screen. Note that this is different than if split screen is visible, which + * is determined by [StageListenerImpl.mVisible]. + * Split stages can be active and in the background + */ + val activeStages = mutableListOf() + val allStages = mutableListOf() + var isActive: Boolean = false + var isVisible: Boolean = false + @SnapPosition private var currentLayout: Int = SNAP_TO_NONE + + init { + for(i in 0 until MAX_STAGES) { + allStages.add(StageTaskListener(context, + taskOrganizer, + displayId, + stageCallbacks, + syncQueue, + iconProvider, + windowDecorViewModel, + stageIds[i]) + ) + } + } + + /** + * Updates internal state to keep record of "active" stages. Note that this does NOT call + * [StageTaskListener.activate] on the stages. + */ + fun onEnteringSplit(@SnapPosition goingToLayout: Int) { + if (goingToLayout == currentLayout) { + // Add protolog here. Return for now, but maybe we want to handle swap case, TBD + return + } + val freeStages: List = + allStages.filterNot { activeStages.contains(it) } + when(goingToLayout) { + SplitScreenConstants.SNAP_TO_2_50_50 -> { + if (activeStages.size < 2) { + // take from allStages and add into activeStages + for (i in 0 until (2 - activeStages.size)) { + val stage = freeStages[i] + activeStages.add(stage) + } + } + } + } + ProtoLog.d( + ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Activated stages: %d ids=%s", + activeStages.size, + activeStages.joinToString(",") { stageTypeToString(it.id) } + ) + isActive = true + } + + fun onExitingSplit() { + activeStages.clear() + isActive = false + } + + /** + * Given a legacy [SplitPosition] returns one of the stages from the actives stages. + * If there are no active stages and [checkAllStagesIfNotActive] is not true, then will return + * null + */ + fun getStageForLegacyPosition(@SplitPosition position: Int, + checkAllStagesIfNotActive : Boolean = false) : + StageTaskListener? { + if (activeStages.size != 2 && !checkAllStagesIfNotActive) { + return null + } + val listToCheck = if (activeStages.isEmpty() and checkAllStagesIfNotActive) + allStages else + activeStages + if (position == SPLIT_POSITION_TOP_OR_LEFT) { + return listToCheck[0] + } else if (position == SPLIT_POSITION_BOTTOM_OR_RIGHT) { + return listToCheck[1] + } else { + throw IllegalArgumentException("No stage for invalid position") + } + } + + /** + * Returns a legacy split position for the given stage. If no stages are active then this will + * return [SPLIT_POSITION_UNDEFINED] + */ + @SplitPosition + fun getLegacyPositionForStage(stage: StageTaskListener) : Int { + if (allStages[0] == stage) { + return SPLIT_POSITION_TOP_OR_LEFT + } else if (allStages[1] == stage) { + return SPLIT_POSITION_BOTTOM_OR_RIGHT + } else { + return SPLIT_POSITION_UNDEFINED + } + } + + /** + * Returns the stageId from a given splitIndex. This will default to checking from all stages if + * [isActive] is false, otherwise will only check active stages. + */ + fun getStageForIndex(@SplitIndex splitIndex: Int) : StageTaskListener { + // Probably should do a check for index to be w/in the bounds of the current split layout + // that we're currently in + val listToCheck = if (isActive) activeStages else allStages + if (splitIndex == SPLIT_INDEX_0) { + return listToCheck[0] + } else if (splitIndex == SPLIT_INDEX_1) { + return listToCheck[1] + } else if (splitIndex == SPLIT_INDEX_2) { + return listToCheck[2] + } else { + // Though I guess what if we're adding to the end? Maybe that indexing needs to be + // resolved elsewhere + throw IllegalStateException("No stage for the given splitIndex") + } + } +} \ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 08cdfdb3b5a9..4a37169add36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -22,10 +22,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; import static android.view.RemoteAnimationTarget.MODE_OPENING; +import static com.android.wm.shell.Flags.enableFlexibleSplit; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; import static com.android.wm.shell.shared.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE; +import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import android.annotation.CallSuper; import android.annotation.Nullable; @@ -72,6 +74,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the // stages should have this be set/being used private boolean mIsActive; + /** Unique identifier for this state, > 0 */ + @StageType private final int mId; /** Callback interface for listening to changes in a split-screen stage. */ public interface StageListenerCallbacks { void onRootTaskAppeared(); @@ -110,13 +114,14 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, IconProvider iconProvider, - Optional windowDecorViewModel) { + Optional windowDecorViewModel, int id) { mContext = context; mCallbacks = callbacks; mSyncQueue = syncQueue; mIconProvider = iconProvider; mWindowDecorViewModel = windowDecorViewModel; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); + mId = id; } int getChildCount() { @@ -161,6 +166,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { return contains(t -> t.isFocused); } + @StageType + int getId() { + return mId; + } + private boolean contains(Predicate predicate) { if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) { return true; @@ -197,10 +207,10 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d " - + "taskActivity=%s", + + "stageId=%s taskActivity=%s", taskInfo.taskId, taskInfo.parentTaskId, mRootTaskInfo != null ? mRootTaskInfo.taskId : -1, - taskInfo.baseActivity); + stageTypeToString(mId), taskInfo.baseActivity); if (mRootTaskInfo == null) { mRootLeash = leash; mRootTaskInfo = taskInfo; @@ -230,8 +240,9 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s", - taskInfo.taskId, taskInfo.baseActivity); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s " + + "stageId=%s", + taskInfo.taskId, taskInfo.baseActivity, stageTypeToString(mId)); mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo)); if (mRootTaskInfo.taskId == taskInfo.taskId) { mRootTaskInfo = taskInfo; @@ -261,7 +272,8 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d stageId=%s", + taskInfo.taskId, stageTypeToString(mId)); final int taskId = taskInfo.taskId; mWindowDecorViewModel.ifPresent(vm -> vm.onTaskVanished(taskInfo)); if (mRootTaskInfo.taskId == taskId) { @@ -466,14 +478,17 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void activate(WindowContainerTransaction wct, boolean includingTopTask) { - if (mIsActive) return; - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b", - includingTopTask); + if (mIsActive && !enableFlexibleSplit()) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b stage=%s", + includingTopTask, stageTypeToString(mId)); if (includingTopTask) { reparentTopTask(wct); } + if (enableFlexibleSplit()) { + return; + } mIsActive = true; } @@ -481,11 +496,14 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { deactivate(wct, false /* toTop */); } - void deactivate(WindowContainerTransaction wct, boolean toTop) { - if (!mIsActive) return; - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s", - toTop, mRootTaskInfo); - mIsActive = false; + void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop) { + if (!mIsActive && !enableFlexibleSplit()) return; + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: reparentTasksToTop=%b " + + "rootTaskInfo=%s stage=%s", + reparentTasksToTop, mRootTaskInfo, stageTypeToString(mId)); + if (!enableFlexibleSplit()) { + mIsActive = false; + } if (mRootTaskInfo == null) return; final WindowContainerToken rootToken = mRootTaskInfo.token; @@ -494,14 +512,15 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { null /* newParent */, null /* windowingModes */, null /* activityTypes */, - toTop); + reparentTasksToTop); } // -------- - // Previously only used in SideStage + // Previously only used in SideStage. With flexible split this is called for all stages boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { - ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", - mChildrenTaskInfo.size(), toTop); + ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b " + + " stageI=%s", + mChildrenTaskInfo.size(), toTop, stageTypeToString(mId)); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, @@ -521,6 +540,15 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { return true; } + @Override + public String toString() { + return "mId: " + stageTypeToString(mId) + + " mVisible: " + mVisible + + " mActive: " + mIsActive + + " mHasRootTask: " + mHasRootTask + + " childSize: " + mChildrenTaskInfo.size(); + } + @Override @CallSuper public void dump(@NonNull PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 936835c34c04..5df395754c7a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -116,6 +116,7 @@ import com.android.wm.shell.recents.RecentsTransitionStateListener import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource.UNKNOWN import com.android.wm.shell.shared.split.SplitScreenConstants +import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED import com.android.wm.shell.splitscreen.SplitScreenController import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController @@ -3158,7 +3159,7 @@ class DesktopTasksControllerTest : ShellTestCase() { runOpenNewWindow(task) verify(splitScreenController) .startIntent(any(), anyInt(), any(), any(), - optionsCaptor.capture(), anyOrNull(), eq(true) + optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED) ) assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode) .isEqualTo(WINDOWING_MODE_MULTI_WINDOW) @@ -3174,7 +3175,7 @@ class DesktopTasksControllerTest : ShellTestCase() { verify(splitScreenController) .startIntent( any(), anyInt(), any(), any(), - optionsCaptor.capture(), anyOrNull(), eq(true) + optionsCaptor.capture(), anyOrNull(), eq(true), eq(SPLIT_INDEX_UNDEFINED) ) assertThat(ActivityOptions.fromBundle(optionsCaptor.value).launchWindowingMode) .isEqualTo(WINDOWING_MODE_MULTI_WINDOW) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java index 2cfce6933e1b..0cf15baf30b0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java @@ -24,6 +24,7 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED; import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData; import static com.android.wm.shell.draganddrop.DragTestUtils.createIntentClipData; import static com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo; @@ -226,7 +227,7 @@ public class SplitDragPolicyTest extends ShellTestCase { mPolicy.onDropped(filterTargetByType(targets, TYPE_FULLSCREEN), null /* hideTaskToken */); verify(mFullscreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_UNDEFINED), any(), any()); + eq(SPLIT_POSITION_UNDEFINED), any(), any(), eq(SPLIT_INDEX_UNDEFINED)); } private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) { @@ -241,12 +242,12 @@ public class SplitDragPolicyTest extends ShellTestCase { mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_LEFT), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any()); + eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any(), eq(SPLIT_INDEX_UNDEFINED)); reset(mSplitScreenStarter); mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_RIGHT), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any()); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any(), eq(SPLIT_INDEX_UNDEFINED)); } private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) { @@ -261,13 +262,13 @@ public class SplitDragPolicyTest extends ShellTestCase { mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_TOP), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any()); + eq(SPLIT_POSITION_TOP_OR_LEFT), any(), any(), eq(SPLIT_INDEX_UNDEFINED)); reset(mSplitScreenStarter); mPolicy.onDropped(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), null /* hideTaskToken */); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any()); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any(), any(), eq(SPLIT_INDEX_UNDEFINED)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index 966651f19711..72a7a3f5ec99 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_0; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -200,10 +201,11 @@ public class SplitScreenControllerTests extends ShellTestCase { PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + SPLIT_INDEX_0); verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(), - eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull()); + eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull(), eq(SPLIT_INDEX_0)); assertEquals(FLAG_ACTIVITY_NO_USER_ACTION, mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION); } @@ -220,10 +222,11 @@ public class SplitScreenControllerTests extends ShellTestCase { doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + SPLIT_INDEX_0); verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(), - eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull()); + eq(SPLIT_POSITION_TOP_OR_LEFT), isNull(), isNull(), eq(SPLIT_INDEX_0)); assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK, mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK); } @@ -243,10 +246,11 @@ public class SplitScreenControllerTests extends ShellTestCase { doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any(), anyInt(), any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + SPLIT_INDEX_0); verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), - isNull(), isNull()); + isNull(), isNull(), eq(SPLIT_INDEX_0)); verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any()); verify(mStageCoordinator, never()).switchSplitPosition(any()); } @@ -269,10 +273,11 @@ public class SplitScreenControllerTests extends ShellTestCase { .findTaskInBackground(any(), anyInt(), any()); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + SPLIT_INDEX_0); verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any()); verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT), - isNull(), isNull()); + isNull(), isNull(), eq(SPLIT_INDEX_0)); } @Test @@ -289,7 +294,8 @@ public class SplitScreenControllerTests extends ShellTestCase { SPLIT_POSITION_BOTTOM_OR_RIGHT); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, - SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */); + SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, + SPLIT_INDEX_0); verify(mStageCoordinator).switchSplitPosition(anyString()); } @@ -301,7 +307,7 @@ public class SplitScreenControllerTests extends ShellTestCase { PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, - true /* forceLaunchNewTask */); + true /* forceLaunchNewTask */, SPLIT_INDEX_0); verify(mRecentTasks, never()).findTaskInBackground(any(), anyInt(), any()); } @@ -312,7 +318,7 @@ public class SplitScreenControllerTests extends ShellTestCase { PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE); mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null, SPLIT_POSITION_TOP_OR_LEFT, null /* options */, null /* hideTaskToken */, - false /* forceLaunchNewTask */); + false /* forceLaunchNewTask */, SPLIT_INDEX_0); verify(mRecentTasks).findTaskInBackground(any(), anyInt(), any()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index ce3944a5855e..e32cf3899a03 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; @@ -133,11 +134,11 @@ public class SplitTransitionTests extends ShellTestCase { mSplitLayout = SplitTestUtils.createMockSplitLayout(); mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, - mIconProvider, Optional.of(mWindowDecorViewModel))); + mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_MAIN)); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, - mIconProvider, Optional.of(mWindowDecorViewModel))); + mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_SIDE)); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index a252a9db7095..2d102fb27c66 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -165,8 +166,9 @@ public class StageCoordinatorTests extends ShellTestCase { final WindowContainerTransaction wct = spy(new WindowContainerTransaction()); mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + // TODO(b/349828130) Address this once we remove index_undefined called verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED)); verify(mMainStage).reparentTopTask(eq(wct)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition()); @@ -183,8 +185,9 @@ public class StageCoordinatorTests extends ShellTestCase { final WindowContainerTransaction wct = new WindowContainerTransaction(); mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + // TODO(b/349828130) Address this once we remove index_undefined called verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition()); assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getSideStagePosition()); } @@ -195,8 +198,9 @@ public class StageCoordinatorTests extends ShellTestCase { final WindowContainerTransaction wct = new WindowContainerTransaction(); mStageCoordinator.moveToStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); + // TODO(b/349828130) Address this once we remove index_undefined called verify(mStageCoordinator).prepareEnterSplitScreen(eq(wct), eq(task), - eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false)); + eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), eq(false), eq(SPLIT_INDEX_UNDEFINED)); assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition()); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 7144a1e038f9..fe91440b106f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -19,6 +19,8 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; @@ -93,7 +95,8 @@ public final class StageTaskListenerTests extends ShellTestCase { mCallbacks, mSyncQueue, mIconProvider, - Optional.of(mWindowDecorViewModel)); + Optional.of(mWindowDecorViewModel), + STAGE_TYPE_UNDEFINED); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; mSurfaceControl = new SurfaceControl.Builder().setName("test").build(); -- GitLab From fcb6024fe06c16b05b431742cc755acd336bba92 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Mon, 18 Nov 2024 11:43:40 +0800 Subject: [PATCH 117/656] [expressive design] Add LocalIsInCategory This indicates if a composable is in a category. And only set BaseLayout background to surfaceBright when in category. Bug: 360916599 Flag: EXEMPT bug fix Test: visual Change-Id: Iecead6b360e2d0564285ce01dae31eb2c278a8a9 --- .../spa/framework/compose/ModifierExt.kt | 33 +++++++++++++++++++ .../spa/widget/preference/BaseLayout.kt | 17 +++++----- .../settingslib/spa/widget/ui/Category.kt | 21 +++++++----- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt index e883a4a55af9..25bb46c837f6 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/ModifierExt.kt @@ -25,3 +25,36 @@ fun Modifier.contentDescription(contentDescription: String?) = if (contentDescription != null) this.semantics { this.contentDescription = contentDescription } else this + +/** + * Concatenates this modifier with another if `condition` is true. + * + * This method allows inline conditional addition of modifiers to a modifier chain. Instead of + * writing + * + * ``` + * val aModifier = Modifier.a() + * val bModifier = if(condition) aModifier.b() else aModifier + * Composable(modifier = bModifier) + * ``` + * + * You can instead write + * + * ``` + * Composable(modifier = Modifier.a().thenIf(condition){ + * Modifier.b() + * } + * ``` + * + * This makes the modifier chain easier to read. + * + * Note that unlike the non-factory version, the conditional modifier is recreated each time, and + * may never be created at all. + * + * @param condition Whether or not to apply the modifiers. + * @param factory Creates the modifier to concatenate with the current one. + * @return a Modifier representing this modifier followed by other in sequence. + * @see Modifier.then + */ +inline fun Modifier.thenIf(condition: Boolean, crossinline factory: () -> Modifier): Modifier = + if (condition) this.then(factory()) else this diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt index acb96be64a34..b1bb79d61b03 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt @@ -36,12 +36,13 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.min +import com.android.settingslib.spa.framework.compose.thenIf import com.android.settingslib.spa.framework.theme.SettingsDimension import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled import com.android.settingslib.spa.framework.theme.SettingsShape import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled +import com.android.settingslib.spa.widget.ui.LocalIsInCategory import com.android.settingslib.spa.widget.ui.SettingsTitle @Composable @@ -57,18 +58,18 @@ internal fun BaseLayout( paddingVertical: Dp = SettingsDimension.itemPaddingVertical, widget: @Composable () -> Unit = {}, ) { + val surfaceBright = MaterialTheme.colorScheme.surfaceBright Row( modifier = modifier .fillMaxWidth() .semantics(mergeDescendants = true) {} - .then( - if (isSpaExpressiveEnabled) - Modifier.heightIn(min = SettingsDimension.preferenceMinHeight) - .clip(SettingsShape.CornerExtraSmall) - .background(MaterialTheme.colorScheme.surfaceBright) - else Modifier - ) + .thenIf(isSpaExpressiveEnabled) { + Modifier.heightIn(min = SettingsDimension.preferenceMinHeight) + } + .thenIf(isSpaExpressiveEnabled && LocalIsInCategory.current) { + Modifier.clip(SettingsShape.CornerExtraSmall).background(surfaceBright) + } .padding(end = paddingEnd), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt index 28b2b4ab1662..96d2abb70391 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt @@ -31,6 +31,8 @@ import androidx.compose.material.icons.outlined.TouchApp import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -99,7 +101,7 @@ fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) verticalArrangement = if (isSpaExpressiveEnabled) Arrangement.spacedBy(SettingsDimension.paddingTiny) else Arrangement.Top, - content = content, + content = { CompositionLocalProvider(LocalIsInCategory provides true) { content() } }, ) } } @@ -109,15 +111,14 @@ fun Category(title: String? = null, content: @Composable ColumnScope.() -> Unit) * * @param list The list of items to display. * @param entry The entry for each list item according to its index in list. - * @param key Optional. The key for each item in list to provide unique item identifiers, making - * the list more efficient. - * @param title Optional. Category title for each item or each group of items in the list. It - * should be decided by the index. + * @param key Optional. The key for each item in list to provide unique item identifiers, making the + * list more efficient. + * @param title Optional. Category title for each item or each group of items in the list. It should + * be decided by the index. * @param bottomPadding Optional. Bottom outside padding of the category. * @param state Optional. State of LazyList. * @param content Optional. Content to be shown at the top of the category. */ - @Composable fun LazyCategory( list: List, @@ -144,17 +145,19 @@ fun LazyCategory( verticalArrangement = Arrangement.spacedBy(SettingsDimension.paddingTiny), state = state, ) { - item { content() } + item { CompositionLocalProvider(LocalIsInCategory provides true) { content() } } items(count = list.size, key = key) { title?.invoke(it)?.let { title -> CategoryTitle(title) } - val entryPreference = entry(it) - entryPreference() + CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() } } } } } +/** LocalIsInCategory containing the if the current composable is in a category. */ +internal val LocalIsInCategory = compositionLocalOf { false } + @Preview @Composable private fun CategoryPreview() { -- GitLab From c04628b5a4a4f6996f0add5d97de6eb72b17140f Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Thu, 14 Nov 2024 10:17:27 +0900 Subject: [PATCH 118/656] Add a functional test for minimizing AutoPip enabled app This CL adds a new test scenario which minimizes the window of the app which enters PiP automatically. Flag: EXEMPT adding a test Bug: 373973157 Test: atest PlatformScenarioTests:com.android.wm.shell.functional.MinimizeAutoPipAppWindowTest Change-Id: Ic2e4b2fa744ee2febd7492bbc1da046427adb6d3 --- .../MinimizeAutoPipAppWindowTest.kt | 27 +++++++ .../scenarios/MinimizeAutoPipAppWindow.kt | 72 +++++++++++++++++++ .../flicker/helpers/DesktopModeAppHelper.kt | 6 +- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt create mode 100644 libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt new file mode 100644 index 000000000000..48befc06b9c5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MinimizeAutoPipAppWindowTest.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.functional + +import android.platform.test.annotations.Postsubmit +import com.android.wm.shell.scenarios.MinimizeAutoPipAppWindow +import org.junit.runner.RunWith +import org.junit.runners.BlockJUnit4ClassRunner + +/* Functional test for [MinimizeAutoPipAppWindow]. */ +@RunWith(BlockJUnit4ClassRunner::class) +@Postsubmit +class MinimizeAutoPipAppWindowTest : MinimizeAutoPipAppWindow() diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt new file mode 100644 index 000000000000..d6c3266e915c --- /dev/null +++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/MinimizeAutoPipAppWindow.kt @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.scenarios + +import android.app.Instrumentation +import android.tools.NavBar +import android.tools.Rotation +import android.tools.traces.parsers.WindowManagerStateHelper +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.helpers.DesktopModeAppHelper +import com.android.server.wm.flicker.helpers.PipAppHelper +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.window.flags.Flags +import com.android.wm.shell.Utils +import org.junit.After +import org.junit.Assume +import org.junit.Before +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test + +/** Base scenario test for minimizing the app entering pip on leave automatically */ +@Ignore("Test Base Class") +abstract class MinimizeAutoPipAppWindow { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val tapl = LauncherInstrumentation() + private val wmHelper = WindowManagerStateHelper(instrumentation) + private val device = UiDevice.getInstance(instrumentation) + private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation)) + private val pipApp = PipAppHelper(instrumentation) + private val pipAppDesktopMode = DesktopModeAppHelper(pipApp) + + @Rule + @JvmField + val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0) + + @Before + fun setup() { + Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet) + Assume.assumeTrue(Flags.enableMinimizeButton()) + testApp.enterDesktopMode(wmHelper, device) + pipApp.launchViaIntent(wmHelper) + pipApp.enableAutoEnterForPipActivity() + } + + @Test + open fun minimizePipAppWindow() { + pipAppDesktopMode.minimizeDesktopApp(wmHelper, device, isPip = true) + } + + @After + fun teardown() { + pipApp.exit(wmHelper) + testApp.exit(wmHelper) + } +} diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt index 2c998c4217ac..d2c9eb30e2fc 100644 --- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt +++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/DesktopModeAppHelper.kt @@ -155,14 +155,16 @@ open class DesktopModeAppHelper(private val innerHelper: IStandardAppHelper) : ?: error("Unable to find resource $MINIMIZE_BUTTON_VIEW\n") } - fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice) { + fun minimizeDesktopApp(wmHelper: WindowManagerStateHelper, device: UiDevice, isPip: Boolean = false) { val caption = getCaptionForTheApp(wmHelper, device) val minimizeButton = getMinimizeButtonForTheApp(caption) minimizeButton.click() wmHelper .StateSyncBuilder() .withAppTransitionIdle() - .withWindowSurfaceDisappeared(innerHelper) + .apply { + if (isPip) withPipShown() else withWindowSurfaceDisappeared(innerHelper) + } .waitForAndVerify() } -- GitLab From 4c6a5d37622f971a4fc12c943bd614cbf6d87bdc Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Mon, 18 Nov 2024 04:20:06 +0000 Subject: [PATCH 119/656] Set background color of more actions pill This aligns the same logic as bindAppInfoPill and bindWindowingPill. The background color should be set on the container rather than the menu items. Bug: 379176148 Flag: EXEMPT bugfix Test: Switch between app with dark and light background. Press app handle on top. The background color of menu item should be updated. Change-Id: I73854817b8d5fca7c65418ce5ceba693eacda5e1 --- .../Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt index 54c247bff984..c98c6a39e305 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt @@ -650,6 +650,8 @@ class HandleMenu( } private fun bindMoreActionsPill(style: MenuStyle) { + moreActionsPill.background.setTint(style.backgroundColor) + arrayOf( screenshotBtn to SHOULD_SHOW_SCREENSHOT_BUTTON, newWindowBtn to shouldShowNewWindowButton, @@ -660,7 +662,6 @@ class HandleMenu( val shouldShow = it.second button.apply { isGone = !shouldShow - background.setTint(style.backgroundColor) setTextColor(style.textColor) compoundDrawableTintList = ColorStateList.valueOf(style.textColor) } -- GitLab From 49aead13eaffe543baf2ae0dafe8f622f122d895 Mon Sep 17 00:00:00 2001 From: Yuanru Qian Date: Mon, 18 Nov 2024 04:46:32 +0000 Subject: [PATCH 120/656] Stop reconnecting on uuid change when a manual disconnection happens. Bug: 362373044 Flag: EXEMPT minor fix Change-Id: I7951ce823a9ca64eb8f993ff206a3c3d110b4629 --- .../bluetooth/CachedBluetoothDevice.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index d0827b30efc9..4eb0567c67d9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -133,8 +133,9 @@ public class CachedBluetoothDevice implements Comparable * If an ACTION_UUID intent comes in within * MAX_UUID_DELAY_FOR_AUTO_CONNECT milliseconds, we will try auto-connect * again with the new UUIDs + * The value is reset if a manual disconnection happens. */ - private long mConnectAttempted; + private long mConnectAttempted = -1; // Active device state private boolean mIsActiveDeviceA2dp = false; @@ -369,6 +370,7 @@ public class CachedBluetoothDevice implements Comparable } public void disconnect() { + mConnectAttempted = -1; synchronized (mProfileLock) { if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) { for (CachedBluetoothDevice member : getMemberDevice()) { @@ -983,15 +985,19 @@ public class CachedBluetoothDevice implements Comparable } if (BluetoothUtils.D) { - Log.d(TAG, "onUuidChanged: Time since last connect=" - + (SystemClock.elapsedRealtime() - mConnectAttempted)); + long lastConnectAttempted = mConnectAttempted == -1 ? 0 : mConnectAttempted; + Log.d( + TAG, + "onUuidChanged: Time since last connect/manual disconnect=" + + (SystemClock.elapsedRealtime() - lastConnectAttempted)); } /* * If a connect was attempted earlier without any UUID, we will do the connect now. * Otherwise, allow the connect on UUID change. */ - if ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) { + if (mConnectAttempted != -1 + && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) { Log.d(TAG, "onUuidChanged: triggering connectDevice"); connectDevice(); } -- GitLab From 3f3968aea3b6e5f226d687437e8e08ba80539ed6 Mon Sep 17 00:00:00 2001 From: Louis Chang Date: Thu, 14 Nov 2024 07:55:14 +0000 Subject: [PATCH 121/656] Avoid sending top-resumed gain/loss to client if resume deferred Defer-resume is enabled while applying WCT. However, it is still possible starting an Activity in the transaction, i.e. HIERARCHY_OP_TYPE_PENDING_INTENT. In that case, the focused-app may be updated while the top-resumed activity is not. Instead of not updating the top-resumed activity, this CL now only avoid sending the top-resumed state loss/gain to the client, and keeps the top-resumed Activity being updated to match the wm hierarchy. Bug: 378110725 Test: ActivityTaskSupervisorTests Flag: EXEMPT bugfix Change-Id: Ic91bd4b367b2d5d19ad91d86a3e803bea0ff3a04 --- .../server/wm/ActivityTaskSupervisor.java | 27 ++++++++++++------- .../server/wm/WindowOrganizerController.java | 8 +----- .../wm/ActivityTaskSupervisorTests.java | 19 +++++++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4857b02eaf7c..70a8f563275f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -343,6 +343,11 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ private ActivityRecord mTopResumedActivity; + /** + * Cached value of the topmost resumed activity that reported to the client. + */ + private ActivityRecord mLastReportedTopResumedActivity; + /** * Flag indicating whether we're currently waiting for the previous top activity to handle the * loss of the state and report back before making new activity top resumed. @@ -2287,15 +2292,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * sent to the new top resumed activity. */ ActivityRecord updateTopResumedActivityIfNeeded(String reason) { - if (!readyToResume()) { - return mTopResumedActivity; - } final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { if (topRootTask == null) { // There's no focused task and there won't have any resumed activity either. scheduleTopResumedActivityStateLossIfNeeded(); + mTopResumedActivity = null; } if (mService.isSleepingLocked()) { // There won't be a next resumed activity. The top process should still be updated @@ -2339,25 +2342,27 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** Schedule current top resumed activity state loss */ private void scheduleTopResumedActivityStateLossIfNeeded() { - if (mTopResumedActivity == null) { + if (mLastReportedTopResumedActivity == null) { return; } // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity // before the prevTopActivity one hasn't reported back yet. So server never sent the top // resumed state change message to prevTopActivity. - if (!mTopResumedActivityWaitingForPrev - && mTopResumedActivity.scheduleTopResumedActivityChanged(false /* onTop */)) { - scheduleTopResumedStateLossTimeout(mTopResumedActivity); + if (!mTopResumedActivityWaitingForPrev && readyToResume() + && mLastReportedTopResumedActivity.scheduleTopResumedActivityChanged( + false /* onTop */)) { + scheduleTopResumedStateLossTimeout(mLastReportedTopResumedActivity); mTopResumedActivityWaitingForPrev = true; + mLastReportedTopResumedActivity = null; } - mTopResumedActivity = null; } /** Schedule top resumed state change if previous top activity already reported back. */ private void scheduleTopResumedActivityStateIfNeeded() { - if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) { + if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev && readyToResume()) { mTopResumedActivity.scheduleTopResumedActivityChanged(true /* onTop */); + mLastReportedTopResumedActivity = mTopResumedActivity; } } @@ -2611,6 +2616,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { */ void endDeferResume() { mDeferResumeCount--; + if (readyToResume() && mLastReportedTopResumedActivity != null + && mTopResumedActivity != mLastReportedTopResumedActivity) { + scheduleTopResumedActivityStateLossIfNeeded(); + } } /** @return True if resume can be called. */ diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 565f75b58230..734052099b74 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -788,9 +788,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub deferResume = false; // Already calls ensureActivityConfig mService.mRootWindowContainer.ensureActivitiesVisible(); - if (!mService.mRootWindowContainer.resumeFocusedTasksTopActivities()) { - mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT-effects"); - } + mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { for (int i = haveConfigChanges.size() - 1; i >= 0; --i) { haveConfigChanges.valueAt(i).forAllActivities(r -> { @@ -816,10 +814,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */); if (deferResume) { mService.mTaskSupervisor.endDeferResume(); - // Transient launching the Recents via HIERARCHY_OP_TYPE_PENDING_INTENT directly - // resume the Recents activity with no TRANSACT_EFFECTS_LIFECYCLE. Explicitly - // checks if the top resumed activity should be updated after defer-resume ended. - mService.mTaskSupervisor.updateTopResumedActivityIfNeeded("endWCT"); } mService.continueWindowLayout(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index 7f260f85a755..70f57eb40385 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -357,6 +357,25 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { assertEquals(activity1.app, mAtm.mTopApp); } + @Test + public void testTopResumedActivity_deferResume() { + final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity2.setState(ActivityRecord.State.RESUMED, "test"); + assertEquals(activity2.app, mAtm.mTopApp); + reset(activity2); + + // Verify that no top-resumed activity changes to the client while defer-resume enabled. + mSupervisor.beginDeferResume(); + activity1.getTask().moveToFront("test"); + activity1.setState(ActivityRecord.State.RESUMED, "test"); + verify(activity2, never()).scheduleTopResumedActivityChanged(eq(false)); + + // Verify that the change is scheduled to the client after defer-resumed disabled + mSupervisor.endDeferResume(); + verify(activity2).scheduleTopResumedActivityChanged(eq(false)); + } + /** * We need to launch home again after user unlocked for those displays that do not have * encryption aware home app. -- GitLab From 0e92f9364ff9eaf79d31f934029ca06ebdee9eaa Mon Sep 17 00:00:00 2001 From: Grace Cheng Date: Thu, 7 Nov 2024 11:41:40 +0000 Subject: [PATCH 122/656] Add permissions for secure lock device feature Flag: android.security.secure_lockdown API-Coverage-Bug: 376456619 Bug: 373422357 Test: N/A Change-Id: Ib2ac72f94026da39e77c6f15754d24e3a4bf3684 --- core/api/system-current.txt | 1 + core/res/AndroidManifest.xml | 11 +++++++++++ data/etc/privapp-permissions-platform.xml | 2 ++ packages/Shell/AndroidManifest.xml | 3 +++ 4 files changed, 17 insertions(+) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index bc0037d9318f..9456f9f5eaea 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -229,6 +229,7 @@ package android { field public static final String MANAGE_ROTATION_RESOLVER = "android.permission.MANAGE_ROTATION_RESOLVER"; field public static final String MANAGE_SAFETY_CENTER = "android.permission.MANAGE_SAFETY_CENTER"; field public static final String MANAGE_SEARCH_UI = "android.permission.MANAGE_SEARCH_UI"; + field @FlaggedApi("android.security.secure_lockdown") public static final String MANAGE_SECURE_LOCK_DEVICE = "android.permission.MANAGE_SECURE_LOCK_DEVICE"; field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY"; field public static final String MANAGE_SMARTSPACE = "android.permission.MANAGE_SMARTSPACE"; field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER"; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7fcbf19d137f..1274250c5e33 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -8651,6 +8651,17 @@ + + + + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 526320debb1a..1070ebdbb946 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -746,6 +746,9 @@ + + + -- GitLab From 43300b49dce6cb286ce5535b59b116d2033affec Mon Sep 17 00:00:00 2001 From: Grace Cheng Date: Thu, 14 Nov 2024 15:48:59 +0000 Subject: [PATCH 123/656] Define Secure Lock Device API surface Secure Lock is a new feature that enables users to remotely lock down their mobile device via authorized clients into an enhanced security state, which restricts access to sensitive data (app notifications, widgets, quick settings, assistant, etc) and requires both credential and biometric authentication for device entry. This change introduces a new AuthenticationPolicyManager, which external clients will use to call into SecureLockDeviceService via AuthenticationPolicyService. SecureLockDeviceService implements the new enableSecureLockDevice and disableSecureLockDevice API methods, which currently have a placeholder no-op implementation that will be filled out at a later time. Bug: 373422357 API-Coverage-Bug: 376456619 Flag: android.security.secure_lockdown Test: atest AuthenticationPolicyServiceTest Ignore-AOSP-First: adding flag to security package Change-Id: I7d2c97a8f51cb8a4475761a6c04ce9f32b38fd19 --- core/api/system-current.txt | 31 +++ .../android/app/SystemServiceRegistry.java | 21 ++ core/java/android/content/Context.java | 22 +- .../AuthenticationPolicyManager.java | 237 ++++++++++++++++++ .../DisableSecureLockDeviceParams.aidl | 21 ++ .../DisableSecureLockDeviceParams.java | 82 ++++++ .../EnableSecureLockDeviceParams.aidl | 21 ++ .../EnableSecureLockDeviceParams.java | 82 ++++++ .../IAuthenticationPolicyService.aidl | 32 +++ .../security/authenticationpolicy/OWNERS | 1 + .../AuthenticationPolicyService.java | 49 +++- .../SecureLockDeviceService.java | 120 +++++++++ .../SecureLockDeviceServiceInternal.java | 55 ++++ .../java/com/android/server/SystemServer.java | 7 + .../AuthenticationPolicyServiceTest.java | 18 ++ 15 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java create mode 100644 core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl create mode 100644 core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java create mode 100644 core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl create mode 100644 core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java create mode 100644 core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl create mode 100644 core/java/android/security/authenticationpolicy/OWNERS create mode 100644 services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java create mode 100644 services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 9456f9f5eaea..433d80a22a6e 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3870,6 +3870,7 @@ package android.content { field public static final String APP_INTEGRITY_SERVICE = "app_integrity"; field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume"; + field @FlaggedApi("android.security.secure_lockdown") public static final String AUTHENTICATION_POLICY_SERVICE = "authentication_policy"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 @@ -12859,6 +12860,36 @@ package android.security.advancedprotection { } +package android.security.authenticationpolicy { + + @FlaggedApi("android.security.secure_lockdown") public final class AuthenticationPolicyManager { + method @FlaggedApi("android.security.secure_lockdown") @RequiresPermission(android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE) public int disableSecureLockDevice(@NonNull android.security.authenticationpolicy.DisableSecureLockDeviceParams); + method @FlaggedApi("android.security.secure_lockdown") @RequiresPermission(android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE) public int enableSecureLockDevice(@NonNull android.security.authenticationpolicy.EnableSecureLockDeviceParams); + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_ALREADY_ENABLED = 6; // 0x6 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_INSUFFICIENT_BIOMETRICS = 5; // 0x5 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_INVALID_PARAMS = 3; // 0x3 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_NO_BIOMETRICS_ENROLLED = 4; // 0x4 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNKNOWN = 0; // 0x0 + field @FlaggedApi("android.security.secure_lockdown") public static final int ERROR_UNSUPPORTED = 2; // 0x2 + field @FlaggedApi("android.security.secure_lockdown") public static final int SUCCESS = 1; // 0x1 + } + + @FlaggedApi("android.security.secure_lockdown") public final class DisableSecureLockDeviceParams implements android.os.Parcelable { + ctor public DisableSecureLockDeviceParams(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + @FlaggedApi("android.security.secure_lockdown") public final class EnableSecureLockDeviceParams implements android.os.Parcelable { + ctor public EnableSecureLockDeviceParams(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + +} + package android.security.forensic { @FlaggedApi("android.security.afl_api") public class ForensicManager { diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 53a7dad76788..6a23349bf8aa 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -241,6 +241,8 @@ import android.security.advancedprotection.AdvancedProtectionManager; import android.security.advancedprotection.IAdvancedProtectionService; import android.security.attestationverification.AttestationVerificationManager; import android.security.attestationverification.IAttestationVerificationManagerService; +import android.security.authenticationpolicy.AuthenticationPolicyManager; +import android.security.authenticationpolicy.IAuthenticationPolicyService; import android.security.forensic.ForensicManager; import android.security.forensic.IForensicService; import android.security.keystore.KeyStoreManager; @@ -1025,6 +1027,25 @@ public final class SystemServiceRegistry { } }); + registerService(Context.AUTHENTICATION_POLICY_SERVICE, + AuthenticationPolicyManager.class, + new CachedServiceFetcher() { + @Override + public AuthenticationPolicyManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + if (!android.security.Flags.secureLockdown()) { + throw new ServiceNotFoundException( + Context.AUTHENTICATION_POLICY_SERVICE); + } + + final IBinder binder = ServiceManager.getServiceOrThrow( + Context.AUTHENTICATION_POLICY_SERVICE); + final IAuthenticationPolicyService service = + IAuthenticationPolicyService.Stub.asInterface(binder); + return new AuthenticationPolicyManager(ctx.getOuterContext(), service); + } + }); + registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class, new CachedServiceFetcher() { @Override diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6086f2455a31..5754e7e30c34 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -18,6 +18,7 @@ package android.content; import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER; import static android.content.flags.Flags.FLAG_ENABLE_BIND_PACKAGE_ISOLATED_PROCESS; +import static android.security.Flags.FLAG_SECURE_LOCKDOWN; import android.annotation.AttrRes; import android.annotation.CallbackExecutor; @@ -4256,6 +4257,7 @@ public abstract class Context { FINGERPRINT_SERVICE, //@hide: FACE_SERVICE, BIOMETRIC_SERVICE, + AUTHENTICATION_POLICY_SERVICE, MEDIA_ROUTER_SERVICE, TELEPHONY_SERVICE, TELEPHONY_SUBSCRIPTION_SERVICE, @@ -4437,6 +4439,9 @@ public abstract class Context { * web domain approval state. *

{@link #DISPLAY_HASH_SERVICE} ("display_hash") *
A {@link android.view.displayhash.DisplayHashManager} for management of display hashes. + *
{@link #AUTHENTICATION_POLICY_SERVICE} ("authentication_policy") + *
A {@link android.security.authenticationpolicy.AuthenticationPolicyManager} + * for managing authentication related policies on the device. * * *

Note: System services obtained via this API may be closely associated with @@ -4521,6 +4526,8 @@ public abstract class Context { * @see android.content.pm.verify.domain.DomainVerificationManager * @see #DISPLAY_HASH_SERVICE * @see android.view.displayhash.DisplayHashManager + * @see #AUTHENTICATION_POLICY_SERVICE + * @see android.security.authenticationpolicy.AuthenticationPolicyManager */ // TODO(b/347269120): Re-add @Nullable public abstract Object getSystemService(@ServiceName @NonNull String name); @@ -4543,7 +4550,8 @@ public abstract class Context { * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler}, * {@link android.app.usage.NetworkStatsManager}, * {@link android.content.pm.verify.domain.DomainVerificationManager}, - * {@link android.view.displayhash.DisplayHashManager}. + * {@link android.view.displayhash.DisplayHashManager} + * {@link android.security.authenticationpolicy.AuthenticationPolicyManager}. *

* *

@@ -5182,6 +5190,18 @@ public abstract class Context { */ public static final String AUTH_SERVICE = "auth"; + /** + * Use with {@link #getSystemService(String)} to retrieve an {@link + * android.security.authenticationpolicy.AuthenticationPolicyManager}. + * @see #getSystemService + * @see android.security.authenticationpolicy.AuthenticationPolicyManager + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final String AUTHENTICATION_POLICY_SERVICE = "authentication_policy"; + /** * Use with {@link #getSystemService(String)} to retrieve a * {@link android.hardware.fingerprint.FingerprintManager} for handling management diff --git a/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java new file mode 100644 index 000000000000..75abd5fa4bb0 --- /dev/null +++ b/core/java/android/security/authenticationpolicy/AuthenticationPolicyManager.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security.authenticationpolicy; + +import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE; +import static android.security.Flags.FLAG_SECURE_LOCKDOWN; + +import android.annotation.FlaggedApi; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.SystemService; +import android.content.Context; +import android.os.RemoteException; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * AuthenticationPolicyManager is a centralized interface for managing authentication related + * policies on the device. This includes device locking capabilities to protect users in "at risk" + * environments. + * + * AuthenticationPolicyManager is designed to protect Android users by integrating with apps and + * key system components, such as the lock screen. It is not related to enterprise control surfaces + * and does not offer additional administrative controls. + * + *

+ * To use this class, call {@link #enableSecureLockDevice} to enable secure lock on the device. + * This will require the caller to have the + * {@link android.Manifest.permission#MANAGE_SECURE_LOCK_DEVICE} permission. + * + *

+ * To disable secure lock on the device, call {@link #disableSecureLockDevice}. This will require + * the caller to have the {@link android.Manifest.permission#MANAGE_SECURE_LOCK_DEVICE} permission. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_SECURE_LOCKDOWN) +@SystemService(Context.AUTHENTICATION_POLICY_SERVICE) +public final class AuthenticationPolicyManager { + private static final String TAG = "AuthenticationPolicyManager"; + + @NonNull private final IAuthenticationPolicyService mAuthenticationPolicyService; + @NonNull private final Context mContext; + + /** + * Error result code for {@link #enableSecureLockDevice} and {@link + * #disableSecureLockDevice}. + * + * Secure lock device request status unknown. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_UNKNOWN = 0; + + /** + * Success result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * + * Secure lock device request successful. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int SUCCESS = 1; + + /** + * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * + * Secure lock device is unsupported. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_UNSUPPORTED = 2; + + + /** + * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * + * Invalid secure lock device request params provided. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_INVALID_PARAMS = 3; + + + /** + * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * + * Secure lock device is unavailable because there are no biometrics enrolled on the device. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_NO_BIOMETRICS_ENROLLED = 4; + + /** + * Error result code for {@link #enableSecureLockDevice} and {@link #disableSecureLockDevice}. + * + * Secure lock device is unavailable because the device has no biometric hardware or the + * biometric sensors do not meet + * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_STRONG} + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_INSUFFICIENT_BIOMETRICS = 5; + + /** + * Error result code for {@link #enableSecureLockDevice}. + * + * Secure lock is already enabled. + * + * @hide + */ + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public static final int ERROR_ALREADY_ENABLED = 6; + + /** + * Communicates the current status of a request to enable secure lock on the device. + * + * @hide + */ + @IntDef(prefix = {"ENABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = { + ERROR_UNKNOWN, + SUCCESS, + ERROR_UNSUPPORTED, + ERROR_INVALID_PARAMS, + ERROR_NO_BIOMETRICS_ENROLLED, + ERROR_INSUFFICIENT_BIOMETRICS, + ERROR_ALREADY_ENABLED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EnableSecureLockDeviceRequestStatus {} + + /** + * Communicates the current status of a request to disable secure lock on the device. + * + * @hide + */ + @IntDef(prefix = {"DISABLE_SECURE_LOCK_DEVICE_STATUS_"}, value = { + ERROR_UNKNOWN, + SUCCESS, + ERROR_UNSUPPORTED, + ERROR_INVALID_PARAMS, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface DisableSecureLockDeviceRequestStatus {} + + /** @hide */ + public AuthenticationPolicyManager(@NonNull Context context, + @NonNull IAuthenticationPolicyService authenticationPolicyService) { + mContext = context; + mAuthenticationPolicyService = authenticationPolicyService; + } + + /** + * Called by a privileged component to remotely enable secure lock on the device. + * + * Secure lock is an enhanced security state that restricts access to sensitive data (app + * notifications, widgets, quick settings, assistant, etc) and requires multi-factor + * authentication for device entry, such as + * {@link android.hardware.biometrics.BiometricManager.Authenticators#DEVICE_CREDENTIAL} and + * {@link android.hardware.biometrics.BiometricManager.Authenticators#BIOMETRIC_STRONG}. + * + * If secure lock is already enabled when this method is called, it will return + * {@link ERROR_ALREADY_ENABLED}. + * + * @param params EnableSecureLockDeviceParams for caller to supply params related to the secure + * lock device request + * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the secure lock + * device request + * + * @hide + */ + @EnableSecureLockDeviceRequestStatus + @RequiresPermission(MANAGE_SECURE_LOCK_DEVICE) + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public int enableSecureLockDevice(@NonNull EnableSecureLockDeviceParams params) { + try { + return mAuthenticationPolicyService.enableSecureLockDevice(params); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Called by a privileged component to disable secure lock on the device. + * + * If secure lock is already disabled when this method is called, it will return + * {@link SUCCESS}. + * + * @param params @DisableSecureLockDeviceParams for caller to supply params related to the + * secure lock device request + * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the secure lock + * device request + * + * @hide + */ + @DisableSecureLockDeviceRequestStatus + @RequiresPermission(MANAGE_SECURE_LOCK_DEVICE) + @SystemApi + @FlaggedApi(FLAG_SECURE_LOCKDOWN) + public int disableSecureLockDevice(@NonNull DisableSecureLockDeviceParams params) { + try { + return mAuthenticationPolicyService.disableSecureLockDevice(params); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl new file mode 100644 index 000000000000..81f7726a500c --- /dev/null +++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security.authenticationpolicy; + +/** + * @hide + */ +parcelable DisableSecureLockDeviceParams; \ No newline at end of file diff --git a/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java new file mode 100644 index 000000000000..64a3f0f60f96 --- /dev/null +++ b/core/java/android/security/authenticationpolicy/DisableSecureLockDeviceParams.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.authenticationpolicy; + +import static android.security.Flags.FLAG_SECURE_LOCKDOWN; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * Parameters related to a request to disable secure lock on the device. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_SECURE_LOCKDOWN) +public final class DisableSecureLockDeviceParams implements Parcelable { + + /** + * Client message associated with the request to disable secure lock on the device. This message + * will be shown on the device when secure lock mode is disabled. + */ + private final @NonNull String mMessage; + + /** + * Creates DisableSecureLockDeviceParams with the given params. + * + * @param message Allows clients to pass in a message with information about the request to + * disable secure lock on the device. This message will be shown to the user when + * secure lock mode is disabled. If an empty string is provided, it will default + * to a system-defined string (e.g. "Secure lock mode has been disabled.") + */ + public DisableSecureLockDeviceParams(@NonNull String message) { + mMessage = message; + } + + private DisableSecureLockDeviceParams(@NonNull Parcel in) { + mMessage = Objects.requireNonNull(in.readString8()); + } + + public static final @NonNull Creator CREATOR = + new Creator() { + @Override + public DisableSecureLockDeviceParams createFromParcel(Parcel in) { + return new DisableSecureLockDeviceParams(in); + } + + @Override + public DisableSecureLockDeviceParams[] newArray(int size) { + return new DisableSecureLockDeviceParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mMessage); + } +} diff --git a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl new file mode 100644 index 000000000000..9e496f82ec69 --- /dev/null +++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.aidl @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security.authenticationpolicy; + +/** + * @hide + */ +parcelable EnableSecureLockDeviceParams; \ No newline at end of file diff --git a/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java new file mode 100644 index 000000000000..1d727727ce37 --- /dev/null +++ b/core/java/android/security/authenticationpolicy/EnableSecureLockDeviceParams.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security.authenticationpolicy; + +import static android.security.Flags.FLAG_SECURE_LOCKDOWN; + +import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + + +/** + * Parameters related to a request to enable secure lock on the device. + * + * @hide + */ +@SystemApi +@FlaggedApi(FLAG_SECURE_LOCKDOWN) +public final class EnableSecureLockDeviceParams implements Parcelable { + + /** + * Client message associated with the request to enable secure lock on the device. This message + * will be shown on the device when secure lock mode is enabled. + */ + private final @NonNull String mMessage; + + /** + * Creates EnableSecureLockDeviceParams with the given params. + * + * @param message Allows clients to pass in a message with information about the request to + * enable secure lock on the device. This message will be shown to the user when + * secure lock mode is enabled. If an empty string is provided, it will default + * to a system-defined string (e.g. "Device is securely locked remotely.") + */ + public EnableSecureLockDeviceParams(@NonNull String message) { + mMessage = message; + } + + private EnableSecureLockDeviceParams(@NonNull Parcel in) { + mMessage = Objects.requireNonNull(in.readString8()); + } + + public static final @NonNull Creator CREATOR = + new Creator() { + @Override + public EnableSecureLockDeviceParams createFromParcel(Parcel in) { + return new EnableSecureLockDeviceParams(in); + } + + @Override + public EnableSecureLockDeviceParams[] newArray(int size) { + return new EnableSecureLockDeviceParams[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mMessage); + } +} diff --git a/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl b/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl new file mode 100644 index 000000000000..5ad4534c257a --- /dev/null +++ b/core/java/android/security/authenticationpolicy/IAuthenticationPolicyService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.authenticationpolicy; + +import android.security.authenticationpolicy.EnableSecureLockDeviceParams; +import android.security.authenticationpolicy.DisableSecureLockDeviceParams; + +/** + * Communication channel from AuthenticationPolicyManager to AuthenticationPolicyService. + * @hide + */ +interface IAuthenticationPolicyService { + @EnforcePermission("MANAGE_SECURE_LOCK_DEVICE") + int enableSecureLockDevice(in EnableSecureLockDeviceParams params); + + @EnforcePermission("MANAGE_SECURE_LOCK_DEVICE") + int disableSecureLockDevice(in DisableSecureLockDeviceParams params); +} \ No newline at end of file diff --git a/core/java/android/security/authenticationpolicy/OWNERS b/core/java/android/security/authenticationpolicy/OWNERS new file mode 100644 index 000000000000..4310d1a3a9db --- /dev/null +++ b/core/java/android/security/authenticationpolicy/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/security/authenticationpolicy/OWNERS \ No newline at end of file diff --git a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java index b8a4a9c26feb..6798a6146ae0 100644 --- a/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java +++ b/services/core/java/com/android/server/security/authenticationpolicy/AuthenticationPolicyService.java @@ -16,8 +16,11 @@ package com.android.server.security.authenticationpolicy; +import static android.Manifest.permission.MANAGE_SECURE_LOCK_DEVICE; + import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; +import android.annotation.EnforcePermission; import android.app.KeyguardManager; import android.content.Context; import android.content.pm.PackageManager; @@ -32,9 +35,14 @@ import android.hardware.biometrics.events.AuthenticationStoppedInfo; import android.hardware.biometrics.events.AuthenticationSucceededInfo; import android.os.Build; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.security.authenticationpolicy.AuthenticationPolicyManager; +import android.security.authenticationpolicy.DisableSecureLockDeviceParams; +import android.security.authenticationpolicy.EnableSecureLockDeviceParams; +import android.security.authenticationpolicy.IAuthenticationPolicyService; import android.util.Log; import android.util.Slog; import android.util.SparseIntArray; @@ -74,6 +82,7 @@ public class AuthenticationPolicyService extends SystemService { private final KeyguardManager mKeyguardManager; private final WindowManagerInternal mWindowManager; private final UserManagerInternal mUserManager; + private SecureLockDeviceServiceInternal mSecureLockDeviceService; @VisibleForTesting final SparseIntArray mFailedAttemptsForUser = new SparseIntArray(); private final SparseLongArray mLastLockedTimestamp = new SparseLongArray(); @@ -94,10 +103,16 @@ public class AuthenticationPolicyService extends SystemService { mWindowManager = Objects.requireNonNull( LocalServices.getService(WindowManagerInternal.class)); mUserManager = Objects.requireNonNull(LocalServices.getService(UserManagerInternal.class)); + if (android.security.Flags.secureLockdown()) { + mSecureLockDeviceService = Objects.requireNonNull( + LocalServices.getService(SecureLockDeviceServiceInternal.class)); + } } @Override - public void onStart() {} + public void onStart() { + publishBinderService(Context.AUTHENTICATION_POLICY_SERVICE, mService); + } @Override public void onBootPhase(int phase) { @@ -294,4 +309,36 @@ public class AuthenticationPolicyService extends SystemService { // next successful primary or biometric auth happens mLastLockedTimestamp.put(userId, SystemClock.elapsedRealtime()); } + + private final IBinder mService = new IAuthenticationPolicyService.Stub() { + /** + * @see AuthenticationPolicyManager#enableSecureLockDevice(EnableSecureLockDeviceParams) + * @param params EnableSecureLockDeviceParams for caller to supply params related + * to the secure lock device request + * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the Secure + * Lock Device request + */ + @Override + @EnforcePermission(MANAGE_SECURE_LOCK_DEVICE) + @AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus + public int enableSecureLockDevice(EnableSecureLockDeviceParams params) { + enableSecureLockDevice_enforcePermission(); + return mSecureLockDeviceService.enableSecureLockDevice(params); + } + + /** + * @see AuthenticationPolicyManager#disableSecureLockDevice(DisableSecureLockDeviceParams) + * @param params @DisableSecureLockDeviceParams for caller to supply params related + * to the secure lock device request + * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the Secure + * Lock Device request + */ + @Override + @EnforcePermission(MANAGE_SECURE_LOCK_DEVICE) + @AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus + public int disableSecureLockDevice(DisableSecureLockDeviceParams params) { + disableSecureLockDevice_enforcePermission(); + return mSecureLockDeviceService.disableSecureLockDevice(params); + } + }; } diff --git a/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java new file mode 100644 index 000000000000..7b89723deb6c --- /dev/null +++ b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceService.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.authenticationpolicy; + +import android.annotation.NonNull; +import android.content.Context; +import android.security.authenticationpolicy.AuthenticationPolicyManager; +import android.security.authenticationpolicy.AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus; +import android.security.authenticationpolicy.AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus; +import android.security.authenticationpolicy.DisableSecureLockDeviceParams; +import android.security.authenticationpolicy.EnableSecureLockDeviceParams; +import android.util.Slog; + +import com.android.server.LocalServices; +import com.android.server.SystemService; + +/** + * System service for remotely calling secure lock on the device. + * + * Callers will access this class via + * {@link com.android.server.security.authenticationpolicy.AuthenticationPolicyService}. + * + * @see AuthenticationPolicyService + * @see AuthenticationPolicyManager#enableSecureLockDevice + * @see AuthenticationPolicyManager#disableSecureLockDevice + * @hide + */ +public class SecureLockDeviceService extends SecureLockDeviceServiceInternal { + private static final String TAG = "SecureLockDeviceService"; + private final Context mContext; + + public SecureLockDeviceService(@NonNull Context context) { + mContext = context; + } + + private void start() { + // Expose private service for system components to use. + LocalServices.addService(SecureLockDeviceServiceInternal.class, this); + } + + /** + * @see AuthenticationPolicyManager#enableSecureLockDevice + * @param params EnableSecureLockDeviceParams for caller to supply params related + * to the secure lock device request + * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the + * secure lock device request + * + * @hide + */ + @Override + @EnableSecureLockDeviceRequestStatus + public int enableSecureLockDevice(EnableSecureLockDeviceParams params) { + // (1) Call into system_server to lock device, configure allowed auth types + // for secure lock + // TODO: lock device, configure allowed authentication types for device entry + // (2) Call into framework to configure secure lock 2FA lockscreen + // update, UI & string updates + // TODO: implement 2FA lockscreen when SceneContainerFlag.isEnabled() + // TODO: implement 2FA lockscreen when !SceneContainerFlag.isEnabled() + // (3) Call into framework to configure keyguard security updates + // TODO: implement security updates + return AuthenticationPolicyManager.ERROR_UNSUPPORTED; + } + + /** + * @see AuthenticationPolicyManager#disableSecureLockDevice + * @param params @DisableSecureLockDeviceParams for caller to supply params related + * to the secure lock device request + * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the + * secure lock device request + * + * @hide + */ + @Override + @DisableSecureLockDeviceRequestStatus + public int disableSecureLockDevice(DisableSecureLockDeviceParams params) { + // (1) Call into system_server to reset allowed auth types + // TODO: reset allowed authentication types for device entry; + // (2) Call into framework to disable secure lock 2FA lockscreen, reset UI + // & string updates + // TODO: implement reverting to normal lockscreen when SceneContainerFlag.isEnabled() + // TODO: implement reverting to normal lockscreen when !SceneContainerFlag.isEnabled() + // (3) Call into framework to revert keyguard security updates + // TODO: implement reverting security updates + return AuthenticationPolicyManager.ERROR_UNSUPPORTED; + } + + /** + * System service lifecycle. + */ + public static final class Lifecycle extends SystemService { + private final SecureLockDeviceService mService; + + public Lifecycle(@NonNull Context context) { + super(context); + mService = new SecureLockDeviceService(context); + } + + @Override + public void onStart() { + Slog.i(TAG, "Starting SecureLockDeviceService"); + mService.start(); + Slog.i(TAG, "Started SecureLockDeviceService"); + } + } +} diff --git a/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java new file mode 100644 index 000000000000..b90370956d8b --- /dev/null +++ b/services/core/java/com/android/server/security/authenticationpolicy/SecureLockDeviceServiceInternal.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.security.authenticationpolicy; + +import android.security.authenticationpolicy.AuthenticationPolicyManager; +import android.security.authenticationpolicy.AuthenticationPolicyManager.DisableSecureLockDeviceRequestStatus; +import android.security.authenticationpolicy.AuthenticationPolicyManager.EnableSecureLockDeviceRequestStatus; +import android.security.authenticationpolicy.DisableSecureLockDeviceParams; +import android.security.authenticationpolicy.EnableSecureLockDeviceParams; + +/** + * Local system service interface for {@link SecureLockDeviceService}. + * + *

No permission / argument checks will be performed inside. + * Callers must check the calling app permission and the calling package name. + * + * @hide + */ +public abstract class SecureLockDeviceServiceInternal { + private static final String TAG = "SecureLockDeviceServiceInternal"; + + /** + * @see AuthenticationPolicyManager#enableSecureLockDevice(EnableSecureLockDeviceParams) + * @param params EnableSecureLockDeviceParams for caller to supply params related + * to the secure lock request + * @return @EnableSecureLockDeviceRequestStatus int indicating the result of the + * secure lock request + */ + @EnableSecureLockDeviceRequestStatus + public abstract int enableSecureLockDevice(EnableSecureLockDeviceParams params); + + /** + * @see AuthenticationPolicyManager#disableSecureLockDevice(DisableSecureLockDeviceParams) + * @param params @DisableSecureLockDeviceParams for caller to supply params related + * to the secure lock device request + * @return @DisableSecureLockDeviceRequestStatus int indicating the result of the + * secure lock device request + */ + @DisableSecureLockDeviceRequestStatus + public abstract int disableSecureLockDevice(DisableSecureLockDeviceParams params); +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index da478f38498b..0dc1ac32c379 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -251,6 +251,7 @@ import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyChainSystemService; import com.android.server.security.advancedprotection.AdvancedProtectionService; import com.android.server.security.authenticationpolicy.AuthenticationPolicyService; +import com.android.server.security.authenticationpolicy.SecureLockDeviceService; import com.android.server.security.forensic.ForensicService; import com.android.server.security.rkp.RemoteProvisioningService; import com.android.server.selinux.SelinuxAuditLogsService; @@ -2659,6 +2660,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(AuthService.class); t.traceEnd(); + if (android.security.Flags.secureLockdown()) { + t.traceBegin("StartSecureLockDeviceService.Lifecycle"); + mSystemServiceManager.startService(SecureLockDeviceService.Lifecycle.class); + t.traceEnd(); + } + if (android.adaptiveauth.Flags.enableAdaptiveAuth()) { t.traceBegin("StartAuthenticationPolicyService"); mSystemServiceManager.startService(AuthenticationPolicyService.class); diff --git a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java index 2238a1be97a1..ee8eb9b35088 100644 --- a/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/security/authenticationpolicy/AuthenticationPolicyServiceTest.java @@ -19,12 +19,14 @@ package com.android.server.security.authenticationpolicy; import static android.adaptiveauth.Flags.FLAG_ENABLE_ADAPTIVE_AUTH; import static android.adaptiveauth.Flags.FLAG_REPORT_BIOMETRIC_AUTH_ATTEMPTS; import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS; +import static android.security.authenticationpolicy.AuthenticationPolicyManager.ERROR_UNSUPPORTED; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST; import static com.android.server.security.authenticationpolicy.AuthenticationPolicyService.MAX_ALLOWED_FAILED_AUTH_ATTEMPTS; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -95,6 +97,8 @@ public class AuthenticationPolicyServiceTest { private WindowManagerInternal mWindowManager; @Mock private UserManagerInternal mUserManager; + @Mock + private SecureLockDeviceServiceInternal mSecureLockDeviceService; @Captor ArgumentCaptor mLockSettingsStateListenerCaptor; @@ -123,6 +127,11 @@ public class AuthenticationPolicyServiceTest { LocalServices.addService(WindowManagerInternal.class, mWindowManager); LocalServices.removeServiceForTest(UserManagerInternal.class); LocalServices.addService(UserManagerInternal.class, mUserManager); + if (android.security.Flags.secureLockdown()) { + LocalServices.removeServiceForTest(SecureLockDeviceServiceInternal.class); + LocalServices.addService(SecureLockDeviceServiceInternal.class, + mSecureLockDeviceService); + } mAuthenticationPolicyService = new AuthenticationPolicyService( mContext, mLockPatternUtils); @@ -136,6 +145,12 @@ public class AuthenticationPolicyServiceTest { // Set PRIMARY_USER_ID as the parent of MANAGED_PROFILE_USER_ID when(mUserManager.getProfileParentId(eq(MANAGED_PROFILE_USER_ID))) .thenReturn(PRIMARY_USER_ID); + if (android.security.Flags.secureLockdown()) { + when(mSecureLockDeviceService.enableSecureLockDevice(any())) + .thenReturn(ERROR_UNSUPPORTED); + when(mSecureLockDeviceService.disableSecureLockDevice(any())) + .thenReturn(ERROR_UNSUPPORTED); + } } @After @@ -143,6 +158,9 @@ public class AuthenticationPolicyServiceTest { LocalServices.removeServiceForTest(LockSettingsInternal.class); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.removeServiceForTest(UserManagerInternal.class); + if (android.security.Flags.secureLockdown()) { + LocalServices.removeServiceForTest(SecureLockDeviceServiceInternal.class); + } } @Test -- GitLab From 0b1fe74a64dc38800d1405e59f467514e30e2297 Mon Sep 17 00:00:00 2001 From: "Pechetty Sravani (xWF)" Date: Mon, 18 Nov 2024 07:46:30 +0000 Subject: [PATCH 124/656] Revert "Address frozen notification API feedback" Revert submission 3267825-binder-api Reason for revert: Reverted changes: /q/submissionid:3267825-binder-api Change-Id: Ib02a539e4ba8518f29b78e139f75acb77bf9dfa8 --- core/api/current.txt | 4 +- core/java/android/os/BinderProxy.java | 34 +++++--------- core/java/android/os/IBinder.java | 39 +--------------- core/java/android/os/RemoteCallbackList.java | 46 ++----------------- .../bfscctestapp/BfsccTestAppCmdService.java | 3 -- ...nderFrozenStateChangeNotificationTest.java | 4 +- .../os/BinderDeathDispatcherTest.java | 3 +- 7 files changed, 21 insertions(+), 112 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index bc2a25dbc877..519deac25177 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33261,7 +33261,7 @@ package android.os { } public interface IBinder { - method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException; @@ -33885,7 +33885,6 @@ package android.os { method public void finishBroadcast(); method public Object getBroadcastCookie(int); method public E getBroadcastItem(int); - method @FlaggedApi("android.os.binder_frozen_state_change_callback") @Nullable public java.util.concurrent.Executor getExecutor(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize(); method public Object getRegisteredCallbackCookie(int); @@ -33906,7 +33905,6 @@ package android.os { @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder { ctor public RemoteCallbackList.Builder(int); method @NonNull public android.os.RemoteCallbackList build(); - method @NonNull public android.os.RemoteCallbackList.Builder setExecutor(@NonNull java.util.concurrent.Executor); method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback); method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int); } diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 01222cdd38b3..3b5a99ed089a 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -36,7 +36,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -652,39 +651,28 @@ public final class BinderProxy implements IBinder { private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags); /** - * This map is to hold strong reference to the frozen state callbacks. - * - * The callbacks are only weakly referenced by JNI so the strong references here are needed to - * keep the callbacks around until the proxy is GC'ed. - * - * The key is the original callback passed into {@link #addFrozenStateChangeCallback}. The value - * is the wrapped callback created in {@link #addFrozenStateChangeCallback} to dispatch the - * calls on the desired executor. + * This list is to hold strong reference to the frozen state callbacks. The callbacks are only + * weakly referenced by JNI so the strong references here are needed to keep the callbacks + * around until the proxy is GC'ed. */ - private Map mFrozenStateChangeCallbacks = - Collections.synchronizedMap(new HashMap<>()); + private List mFrozenStateChangeCallbacks = + Collections.synchronizedList(new ArrayList<>()); /** * See {@link IBinder#addFrozenStateChangeCallback(FrozenStateChangeCallback)} */ - public void addFrozenStateChangeCallback(Executor executor, FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) throws RemoteException { - FrozenStateChangeCallback wrappedCallback = (who, state) -> - executor.execute(() -> callback.onFrozenStateChanged(who, state)); - addFrozenStateChangeCallbackNative(wrappedCallback); - mFrozenStateChangeCallbacks.put(callback, wrappedCallback); + addFrozenStateChangeCallbackNative(callback); + mFrozenStateChangeCallbacks.add(callback); } /** * See {@link IBinder#removeFrozenStateChangeCallback} */ - public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) - throws IllegalArgumentException { - FrozenStateChangeCallback wrappedCallback = mFrozenStateChangeCallbacks.remove(callback); - if (wrappedCallback == null) { - throw new IllegalArgumentException("callback not found"); - } - return removeFrozenStateChangeCallbackNative(wrappedCallback); + public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) { + mFrozenStateChangeCallbacks.remove(callback); + return removeFrozenStateChangeCallbackNative(callback); } private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback) diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 8cfd32449537..a997f4c86704 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -16,7 +16,6 @@ package android.os; -import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -26,7 +25,6 @@ import android.compat.annotation.UnsupportedAppUsage; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.Executor; /** * Base interface for a remotable object, the core part of a lightweight @@ -399,31 +397,12 @@ public interface IBinder { @interface State { } - /** - * Represents the frozen state of the remote process. - * - * While in this state, the remote process won't be able to receive and handle a - * transaction. Therefore, any asynchronous transactions will be buffered and delivered when - * the process is unfrozen, and any synchronous transactions will result in an error. - * - * Buffered transactions may be stale by the time that the process is unfrozen and handles - * them. To avoid overwhelming the remote process with stale events or overflowing their - * buffers, it's best to avoid sending binder transactions to a frozen process. - */ int STATE_FROZEN = 0; - - /** - * Represents the unfrozen state of the remote process. - * - * In this state, the process hosting the object can execute and is not restricted - * by the freezer from using the CPU or responding to binder transactions. - */ int STATE_UNFROZEN = 1; /** * Interface for receiving a callback when the process hosting an IBinder * has changed its frozen state. - * * @param who The IBinder whose hosting process has changed state. * @param state The latest state. */ @@ -448,29 +427,13 @@ public interface IBinder { *

You will only receive state change notifications for remote binders, as local binders by * definition can't be frozen without you being frozen too.

* - * @param executor The executor on which to run the callback. - * @param callback The callback used to deliver state change notifications. - * *

@throws {@link UnsupportedOperationException} if the kernel binder driver does not support * this feature. */ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) - default void addFrozenStateChangeCallback( - @NonNull @CallbackExecutor Executor executor, - @NonNull FrozenStateChangeCallback callback) - throws RemoteException { - throw new UnsupportedOperationException(); - } - - /** - * Same as {@link #addFrozenStateChangeCallback(Executor, FrozenStateChangeCallback)} except - * that callbacks are invoked on a binder thread. - * - * @hide - */ default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) throws RemoteException { - addFrozenStateChangeCallback(Runnable::run, callback); + throw new UnsupportedOperationException(); } /** diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index d5630fd46eb4..91c482faf7d7 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -16,7 +16,6 @@ package android.os; -import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -30,7 +29,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -136,7 +134,6 @@ public class RemoteCallbackList { private final @FrozenCalleePolicy int mFrozenCalleePolicy; private final int mMaxQueueSize; - private final Executor mExecutor; private final class Interface implements IBinder.DeathRecipient, IBinder.FrozenStateChangeCallback { @@ -200,7 +197,7 @@ public class RemoteCallbackList { void maybeSubscribeToFrozenCallback() throws RemoteException { if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { try { - mBinder.addFrozenStateChangeCallback(mExecutor, this); + mBinder.addFrozenStateChangeCallback(this); } catch (UnsupportedOperationException e) { // The kernel does not support frozen notifications. In this case we want to // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply @@ -240,7 +237,6 @@ public class RemoteCallbackList { private @FrozenCalleePolicy int mFrozenCalleePolicy; private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE; private InterfaceDiedCallback mInterfaceDiedCallback; - private Executor mExecutor; /** * Creates a Builder for {@link RemoteCallbackList}. @@ -288,18 +284,6 @@ public class RemoteCallbackList { return this; } - /** - * Sets the executor to be used when invoking callbacks asynchronously. - * - * This is only used when callbacks need to be invoked asynchronously, e.g. when the process - * hosting a callback becomes unfrozen. Callbacks that can be invoked immediately run on the - * same thread that calls {@link #broadcast} synchronously. - */ - public @NonNull Builder setExecutor(@NonNull @CallbackExecutor Executor executor) { - mExecutor = executor; - return this; - } - /** * For notifying when the process hosting a callback interface has died. * @@ -324,21 +308,15 @@ public class RemoteCallbackList { * @return The built {@link RemoteCallbackList} object. */ public @NonNull RemoteCallbackList build() { - Executor executor = mExecutor; - if (executor == null && mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { - // TODO Throw an exception here once the existing API caller is updated to provide - // an executor. - executor = new HandlerExecutor(Handler.getMain()); - } if (mInterfaceDiedCallback != null) { - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor) { + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize) { @Override public void onCallbackDied(E deadInterface, Object cookie) { mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie); } }; } - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor); + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize); } } @@ -362,16 +340,6 @@ public class RemoteCallbackList { return mMaxQueueSize; } - /** - * Returns the executor used when invoking callbacks asynchronously. - * - * @return The executor. - */ - @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) - public @Nullable Executor getExecutor() { - return mExecutor; - } - /** * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to *

@@ -379,7 +347,7 @@ public class RemoteCallbackList {
      * 
*/ public RemoteCallbackList() { - this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE, null); + this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE); } /** @@ -394,14 +362,10 @@ public class RemoteCallbackList { * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be * dropped to keep the size under limit. Ignored except for * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. - * - * @param executor The executor used when invoking callbacks asynchronously. */ - private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize, - @CallbackExecutor Executor executor) { + private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) { mFrozenCalleePolicy = frozenCalleePolicy; mMaxQueueSize = maxQueueSize; - mExecutor = executor; } /** diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java index 945147db1ef5..fe54aa8d87f0 100644 --- a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java +++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java @@ -18,8 +18,6 @@ package com.android.frameworks.coretests.bfscctestapp; import android.app.Service; import android.content.Intent; -import android.os.Handler; -import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; @@ -38,7 +36,6 @@ public class BfsccTestAppCmdService extends Service { @Override public void listenTo(IBinder binder) throws RemoteException { binder.addFrozenStateChangeCallback( - new HandlerExecutor(Handler.getMain()), (IBinder who, int state) -> mNotifications.offer(state)); } diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java index 523fe1a8aa5d..195a18a5f521 100644 --- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java +++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java @@ -200,7 +200,7 @@ public class BinderFrozenStateChangeNotificationTest { IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> results.offer(who); try { - binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); + binder.addFrozenStateChangeCallback(callback); } catch (UnsupportedOperationException e) { return; } @@ -227,7 +227,7 @@ public class BinderFrozenStateChangeNotificationTest { final IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> queue.offer(state == IBinder.FrozenStateChangeCallback.STATE_FROZEN); - binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); + binder.addFrozenStateChangeCallback(callback); return callback; } catch (UnsupportedOperationException e) { return null; diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java index f888c9ba93a9..d0070678d4fa 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java @@ -42,7 +42,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; -import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) @@ -126,7 +125,7 @@ public class BinderDeathDispatcherTest { } @Override - public void addFrozenStateChangeCallback(Executor e, FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) throws RemoteException { } -- GitLab From 175884b684156498b3942ac6fb0a43b795d5e9a9 Mon Sep 17 00:00:00 2001 From: Madhav Iyengar Date: Mon, 18 Nov 2024 07:15:25 +0000 Subject: [PATCH 125/656] contexthub: Handle unsupported APIs Bug: b/378545373 Flag: android.chre.flags.offload_api Test: presubmit Change-Id: Ica5e7e1e0aa162b1408a6c860c5856db43519e7a --- .../server/location/contexthub/ContextHubService.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index f611c57dab03..8763073ad644 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -322,8 +322,15 @@ public class ContextHubService extends IContextHubService.Stub { } if (Flags.offloadApi()) { - mHubInfoRegistry = new HubInfoRegistry(mContextHubWrapper); - Log.i(TAG, "Enabling generic offload API"); + HubInfoRegistry registry; + try { + registry = new HubInfoRegistry(mContextHubWrapper); + Log.i(TAG, "Enabling generic offload API"); + } catch (UnsupportedOperationException e) { + registry = null; + Log.w(TAG, "Generic offload API not supported, disabling"); + } + mHubInfoRegistry = registry; } else { mHubInfoRegistry = null; Log.i(TAG, "Disabling generic offload API"); -- GitLab From c27a70d16a526c310c586d9f1868613dc72a8d4e Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Fri, 15 Nov 2024 23:19:39 +0000 Subject: [PATCH 126/656] Prioritize strength error over hardware error Fixes: 375957176 Flag: EXEMPT bug fix Test: atest PreAuthInfoTest Change-Id: I911fc76d14837e277354cb4c0e2e56ee486fcfd9 Merged-In: I911fc76d14837e277354cb4c0e2e56ee486fcfd9 (cherry picked from commit 771f6d72b659f70431041982eecbe90d30bb72dc) --- .../server/biometrics/PreAuthInfo.java | 8 +++--- .../server/biometrics/PreAuthInfoTest.java | 25 +++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index f085647c9676..645206a91c0a 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -174,10 +174,6 @@ class PreAuthInfo { return BIOMETRIC_NO_HARDWARE; } - if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) { - return BIOMETRIC_HARDWARE_NOT_DETECTED; - } - final boolean wasStrongEnough = Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); final boolean isStrongEnough = @@ -189,6 +185,10 @@ class PreAuthInfo { return BIOMETRIC_INSUFFICIENT_STRENGTH; } + if (sensor.modality == TYPE_FACE && biometricCameraManager.isAnyCameraUnavailable()) { + return BIOMETRIC_HARDWARE_NOT_DETECTED; + } + try { if (!sensor.impl.isHardwareDetected(opPackageName)) { return BIOMETRIC_HARDWARE_NOT_DETECTED; diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java index b40d7ee39a4c..67202174535c 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java @@ -20,6 +20,7 @@ import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NO import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE; +import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; import static com.android.server.biometrics.sensors.LockoutTracker.LOCKOUT_NONE; @@ -55,6 +56,7 @@ public class PreAuthInfoTest { @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final int USER_ID = 0; private static final int SENSOR_ID_FINGERPRINT = 0; private static final int SENSOR_ID_FACE = 1; private static final String TEST_PACKAGE_NAME = "PreAuthInfoTestPackage"; @@ -184,6 +186,20 @@ public class PreAuthInfoTest { assertThat(preAuthInfo.eligibleSensors.get(0).modality).isEqualTo(TYPE_FINGERPRINT); } + @Test + public void prioritizeStrengthErrorBeforeCameraUnavailableError() throws Exception { + final BiometricSensor sensor = getFaceSensorWithStrength( + BiometricManager.Authenticators.BIOMETRIC_WEAK); + final PromptInfo promptInfo = new PromptInfo(); + promptInfo.setAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG); + promptInfo.setNegativeButtonText(TEST_PACKAGE_NAME); + final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, + mSettingObserver, List.of(sensor), USER_ID , promptInfo, TEST_PACKAGE_NAME, + false /* checkDevicePolicyManager */, mContext, mBiometricCameraManager); + + assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE); + } + private BiometricSensor getFingerprintSensor() { BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT, TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG, @@ -202,9 +218,10 @@ public class PreAuthInfoTest { return sensor; } - private BiometricSensor getFaceSensor() { + private BiometricSensor getFaceSensorWithStrength( + @BiometricManager.Authenticators.Types int sensorStrength) { BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FACE, TYPE_FACE, - BiometricManager.Authenticators.BIOMETRIC_STRONG, mFaceAuthenticator) { + sensorStrength, mFaceAuthenticator) { @Override boolean confirmationAlwaysRequired(int userId) { return false; @@ -218,4 +235,8 @@ public class PreAuthInfoTest { return sensor; } + + private BiometricSensor getFaceSensor() { + return getFaceSensorWithStrength(BiometricManager.Authenticators.BIOMETRIC_STRONG); + } } -- GitLab From 2fa48adb1ef3cd2e6987ccaa6c1395da9f792911 Mon Sep 17 00:00:00 2001 From: Eric Lin Date: Mon, 18 Nov 2024 17:51:55 +0800 Subject: [PATCH 127/656] Revert DeviceState callback to main thread in FFP. This partially reverts commit I8a4af1c5ac98f5653e4293a049291ba5ff7cd464. Revert device state callback to the main thread in FFP to fix crashes in Chrome and Photos. The original change allowed callbacks off the main thread but this caused crashes because: 1. Chrome loads window extension directly; it crashs when receiving callbacks on binder thread. 2. Photos uses Java API with Runnable::run, breaking the flowOn main operator; it crashs when receiving callbacks on the binder thread. Window layout info remains available via getter because the device state is available synchronously in the DeviceStateManagerGlobal constructor, so this rollback does not impact functionality. Bug: 337820752 Bug: 379501835 Test: atest WMJetpackUnitTests:DeviceStateManagerFoldingFeatureProducerTest Test: Run with latest Chrome (131.0.6778.39) Flag: com.android.window.flags.wlinfo_oncreate Change-Id: Ifc5af9955a27b6cfb5405e63c0763f3c55a5a75e --- ...iceStateManagerFoldingFeatureProducer.java | 65 ++++--------------- ...eStateManagerFoldingFeatureProducerTest.kt | 30 +-------- 2 files changed, 14 insertions(+), 81 deletions(-) diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index 089613853555..5ea38431829e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -30,8 +30,6 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; -import androidx.annotation.BinderThread; -import androidx.annotation.GuardedBy; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -39,14 +37,12 @@ import androidx.window.common.layout.CommonFoldingFeature; import androidx.window.common.layout.DisplayFoldFeatureCommon; import com.android.internal.R; -import com.android.window.flags.Flags; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.Executor; import java.util.function.Consumer; /** @@ -70,20 +66,8 @@ public final class DeviceStateManagerFoldingFeatureProducer * "rear display". Concurrent mode for example is activated via public API and can be active in * both the "open" and "half folded" device states. */ - // TODO: b/337820752 - Add @GuardedBy("mCurrentDeviceStateLock") after flag cleanup. private DeviceState mCurrentDeviceState = INVALID_DEVICE_STATE; - /** - * Lock to synchronize access to {@link #mCurrentDeviceState}. - * - *

This lock is used to ensure thread-safety when accessing and modifying the - * {@link #mCurrentDeviceState} field. It is acquired by both the binder thread (if - * {@link Flags#wlinfoOncreate()} is enabled) and the main thread (if - * {@link Flags#wlinfoOncreate()} is disabled) to prevent race conditions and - * ensure data consistency. - */ - private final Object mCurrentDeviceStateLock = new Object(); - @NonNull private final RawFoldingFeatureProducer mRawFoldSupplier; @@ -95,15 +79,12 @@ public final class DeviceStateManagerFoldingFeatureProducer // The GuardedBy analysis is intra-procedural, meaning it doesn’t consider the getData() // implementation. See https://errorprone.info/bugpattern/GuardedBy for limitations. @SuppressWarnings("GuardedBy") - @BinderThread // When Flags.wlinfoOncreate() is enabled. - @MainThread // When Flags.wlinfoOncreate() is disabled. + @MainThread @Override public void onDeviceStateChanged(@NonNull DeviceState state) { - synchronized (mCurrentDeviceStateLock) { - mCurrentDeviceState = state; - mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this - ::notifyFoldingFeatureChangeLocked); - } + mCurrentDeviceState = state; + mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer.this + ::notifyFoldingFeatureChangeLocked); } }; @@ -115,10 +96,8 @@ public final class DeviceStateManagerFoldingFeatureProducer new DeviceStateMapper(context, deviceStateManager.getSupportedDeviceStates()); if (!mDeviceStateMapper.isDeviceStateToPostureMapEmpty()) { - final Executor executor = - Flags.wlinfoOncreate() ? Runnable::run : context.getMainExecutor(); Objects.requireNonNull(deviceStateManager) - .registerCallback(executor, mDeviceStateCallback); + .registerCallback(context.getMainExecutor(), mDeviceStateCallback); } } @@ -145,21 +124,17 @@ public final class DeviceStateManagerFoldingFeatureProducer @Override protected void onListenersChanged() { super.onListenersChanged(); - synchronized (mCurrentDeviceStateLock) { - if (hasListeners()) { - mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChangeLocked); - } else { - mCurrentDeviceState = INVALID_DEVICE_STATE; - mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChangeLocked); - } + if (hasListeners()) { + mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChangeLocked); + } else { + mCurrentDeviceState = INVALID_DEVICE_STATE; + mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChangeLocked); } } @NonNull private DeviceState getCurrentDeviceState() { - synchronized (mCurrentDeviceStateLock) { - return mCurrentDeviceState; - } + return mCurrentDeviceState; } @NonNull @@ -231,7 +206,6 @@ public final class DeviceStateManagerFoldingFeatureProducer }); } - @GuardedBy("mCurrentDeviceStateLock") private void notifyFoldingFeatureChangeLocked(String displayFeaturesString) { final DeviceState state = mCurrentDeviceState; if (!mDeviceStateMapper.isDeviceStateValid(state)) { @@ -252,29 +226,16 @@ public final class DeviceStateManagerFoldingFeatureProducer return parseListFromString(displayFeaturesString, hingeState); } - /** - * Internal class to map device states to corresponding postures. - * - *

This class encapsulates the logic for mapping device states to postures. The mapping is - * immutable after initialization to ensure thread safety. - */ + /** Internal class to map device states to corresponding postures. */ private static class DeviceStateMapper { /** * Emulated device state * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to * {@link CommonFoldingFeature.State} map. - * - *

This map must be immutable after initialization to ensure thread safety, as it may be - * accessed from multiple threads. Modifications should only occur during object - * construction. */ private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray(); - /** - * The list of device states that are supported. - * - *

This list must be immutable after initialization to ensure thread safety. - */ + /** The list of device states that are supported. */ @NonNull private final List mSupportedStates; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt index 90887a747a6f..fb01cd88158c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducerTest.kt @@ -20,9 +20,6 @@ import android.content.Context import android.content.res.Resources import android.hardware.devicestate.DeviceState import android.hardware.devicestate.DeviceStateManager -import android.platform.test.annotations.DisableFlags -import android.platform.test.annotations.EnableFlags -import android.platform.test.flag.junit.SetFlagsRule import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.window.common.layout.CommonFoldingFeature import androidx.window.common.layout.CommonFoldingFeature.COMMON_STATE_FLAT @@ -34,15 +31,11 @@ import androidx.window.common.layout.DisplayFoldFeatureCommon import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_PROPERTY_SUPPORTS_HALF_OPENED import androidx.window.common.layout.DisplayFoldFeatureCommon.DISPLAY_FOLD_FEATURE_TYPE_SCREEN_FOLD_IN import com.android.internal.R -import com.android.window.flags.Flags import com.google.common.truth.Truth.assertThat import java.util.Optional -import java.util.concurrent.Executor import java.util.function.Consumer -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.doReturn @@ -60,9 +53,6 @@ import org.mockito.kotlin.verify */ @RunWith(AndroidJUnit4::class) class DeviceStateManagerFoldingFeatureProducerTest { - @get:Rule - val setFlagsRule: SetFlagsRule = SetFlagsRule() - private val mMockDeviceStateManager = mock() private val mMockResources = mock { on { getStringArray(R.array.config_device_state_postures) } doReturn DEVICE_STATE_POSTURES @@ -79,8 +69,7 @@ class DeviceStateManagerFoldingFeatureProducerTest { } @Test - @DisableFlags(Flags.FLAG_WLINFO_ONCREATE) - fun testRegisterCallback_whenWlinfoOncreateIsDisabled_usesMainExecutor() { + fun testRegisterCallback_usesMainExecutor() { DeviceStateManagerFoldingFeatureProducer( mMockContext, mRawFoldSupplier, @@ -90,23 +79,6 @@ class DeviceStateManagerFoldingFeatureProducerTest { verify(mMockDeviceStateManager).registerCallback(eq(mMockContext.mainExecutor), any()) } - @Test - @EnableFlags(Flags.FLAG_WLINFO_ONCREATE) - fun testRegisterCallback_whenWlinfoOncreateIsEnabled_usesRunnableRun() { - val executorCaptor = ArgumentCaptor.forClass(Executor::class.java) - val runnable = mock() - - DeviceStateManagerFoldingFeatureProducer( - mMockContext, - mRawFoldSupplier, - mMockDeviceStateManager, - ) - - verify(mMockDeviceStateManager).registerCallback(executorCaptor.capture(), any()) - executorCaptor.value.execute(runnable) - verify(runnable).run() - } - @Test fun testGetCurrentData_validCurrentState_returnsFoldingFeatureWithState() { val ffp = DeviceStateManagerFoldingFeatureProducer( -- GitLab From 293f2dbfc23bfdc50f33ba33134e25789485ab85 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 13 Nov 2024 23:59:39 +0000 Subject: [PATCH 128/656] added Logic to convert UI key event to shortcut key models This is background work for capturing user's selected key combination for a shortcuts Test: CustomShortcutCategoriesRepositoryTest Flag: com.android.systemui.keyboard_shortcut_helper_shortcut_customizer Bug: 373638584 Change-Id: I4c1878187a682cad24b995e83946f1d7aeda1e4a --- .../CustomShortcutCategoriesRepositoryTest.kt | 81 +++++++++++++++++++ .../CustomShortcutCategoriesRepository.kt | 46 +++++++++++ .../repository/ShortcutCategoriesUtils.kt | 28 +++++-- .../data/repository/ShortcutHelperKeys.kt | 3 +- .../ShortcutCustomizationInteractor.kt | 15 +++- .../ShortcutHelperCategoriesInteractor.kt | 2 +- .../shortcut/shared/model/KeyCombination.kt | 19 +++++ .../ui/viewmodel/ShortcutHelperViewModel.kt | 2 +- .../ShortcutCustomizationViewModelTest.kt | 3 +- .../viewmodel/ShortcutHelperViewModelTest.kt | 26 ++++-- .../shortcut/KeyboardShortcutHelperKosmos.kt | 9 ++- 11 files changed, 209 insertions(+), 25 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt index af8341b8e0b9..27b8c59a076d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt @@ -21,6 +21,14 @@ import android.content.Context.INPUT_SERVICE import android.hardware.input.fakeInputManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.view.KeyEvent.KEYCODE_SLASH +import android.view.KeyEvent.META_ALT_ON +import android.view.KeyEvent.META_CAPS_LOCK_ON +import android.view.KeyEvent.META_CTRL_ON +import android.view.KeyEvent.META_FUNCTION_ON +import android.view.KeyEvent.META_META_ON +import android.view.KeyEvent.META_SHIFT_ON +import android.view.KeyEvent.META_SYM_ON import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES @@ -31,8 +39,11 @@ import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination +import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.userTracker import com.android.systemui.testKosmos @@ -114,4 +125,74 @@ class CustomShortcutCategoriesRepositoryTest : SysuiTestCase() { assertThat(categories).isEmpty() } } + + @Test + fun pressedKeys_isEmptyByDefault() { + testScope.runTest { + val pressedKeys by collectLastValue(repo.pressedKeys) + assertThat(pressedKeys).isEmpty() + + helper.toggle(deviceId = 123) + assertThat(pressedKeys).isEmpty() + } + } + + @Test + fun pressedKeys_recognizesAllSupportedModifiers() { + testScope.runTest { + helper.toggle(deviceId = 123) + val pressedKeys by collectLastValue(repo.pressedKeys) + repo.updateUserKeyCombination( + KeyCombination(modifiers = allSupportedModifiers, keyCode = null) + ) + + assertThat(pressedKeys) + .containsExactly( + ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta), + ShortcutKey.Text("Ctrl"), + ShortcutKey.Text("Fn"), + ShortcutKey.Text("Shift"), + ShortcutKey.Text("Alt"), + ShortcutKey.Text("Sym"), + ) + } + } + + @Test + fun pressedKeys_ignoresUnsupportedModifiers() { + testScope.runTest { + helper.toggle(deviceId = 123) + val pressedKeys by collectLastValue(repo.pressedKeys) + repo.updateUserKeyCombination( + KeyCombination(modifiers = META_CAPS_LOCK_ON, keyCode = null) + ) + + assertThat(pressedKeys).isEmpty() + } + } + + @Test + fun pressedKeys_assertCorrectConversion() { + testScope.runTest { + helper.toggle(deviceId = 123) + val pressedKeys by collectLastValue(repo.pressedKeys) + repo.updateUserKeyCombination( + KeyCombination(modifiers = META_META_ON, keyCode = KEYCODE_SLASH) + ) + + assertThat(pressedKeys) + .containsExactly( + ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta), + ShortcutKey.Text("/"), + ) + } + } + + private val allSupportedModifiers = + META_META_ON or + META_CTRL_ON or + META_FUNCTION_ON or + META_SHIFT_ON or + META_ALT_ON or + META_SYM_ON } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt index afe87435e159..da5590ae27fa 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt @@ -23,19 +23,24 @@ import android.hardware.input.InputGestureData.KeyTrigger import android.hardware.input.InputManager import android.hardware.input.InputSettings import android.hardware.input.KeyGestureEvent +import com.android.systemui.Flags.shortcutHelperKeyGlyph import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutInfo +import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active +import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.settings.UserTracker import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -60,6 +65,8 @@ constructor( private val inputManager: InputManager get() = userContext.getSystemService(INPUT_SERVICE) as InputManager + private val _selectedKeyCombination = MutableStateFlow(null) + private val activeInputDevice = stateRepository.state.map { if (it is Active) { @@ -69,6 +76,41 @@ constructor( } } + val pressedKeys = + _selectedKeyCombination + .combine(activeInputDevice) { keyCombination, inputDevice -> + if (inputDevice == null || keyCombination == null) { + return@combine emptyList() + } else { + val keyGlyphMap = + if (shortcutHelperKeyGlyph()) { + inputManager.getKeyGlyphMap(inputDevice.id) + } else null + val modifiers = + shortcutCategoriesUtils.toShortcutModifierKeys( + keyCombination.modifiers, + keyGlyphMap, + ) + val triggerKey = + keyCombination.keyCode?.let { + shortcutCategoriesUtils.toShortcutKey( + keyGlyphMap, + inputDevice.keyCharacterMap, + keyCode = it, + ) + } + val keys = mutableListOf() + modifiers?.let { keys += it } + triggerKey?.let { keys += it } + return@combine keys + } + } + .stateIn( + scope = backgroundScope, + started = SharingStarted.Lazily, + initialValue = emptyList(), + ) + override val categories: Flow> = activeInputDevice .map { inputDevice -> @@ -104,6 +146,10 @@ constructor( started = SharingStarted.Lazily, ) + fun updateUserKeyCombination(keyCombination: KeyCombination?) { + _selectedKeyCombination.value = keyCombination + } + private fun toInternalGroupSources( inputGestures: List ): List { diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt index dd0db88c06d1..3988d1f155bd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCategoriesUtils.kt @@ -24,6 +24,7 @@ import android.util.Log import android.view.InputDevice import android.view.KeyCharacterMap import android.view.KeyEvent +import android.view.KeyEvent.META_META_ON import com.android.systemui.Flags.shortcutHelperKeyGlyph import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shortcut.data.model.InternalKeyboardShortcutGroup @@ -35,9 +36,9 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCommand import com.android.systemui.keyboard.shortcut.shared.model.ShortcutIcon import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory -import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.coroutines.CoroutineContext +import kotlinx.coroutines.withContext class ShortcutCategoriesUtils @Inject @@ -161,7 +162,7 @@ constructor( } if (remainingModifiers != 0) { // There is a remaining modifier we don't support - Log.wtf(TAG, "Unsupported modifiers remaining: $remainingModifiers") + Log.w(TAG, "Unsupported modifiers remaining: $remainingModifiers") return null } if (info.keycode != 0 || info.baseCharacter > Char.MIN_VALUE) { @@ -170,21 +171,32 @@ constructor( ?: return null } if (keys.isEmpty()) { - Log.wtf(TAG, "No keys for $info") + Log.w(TAG, "No keys for $info") return null } return ShortcutCommand(keys = keys, isCustom = info.isCustomShortcut) } + fun toShortcutModifierKeys(modifiers: Int, keyGlyphMap: KeyGlyphMap?): List? { + val keys: MutableList = mutableListOf() + var remainingModifiers = modifiers + SUPPORTED_MODIFIERS.forEach { supportedModifier -> + if ((supportedModifier and remainingModifiers) != 0) { + keys += toShortcutModifierKey(keyGlyphMap, supportedModifier) ?: return null + remainingModifiers = remainingModifiers and supportedModifier.inv() + } + } + return keys + } + private fun toShortcutModifierKey(keyGlyphMap: KeyGlyphMap?, modifierMask: Int): ShortcutKey? { val modifierDrawable = keyGlyphMap?.getDrawableForModifierState(context, modifierMask) if (modifierDrawable != null) { return ShortcutKey.Icon.DrawableIcon(drawable = modifierDrawable) } - val iconResId = ShortcutHelperKeys.keyIcons[modifierMask] - if (iconResId != null) { - return ShortcutKey.Icon.ResIdIcon(iconResId) + if (modifierMask == META_META_ON) { + return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.metaModifierIconResId) } val modifierLabel = ShortcutHelperKeys.modifierLabels[modifierMask] @@ -195,7 +207,7 @@ constructor( return null } - private fun toShortcutKey( + fun toShortcutKey( keyGlyphMap: KeyGlyphMap?, keyCharacterMap: KeyCharacterMap, keyCode: Int, @@ -222,7 +234,7 @@ constructor( if (displayLabelCharacter.code != 0) { return ShortcutKey.Text(displayLabelCharacter.toString()) } - Log.wtf(TAG, "Couldn't find label or icon for key: $keyCode") + Log.w(TAG, "Couldn't find label or icon for key: $keyCode") return null } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt index 288efa275219..e47b33f8c37c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperKeys.kt @@ -116,9 +116,10 @@ import com.android.systemui.res.R object ShortcutHelperKeys { + val metaModifierIconResId = R.drawable.ic_ksh_key_meta + val keyIcons = mapOf( - META_META_ON to R.drawable.ic_ksh_key_meta, KEYCODE_BACK to R.drawable.ic_arrow_back_2, KEYCODE_HOME to R.drawable.ic_radio_button_unchecked, KEYCODE_RECENT_APPS to R.drawable.ic_check_box_outline_blank, diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt index 85d22144f201..aad55dc11c16 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutCustomizationInteractor.kt @@ -16,13 +16,22 @@ package com.android.systemui.keyboard.shortcut.domain.interactor -import android.view.KeyEvent.META_META_ON +import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys +import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import javax.inject.Inject -class ShortcutCustomizationInteractor @Inject constructor() { +class ShortcutCustomizationInteractor +@Inject +constructor(private val customShortcutRepository: CustomShortcutCategoriesRepository) { + val pressedKeys = customShortcutRepository.pressedKeys + + fun updateUserSelectedKeyCombination(keyCombination: KeyCombination?) { + customShortcutRepository.updateUserKeyCombination(keyCombination) + } + fun getDefaultCustomShortcutModifierKey(): ShortcutKey.Icon.ResIdIcon { - return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.keyIcons[META_META_ON]!!) + return ShortcutKey.Icon.ResIdIcon(ShortcutHelperKeys.metaModifierIconResId) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt index 06aadb84f079..0381eaec5026 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCategoriesInteractor.kt @@ -25,10 +25,10 @@ import com.android.systemui.keyboard.shortcut.shared.model.Shortcut import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory import dagger.Lazy +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject @SysUISingleton class ShortcutHelperCategoriesInteractor diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt new file mode 100644 index 000000000000..5e4031b29502 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/shared/model/KeyCombination.kt @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard.shortcut.shared.model + +data class KeyCombination(val modifiers: Int, val keyCode: Int?) diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt index fa3edaf592c2..08fd0af81006 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt @@ -40,6 +40,7 @@ import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState import com.android.systemui.res.R import com.android.systemui.settings.UserTracker +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -50,7 +51,6 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext -import javax.inject.Inject class ShortcutHelperViewModel @Inject diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt index f8d848139039..a9b6dd16cd95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey import com.android.systemui.keyboard.shortcut.shortcutCustomizationViewModelFactory import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.google.common.truth.Truth.assertThat @@ -37,7 +38,7 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ShortcutCustomizationViewModelTest : SysuiTestCase() { - private val kosmos = Kosmos() + private val kosmos = Kosmos().also { it.testCase = this } private val testScope = kosmos.testScope private val viewModel = kosmos.shortcutCustomizationViewModelFactory.create() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt index 14cb9a8ce662..feae901114e5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt @@ -278,7 +278,11 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { testScope.runTest { fakeSystemSource.setGroups( groupWithShortcutLabels("first Foo shortcut1", "first bar shortcut1"), - groupWithShortcutLabels("second foO shortcut2", "second bar shortcut2", groupLabel = SECOND_SIMPLE_GROUP_LABEL), + groupWithShortcutLabels( + "second foO shortcut2", + "second bar shortcut2", + groupLabel = SECOND_SIMPLE_GROUP_LABEL, + ), ) fakeMultiTaskingSource.setGroups( groupWithShortcutLabels("third FoO shortcut1", "third bar shortcut1") @@ -298,7 +302,10 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { ShortcutCategory( System, subCategoryWithShortcutLabels("first Foo shortcut1"), - subCategoryWithShortcutLabels("second foO shortcut2", subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL), + subCategoryWithShortcutLabels( + "second foO shortcut2", + subCategoryLabel = SECOND_SIMPLE_GROUP_LABEL, + ), ), ), ShortcutCategoryUi( @@ -380,14 +387,21 @@ class ShortcutHelperViewModelTest : SysuiTestCase() { assertThat(activeUiState.defaultSelectedCategory).isInstanceOf(CurrentApp::class.java) } - private fun groupWithShortcutLabels(vararg shortcutLabels: String, groupLabel: String = FIRST_SIMPLE_GROUP_LABEL) = - KeyboardShortcutGroup(groupLabel, shortcutLabels.map { simpleShortcutInfo(it) }) - .apply { packageName = "test.package.name" } + private fun groupWithShortcutLabels( + vararg shortcutLabels: String, + groupLabel: String = FIRST_SIMPLE_GROUP_LABEL, + ) = + KeyboardShortcutGroup(groupLabel, shortcutLabels.map { simpleShortcutInfo(it) }).apply { + packageName = "test.package.name" + } private fun simpleShortcutInfo(label: String) = KeyboardShortcutInfo(label, KeyEvent.KEYCODE_A, KeyEvent.META_CTRL_ON) - private fun subCategoryWithShortcutLabels(vararg shortcutLabels: String, subCategoryLabel: String = FIRST_SIMPLE_GROUP_LABEL) = + private fun subCategoryWithShortcutLabels( + vararg shortcutLabels: String, + subCategoryLabel: String = FIRST_SIMPLE_GROUP_LABEL, + ) = ShortcutSubCategory( label = subCategoryLabel, shortcuts = shortcutLabels.map { simpleShortcut(it) }, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 7c5f901eeff9..9cb15c5b816d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -137,9 +137,9 @@ val Kosmos.shortcutHelperStateInteractor by val Kosmos.shortcutHelperCategoriesInteractor by Kosmos.Fixture { - ShortcutHelperCategoriesInteractor( - defaultShortcutCategoriesRepository, - ) { customShortcutCategoriesRepository } + ShortcutHelperCategoriesInteractor(defaultShortcutCategoriesRepository) { + customShortcutCategoriesRepository + } } val Kosmos.shortcutHelperViewModel by @@ -166,7 +166,8 @@ val Kosmos.shortcutCustomizationDialogStarterFactory by } } -val Kosmos.shortcutCustomizationInteractor by Kosmos.Fixture { ShortcutCustomizationInteractor() } +val Kosmos.shortcutCustomizationInteractor by + Kosmos.Fixture { ShortcutCustomizationInteractor(customShortcutCategoriesRepository) } val Kosmos.shortcutCustomizationViewModelFactory by Kosmos.Fixture { -- GitLab From 9702e9e2366a8259163cb57b34f3003d87f00a44 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Fri, 15 Nov 2024 16:33:10 +0000 Subject: [PATCH 129/656] Add "list" and "any_external" options to shade_display_override command Those options allow to list the available displays (and see which one had the shade), and set the shade window on the first non-default display without caring about display ids. + Small refactor to use dagger in ShadePrimaryDisplayCommand and decouple it from ShadeDisplaysRepository To use them "adb shell cmd statusbar shade_display_override list" or "any_external" Bug: 362719719 Bug: 374264564 Test: ShadePrimaryDisplayCommandTest Flag: com.android.systemui.shade_window_goes_around Change-Id: Ib2be5359c2cd77e903bb7db592354d63cee17a09 --- ...t.kt => ShadePrimaryDisplayCommandTest.kt} | 26 ++++- .../systemui/shade/ShadeDisplayAwareModule.kt | 6 +- .../shade/ShadePrimaryDisplayCommand.kt | 107 ++++++++++++++---- .../repository/ShadeDisplaysRepository.kt | 13 +-- 4 files changed, 112 insertions(+), 40 deletions(-) rename packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/{ShadeDisplaysRepositoryTest.kt => ShadePrimaryDisplayCommandTest.kt} (69%) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt similarity index 69% rename from packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt rename to packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt index 4e7839efe2a3..af01547be7e3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadePrimaryDisplayCommandTest.kt @@ -21,7 +21,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.testScope +import com.android.systemui.shade.ShadePrimaryDisplayCommand import com.android.systemui.statusbar.commandline.commandRegistry import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -34,13 +36,16 @@ import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) -class ShadeDisplaysRepositoryTest : SysuiTestCase() { +class ShadePrimaryDisplayCommandTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val commandRegistry = kosmos.commandRegistry + private val displayRepository = kosmos.displayRepository + private val shadeDisplaysRepository = ShadeDisplaysRepositoryImpl() private val pw = PrintWriter(StringWriter()) - private val underTest = ShadeDisplaysRepositoryImpl(commandRegistry) + private val underTest = + ShadePrimaryDisplayCommand(commandRegistry, displayRepository, shadeDisplaysRepository) @Before fun setUp() { @@ -50,7 +55,7 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() { @Test fun commandDisplayOverride_updatesDisplayId() = testScope.runTest { - val displayId by collectLastValue(underTest.displayId) + val displayId by collectLastValue(shadeDisplaysRepository.displayId) assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) val newDisplayId = 2 @@ -65,7 +70,7 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() { @Test fun commandShadeDisplayOverride_resetsDisplayId() = testScope.runTest { - val displayId by collectLastValue(underTest.displayId) + val displayId by collectLastValue(shadeDisplaysRepository.displayId) assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) val newDisplayId = 2 @@ -78,4 +83,17 @@ class ShadeDisplaysRepositoryTest : SysuiTestCase() { commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "reset")) assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) } + + @Test + fun commandShadeDisplayOverride_anyExternalDisplay_notOnDefaultAnymore() = + testScope.runTest { + val displayId by collectLastValue(shadeDisplaysRepository.displayId) + assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY) + val newDisplayId = 2 + displayRepository.addDisplay(displayId = newDisplayId) + + commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "any_external")) + + assertThat(displayId).isEqualTo(newDisplayId) + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt index fed4a26ab1ab..4f73a3456cad 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt @@ -164,10 +164,8 @@ object ShadeDisplayAwareModule { @Provides @IntoMap - @ClassKey(ShadeDisplaysRepositoryImpl::class) - fun provideShadePositionRepositoryAsCoreStartable( - impl: ShadeDisplaysRepositoryImpl - ): CoreStartable { + @ClassKey(ShadePrimaryDisplayCommand::class) + fun provideShadePrimaryDisplayCommand(impl: ShadePrimaryDisplayCommand): CoreStartable { return if (ShadeWindowGoesAround.isEnabled) { impl } else { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt index 506b4e9ab565..a5d9e9660ca5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadePrimaryDisplayCommand.kt @@ -17,40 +17,107 @@ package com.android.systemui.shade import android.view.Display +import com.android.systemui.CoreStartable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.statusbar.commandline.Command +import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter +import javax.inject.Inject -class ShadePrimaryDisplayCommand(private val positionRepository: ShadeDisplaysRepository) : - Command { +@SysUISingleton +class ShadePrimaryDisplayCommand +@Inject +constructor( + private val commandRegistry: CommandRegistry, + private val displaysRepository: DisplayRepository, + private val positionRepository: ShadeDisplaysRepository, +) : Command, CoreStartable { + + override fun start() { + commandRegistry.registerCommand("shade_display_override") { this } + } + + override fun help(pw: PrintWriter) { + pw.println("shade_display_override ") + pw.println("Set the display which is holding the shade.") + pw.println() + pw.println("shade_display_override reset ") + pw.println("Reset the display which is holding the shade.") + pw.println() + pw.println("shade_display_override (list|status) ") + pw.println("Lists available displays and which has the shade") + pw.println() + pw.println("shade_display_override any_external") + pw.println("Moves the shade to the first not-default display available") + } override fun execute(pw: PrintWriter, args: List) { - if (args[0].lowercase() == "reset") { + CommandHandler(pw, args).execute() + } + + /** Wrapper class to avoid propagating [PrintWriter] to all methods. */ + private inner class CommandHandler( + private val pw: PrintWriter, + private val args: List, + ) { + + fun execute() { + when (val command = args.getOrNull(0)?.lowercase()) { + "reset" -> reset() + "list", + "status" -> printStatus() + "any_external" -> anyExternal() + else -> { + val cmdAsInteger = command?.toIntOrNull() + if (cmdAsInteger != null) { + changeDisplay(displayId = cmdAsInteger) + } else { + help(pw) + } + } + } + } + + private fun reset() { positionRepository.resetDisplayId() pw.println("Reset shade primary display id to ${Display.DEFAULT_DISPLAY}") - return } - val displayId: Int = - try { - args[0].toInt() - } catch (e: NumberFormatException) { - pw.println("Error: task id should be an integer") - return + private fun printStatus() { + val displays = displaysRepository.displays.value + val shadeDisplay = positionRepository.displayId.value + pw.println("Available displays: ") + displays.forEach { + pw.print(" - ${it.displayId}") + pw.println(if (it.displayId == shadeDisplay) " (Shade window is here)" else "") } + } - if (displayId < 0) { - pw.println("Error: display id should be positive integer") + private fun anyExternal() { + val anyExternalDisplay = + displaysRepository.displays.value.firstOrNull { + it.displayId != Display.DEFAULT_DISPLAY + } + if (anyExternalDisplay == null) { + pw.println("No external displays available.") + return + } + setDisplay(anyExternalDisplay.displayId) } - positionRepository.setDisplayId(displayId) - pw.println("New shade primary display id is $displayId") - } + private fun changeDisplay(displayId: Int) { + if (displayId < 0) { + pw.println("Error: display id should be positive integer") + } - override fun help(pw: PrintWriter) { - pw.println("shade_display_override ") - pw.println("Set the display which is holding the shade.") - pw.println("shade_display_override reset ") - pw.println("Reset the display which is holding the shade.") + setDisplay(displayId) + } + + private fun setDisplay(id: Int) { + positionRepository.setDisplayId(id) + pw.println("New shade primary display id is $id") + } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt index e920abac8ccc..4a95e339cade 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepository.kt @@ -17,10 +17,7 @@ package com.android.systemui.shade.data.repository import android.view.Display -import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.shade.ShadePrimaryDisplayCommand -import com.android.systemui.statusbar.commandline.CommandRegistry import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -41,9 +38,7 @@ interface ShadeDisplaysRepository { /** Source of truth for the display currently holding the shade. */ @SysUISingleton -class ShadeDisplaysRepositoryImpl -@Inject -constructor(private val commandRegistry: CommandRegistry) : ShadeDisplaysRepository, CoreStartable { +class ShadeDisplaysRepositoryImpl @Inject constructor() : ShadeDisplaysRepository { private val _displayId = MutableStateFlow(Display.DEFAULT_DISPLAY) override val displayId: StateFlow @@ -56,10 +51,4 @@ constructor(private val commandRegistry: CommandRegistry) : ShadeDisplaysReposit override fun resetDisplayId() { _displayId.value = Display.DEFAULT_DISPLAY } - - override fun start() { - commandRegistry.registerCommand("shade_display_override") { - ShadePrimaryDisplayCommand(this) - } - } } -- GitLab From 4de1ed48ec5aa5f8044b3a70850291cb4f99f99f Mon Sep 17 00:00:00 2001 From: Thomas Nguyen Date: Thu, 31 Oct 2024 18:59:38 +0000 Subject: [PATCH 130/656] Add KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE to carrierconfig Change-Id: Ib59c66a3143b5438b53cf8e68d769872ac79f41e Flag: com.android.internal.telephony.flags.carrier_roaming_nb_iot_ntn Bug: 373872272 Test: SatelliteManagerTestOnMockService SatelliteAccessControllerTest --- .../telephony/CarrierConfigManager.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 6490cbe3e31a..7cfdec664a92 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9733,6 +9733,35 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle"; + /** + * A PersistableBundle that contains a list of key-value pairs, where the values are integer + * arrays. + *

+ * Keys are the IDs of regional satellite configs as strings and values are + * integer arrays of earfcns in the corresponding regions. + * + * An example config for two regions "1" and "2": + *

{@code
+     * 
+     *   
+     *     
+     *       
+     *       
+     *     
+     *     
+     *       
+     *     
+     *   
+     * 
+     * }
+ *

+ * This config is empty by default. + * @hide + */ + @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) + public static final String KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE = + "regional_satellite_earfcn_bundle"; + /** * This config enables modem to scan satellite PLMNs specified as per * {@link #KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE} and attach to same @@ -11264,6 +11293,9 @@ public class CarrierConfigManager { sDefaults.putPersistableBundle( KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, PersistableBundle.EMPTY); + sDefaults.putPersistableBundle( + KEY_REGIONAL_SATELLITE_EARFCN_BUNDLE, + PersistableBundle.EMPTY); sDefaults.putBoolean(KEY_SATELLITE_ATTACH_SUPPORTED_BOOL, false); sDefaults.putInt(KEY_SATELLITE_CONNECTION_HYSTERESIS_SEC_INT, 180); sDefaults.putIntArray(KEY_NTN_LTE_RSRP_THRESHOLDS_INT_ARRAY, -- GitLab From 24c8ba68bdfc5a728db06e3a778b2b4f43bca4e7 Mon Sep 17 00:00:00 2001 From: Arpit Singh Date: Fri, 25 Oct 2024 21:08:08 +0000 Subject: [PATCH 131/656] Check if cursor has moved out of viewport bounds in CursorController This CL updates CursorController to check if and which boundary the cursor has crossed. This will be used to enable cursor moving between different connected displays. Test: presubmit and manual Bug: 367660694 Flag: com.android.input.flags.connected_displays_cursor Change-Id: Ib559e0d72364f446269e73cfa1671ee9d2f715ac --- libs/input/MouseCursorController.cpp | 32 +++-- libs/input/MouseCursorController.h | 7 +- libs/input/PointerController.cpp | 13 +- libs/input/PointerController.h | 4 +- libs/input/tests/PointerController_test.cpp | 131 ++++++++++++++++++++ 5 files changed, 165 insertions(+), 22 deletions(-) diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index d993b8715260..4330f0addaa9 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -28,12 +28,14 @@ #define INDENT " " #define INDENT2 " " +namespace android { + namespace { + // Time to spend fading out the pointer completely. const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms -} // namespace -namespace android { +} // namespace // --- MouseCursorController --- @@ -64,17 +66,21 @@ MouseCursorController::~MouseCursorController() { mLocked.pointerSprite.clear(); } -void MouseCursorController::move(float deltaX, float deltaY) { +vec2 MouseCursorController::move(float deltaX, float deltaY) { #if DEBUG_MOUSE_CURSOR_UPDATES ALOGD("Move pointer by deltaX=%0.3f, deltaY=%0.3f", deltaX, deltaY); #endif if (deltaX == 0.0f && deltaY == 0.0f) { - return; + return {}; } + // When transition occurs, the MouseCursorController object may or may not be deleted, depending + // if there's another display on the other side of the transition. At this point we still need + // to move the cursor to the boundary. std::scoped_lock lock(mLock); - - setPositionLocked(mLocked.pointerX + deltaX, mLocked.pointerY + deltaY); + const vec2 pos{mLocked.pointerX + deltaX, mLocked.pointerY + deltaY}; + setPositionLocked(pos.x, pos.y); + return vec2{pos.x - mLocked.pointerX, pos.y - mLocked.pointerY}; } void MouseCursorController::setPosition(float x, float y) { @@ -82,22 +88,26 @@ void MouseCursorController::setPosition(float x, float y) { ALOGD("Set pointer position to x=%0.3f, y=%0.3f", x, y); #endif std::scoped_lock lock(mLock); + setPositionLocked(x, y); } -void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { - const auto& v = mLocked.viewport; - if (!v.isValid()) return; - +FloatRect MouseCursorController::getBoundsLocked() REQUIRES(mLock) { // The valid bounds for a mouse cursor. Since the right and bottom edges are considered outside // the display, clip the bounds by one pixel instead of letting the cursor get arbitrarily // close to the outside edge. - const FloatRect bounds{ + return FloatRect{ static_cast(mLocked.viewport.logicalLeft), static_cast(mLocked.viewport.logicalTop), static_cast(mLocked.viewport.logicalRight - 1), static_cast(mLocked.viewport.logicalBottom - 1), }; +} +void MouseCursorController::setPositionLocked(float x, float y) REQUIRES(mLock) { + const auto& v = mLocked.viewport; + if (!v.isValid()) return; + + const FloatRect bounds = getBoundsLocked(); mLocked.pointerX = std::max(bounds.left, std::min(bounds.right, x)); mLocked.pointerY = std::max(bounds.top, std::min(bounds.bottom, y)); diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index 12b31a8c531a..10f0d7645105 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -20,9 +20,6 @@ #include #include #include -#include -#include -#include #include #include @@ -43,7 +40,8 @@ public: MouseCursorController(PointerControllerContext& context); ~MouseCursorController(); - void move(float deltaX, float deltaY); + // Move the pointer and return unconsumed delta + vec2 move(float deltaX, float deltaY); void setPosition(float x, float y); FloatPoint getPosition() const; ui::LogicalDisplayId getDisplayId() const; @@ -113,6 +111,7 @@ private: bool doFadingAnimationLocked(nsecs_t timestamp); void startAnimationLocked(); + FloatRect getBoundsLocked(); }; } // namespace android diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 78d7d3a7051b..59397dab592f 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -138,15 +138,18 @@ std::mutex& PointerController::getLock() const { return mDisplayInfoListener->mLock; } -void PointerController::move(float deltaX, float deltaY) { +vec2 PointerController::move(float deltaX, float deltaY) { const ui::LogicalDisplayId displayId = mCursorController.getDisplayId(); - vec2 transformed; + ui::Transform transform; { std::scoped_lock lock(getLock()); - const auto& transform = getTransformForDisplayLocked(displayId); - transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); + transform = getTransformForDisplayLocked(displayId); } - mCursorController.move(transformed.x, transformed.y); + + vec2 transformed = transformWithoutTranslation(transform, {deltaX, deltaY}); + + vec2 unconsumedDelta = mCursorController.move(transformed.x, transformed.y); + return transformWithoutTranslation(transform.inverse(), unconsumedDelta); } void PointerController::setPosition(float x, float y) { diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index ee8d1211341f..69bef546b788 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -51,7 +51,7 @@ public: ~PointerController() override; - void move(float deltaX, float deltaY) override; + vec2 move(float deltaX, float deltaY) override; void setPosition(float x, float y) override; FloatPoint getPosition() const override; ui::LogicalDisplayId getDisplayId() const override; @@ -165,7 +165,7 @@ public: ~TouchPointerController() override; - void move(float, float) override { + vec2 move(float, float) override { LOG_ALWAYS_FATAL("Should not be called"); } void setPosition(float, float) override { diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 5b00fca4d857..80c934a9bd95 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -40,6 +40,8 @@ enum TestCursorType { CURSOR_TYPE_CUSTOM = -1, }; +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; + using ::testing::AllOf; using ::testing::Field; using ::testing::NiceMock; @@ -399,6 +401,135 @@ INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest, testing::Values(PointerControllerInterface::ControllerType::MOUSE, PointerControllerInterface::ControllerType::STYLUS)); +class MousePointerControllerTest : public PointerControllerTest { +protected: + MousePointerControllerTest() { + sp testPointerSprite(new NiceMock); + EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite)); + + // create a mouse pointer controller + mPointerController = + PointerController::create(mPolicy, mLooper, *mSpriteController, + PointerControllerInterface::ControllerType::MOUSE); + + // set display viewport + DisplayViewport viewport; + viewport.displayId = ui::LogicalDisplayId::DEFAULT; + viewport.logicalRight = 5; + viewport.logicalBottom = 5; + viewport.physicalRight = 5; + viewport.physicalBottom = 5; + viewport.deviceWidth = 5; + viewport.deviceHeight = 5; + mPointerController->setDisplayViewport(viewport); + } +}; + +struct MousePointerControllerTestParam { + vec2 startPosition; + vec2 delta; + vec2 endPosition; + vec2 unconsumedDelta; +}; + +class PointerControllerViewportTransitionTest + : public MousePointerControllerTest, + public testing::WithParamInterface {}; + +TEST_P(PointerControllerViewportTransitionTest, testPointerViewportTransition) { + const auto& params = GetParam(); + + mPointerController->setPosition(params.startPosition.x, params.startPosition.y); + auto unconsumedDelta = mPointerController->move(params.delta.x, params.delta.y); + + auto position = mPointerController->getPosition(); + EXPECT_NEAR(position.x, params.endPosition.x, EPSILON); + EXPECT_NEAR(position.y, params.endPosition.y, EPSILON); + EXPECT_NEAR(unconsumedDelta.x, params.unconsumedDelta.x, EPSILON); + EXPECT_NEAR(unconsumedDelta.y, params.unconsumedDelta.y, EPSILON); +} + +INSTANTIATE_TEST_SUITE_P(PointerControllerViewportTransitionTest, + PointerControllerViewportTransitionTest, + testing::Values( + // no transition + MousePointerControllerTestParam{{2.0f, 2.0f}, + {2.0f, 2.0f}, + {4.0f, 4.0f}, + {0.0f, 0.0f}}, + // right boundary + MousePointerControllerTestParam{{2.0f, 2.0f}, + {3.0f, 0.0f}, + {4.0f, 2.0f}, + {1.0f, 0.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {3.0f, -1.0f}, + {4.0f, 1.0f}, + {1.0f, 0.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {3.0f, 1.0f}, + {4.0f, 3.0f}, + {1.0f, 0.0f}}, + // left boundary + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-3.0f, 0.0f}, + {0.0f, 2.0f}, + {-1.0f, 0.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-3.0f, -1.0f}, + {0.0f, 1.0f}, + {-1.0f, 0.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-3.0f, 1.0f}, + {0.0f, 3.0f}, + {-1.0f, 0.0f}}, + // bottom boundary + MousePointerControllerTestParam{{2.0f, 2.0f}, + {0.0f, 3.0f}, + {2.0f, 4.0f}, + {0.0f, 1.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-1.0f, 3.0f}, + {1.0f, 4.0f}, + {0.0f, 1.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {1.0f, 3.0f}, + {3.0f, 4.0f}, + {0.0f, 1.0f}}, + // top boundary + MousePointerControllerTestParam{{2.0f, 2.0f}, + {0.0f, -3.0f}, + {2.0f, 0.0f}, + {0.0f, -1.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-1.0f, -3.0f}, + {1.0f, 0.0f}, + {0.0f, -1.0f}}, + MousePointerControllerTestParam{{2.0f, 2.0f}, + {1.0f, -3.0f}, + {3.0f, 0.0f}, + {0.0f, -1.0f}}, + // top-left corner + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-3.0f, -3.0f}, + {0.0f, 0.0f}, + {-1.0f, -1.0f}}, + // top-right corner + MousePointerControllerTestParam{{2.0f, 2.0f}, + {3.0f, -3.0f}, + {4.0f, 0.0f}, + {1.0f, -1.0f}}, + // bottom-right corner + MousePointerControllerTestParam{{2.0f, 2.0f}, + {3.0f, 3.0f}, + {4.0f, 4.0f}, + {1.0f, 1.0f}}, + // bottom-left corner + MousePointerControllerTestParam{{2.0f, 2.0f}, + {-3.0f, 3.0f}, + {0.0f, 4.0f}, + {-1.0f, 1.0f}})); + class PointerControllerWindowInfoListenerTest : public Test {}; TEST_F(PointerControllerWindowInfoListenerTest, -- GitLab From 5f837195fcf63895df7e6933705a78187dd9638b Mon Sep 17 00:00:00 2001 From: Vaibhav Devmurari Date: Fri, 15 Nov 2024 13:47:21 +0000 Subject: [PATCH 132/656] Add TestApi to reset locked modifier state To make CTS tests more reliable, we need a way to clear locked modifier state that can creep in from other tests since locked modifier state is global and saved across devices. Test: atest CtsInputTestCases Bug: 377353219 Flag: EXEMPT test_only Change-Id: I8ae29652067966019d18e5f104925b4fbae91408 --- core/api/test-current.txt | 1 + .../android/hardware/input/IInputManager.aidl | 2 ++ .../java/android/hardware/input/InputManager.java | 15 +++++++++++++++ .../android/server/input/InputManagerService.java | 5 +++++ .../server/input/NativeInputManagerService.java | 5 +++++ ...m_android_server_input_InputManagerService.cpp | 7 +++++++ 6 files changed, 35 insertions(+) diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 44bcc2a737b9..ad1d937a3d10 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1805,6 +1805,7 @@ package android.hardware.input { method @RequiresPermission(android.Manifest.permission.REMAP_MODIFIER_KEYS) public void remapModifierKey(int, int); method @FlaggedApi("com.android.input.flags.device_associations") @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByDescriptor(@NonNull String); method @RequiresPermission("android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY") public void removeUniqueIdAssociationByPort(@NonNull String); + method public void resetLockedModifierState(); field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL } diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 3284761eb273..ed510e467f82 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -281,4 +281,6 @@ interface IInputManager { AidlInputGestureData[] getCustomInputGestures(int userId, int tag); AidlInputGestureData[] getAppLaunchBookmarks(); + + void resetLockedModifierState(); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index f8241925dff0..3f7b4ab59a7e 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1589,6 +1589,21 @@ public final class InputManager { } } + /** + * Resets locked modifier state (i.e.. Caps Lock, Num Lock, Scroll Lock state) + * + * @hide + */ + @TestApi + @SuppressLint("UnflaggedApi") // @TestApi without associated feature. + public void resetLockedModifierState() { + try { + mIm.resetLockedModifierState(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * A callback used to be notified about battery state changes for an input device. The * {@link #onBatteryStateChanged(int, long, BatteryState)} method will be called once after the diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index edad2473061c..e5b077d23bec 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3031,6 +3031,11 @@ public class InputManagerService extends IInputManager.Stub return mKeyGestureController.getAppLaunchBookmarks(); } + @Override + public void resetLockedModifierState() { + mNative.resetLockedModifierState(); + } + private void handleCurrentUserChanged(@UserIdInt int userId) { mCurrentUserId = userId; mKeyGestureController.setCurrentUserId(userId); diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java index 8903c27b491a..728e44062e82 100644 --- a/services/core/java/com/android/server/input/NativeInputManagerService.java +++ b/services/core/java/com/android/server/input/NativeInputManagerService.java @@ -98,6 +98,8 @@ interface NativeInputManagerService { void toggleCapsLock(int deviceId); + void resetLockedModifierState(); + void displayRemoved(int displayId); void setInputDispatchMode(boolean enabled, boolean frozen); @@ -369,6 +371,9 @@ interface NativeInputManagerService { @Override public native void toggleCapsLock(int deviceId); + @Override + public native void resetLockedModifierState(); + @Override public native void displayRemoved(int displayId); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index e38337540ad9..dece612c9424 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -2330,6 +2330,12 @@ static void nativeToggleCapsLock(JNIEnv* env, jobject nativeImplObj, jint device im->getInputManager()->getReader().toggleCapsLockState(deviceId); } +static void resetLockedModifierState(JNIEnv* env, jobject nativeImplObj) { + NativeInputManager* im = getNativeInputManager(env, nativeImplObj); + + im->getInputManager()->getReader().resetLockedModifierState(); +} + static void nativeDisplayRemoved(JNIEnv* env, jobject nativeImplObj, jint displayId) { NativeInputManager* im = getNativeInputManager(env, nativeImplObj); @@ -3134,6 +3140,7 @@ static const JNINativeMethod gInputManagerMethods[] = { {"verifyInputEvent", "(Landroid/view/InputEvent;)Landroid/view/VerifiedInputEvent;", (void*)nativeVerifyInputEvent}, {"toggleCapsLock", "(I)V", (void*)nativeToggleCapsLock}, + {"resetLockedModifierState", "()V", (void*)resetLockedModifierState}, {"displayRemoved", "(I)V", (void*)nativeDisplayRemoved}, {"setFocusedApplication", "(ILandroid/view/InputApplicationHandle;)V", (void*)nativeSetFocusedApplication}, -- GitLab From dbd2105b81247b21c2cc37916f3c73bed9ef11c1 Mon Sep 17 00:00:00 2001 From: Gustav Sennton Date: Mon, 18 Nov 2024 11:21:00 +0000 Subject: [PATCH 133/656] Mark Desktop Windowing transitions flags as BUGFIX Bug: 353650277, 353650462, 353651428 Test: NA Flag: EXEMPT flag changes Change-Id: I07e70e4f249b5174e15a8e5b5d902c4dc6644458 --- .../window/flags/lse_desktop_experience.aconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index eebdeadcdeb2..c7d5a9fe6041 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -253,6 +253,9 @@ flag { namespace: "lse_desktop_experience" description: "Enables enter desktop windowing transition & motion polish changes" bug: "369763947" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { @@ -260,6 +263,9 @@ flag { namespace: "lse_desktop_experience" description: "Enables exit desktop windowing transition & motion polish changes" bug: "353650462" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { @@ -343,6 +349,9 @@ flag { namespace: "lse_desktop_experience" description: "Enables custom transitions for alt-tab app launches in Desktop Mode." bug: "370735595" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { @@ -350,6 +359,9 @@ flag { namespace: "lse_desktop_experience" description: "Enables custom transitions for app launches in Desktop Mode." bug: "375992828" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { -- GitLab From 66d31a490b2991f497ab94a3e44e6030481ff920 Mon Sep 17 00:00:00 2001 From: Ioana Alexandru Date: Thu, 14 Nov 2024 16:15:49 +0100 Subject: [PATCH 134/656] [Notif redesign] Bigger "small" icon in HUN Note that notification_2025_template_heads_up is a fork of notification_template_material_heads_up. Bug: 378660052 Test: visual test, screenshot tests to come later Flag: android.app.notifications_redesign_templates Change-Id: I32d3a355900cb10470600b3c467caa28e87d30da --- core/java/android/app/Notification.java | 8 ++- ...tification_2025_template_heads_up_base.xml | 66 +++++++++++++++++++ core/res/res/values/symbols.xml | 1 + 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 core/res/res/layout/notification_2025_template_heads_up_base.xml diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 940039714cb9..dec21c40b6e9 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -814,8 +814,8 @@ public class Notification implements Parcelable if (Flags.notificationsRedesignTemplates()) { return switch (layoutId) { case R.layout.notification_2025_template_collapsed_base, + R.layout.notification_2025_template_heads_up_base, R.layout.notification_2025_template_header, - R.layout.notification_template_material_heads_up_base, R.layout.notification_template_material_big_base, R.layout.notification_template_material_big_picture, R.layout.notification_template_material_big_text, @@ -7517,7 +7517,11 @@ public class Notification implements Parcelable } private int getHeadsUpBaseLayoutResource() { - return R.layout.notification_template_material_heads_up_base; + if (Flags.notificationsRedesignTemplates()) { + return R.layout.notification_2025_template_heads_up_base; + } else { + return R.layout.notification_template_material_heads_up_base; + } } private int getCompactHeadsUpBaseLayoutResource() { diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml new file mode 100644 index 000000000000..e4ff835a3524 --- /dev/null +++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0eb1501d38e0..dc3b2d9c0029 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2390,6 +2390,7 @@ + -- GitLab From db88a6329093a0fba027dcef5099f8bdd5ea3cbb Mon Sep 17 00:00:00 2001 From: Orhan Uysal Date: Mon, 18 Nov 2024 11:46:50 +0000 Subject: [PATCH 135/656] Set DesktopWallpaper window as translucent. This prevents DesktopWallpaper from showing splashscreen. Bug: 378868982 Test: Manually enter desktop using the button on app handle Flag: EXEMPT Bugfix Change-Id: I3f23d1e388a88abacf4c4c96af0d4138104e0d55 --- libs/WindowManager/Shell/res/values/styles.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 55cda783005f..597a921302b7 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -29,6 +29,7 @@ @android:color/transparent true @null + true - diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java index 3c790f0e24ee..bc06bdbd29af 100644 --- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java +++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java @@ -31,10 +31,17 @@ import android.text.Editable; import android.text.TextWatcher; import android.util.Slog; import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + /** * Confirm with the user that a requested full backup/restore operation is legitimate. * Any attempt to perform a full backup/restore will launch this UI and wait for a @@ -208,6 +215,8 @@ public class BackupRestoreConfirmation extends Activity { setTitle(titleId); setContentView(layoutId); + handleInsets(); + // Same resource IDs for each layout variant (backup / restore) mStatusView = findViewById(R.id.package_name); mAllowButton = findViewById(R.id.button_allow); @@ -254,6 +263,31 @@ public class BackupRestoreConfirmation extends Activity { } } + // Handle insets so that UI components are not covered by navigation and status bars + private void handleInsets() { + LinearLayout buttonBar = findViewById(R.id.button_bar); + ViewCompat.setOnApplyWindowInsetsListener(buttonBar, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + mlp.leftMargin = insets.left; + mlp.bottomMargin = insets.bottom; + mlp.rightMargin = insets.right; + v.setLayoutParams(mlp); + return WindowInsetsCompat.CONSUMED; + }); + + ScrollView scrollView = findViewById(R.id.scroll_view); + ViewCompat.setOnApplyWindowInsetsListener(scrollView, (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams(); + mlp.leftMargin = insets.left; + mlp.topMargin = insets.top; + mlp.rightMargin = insets.right; + v.setLayoutParams(mlp); + return WindowInsetsCompat.CONSUMED; + }); + } + private void monitorEncryptionPassword() { mAllowButton.setEnabled(false); mEncPassword.addTextChangedListener(new TextWatcher() { -- GitLab From ad59579ad8a451c9e8e4c1ac2b19e0e86397704f Mon Sep 17 00:00:00 2001 From: Florence Yang Date: Mon, 18 Nov 2024 19:52:59 +0000 Subject: [PATCH 189/656] Add flags for GSF on Bouncer & QS These flags are for the first round of implementation of GSF styles on Bouncer and Quick Settings. These flags will be removed once we gather the trunkfood-50 metrics. Flag: EXEMPT adding new flags Bug: 379364381 Test: N/A Change-Id: Ib8459d61ce002e198aa37383d777348dfacd4051 --- packages/SystemUI/aconfig/systemui.aconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 67666c3db81f..c82c63c7c78b 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1798,3 +1798,17 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "gsf_bouncer" + namespace: "systemui" + description: "Applies GSF font styles to Bouncer surfaces." + bug: "379364381" +} + +flag { + name: "gsf_quick_settings" + namespace: "systemui" + description: "Applies GSF font styles to Quick Settings surfaces." + bug: "379364381" +} -- GitLab From 721e5fcc3f0d2ce8025bd545ade78ad14e0da670 Mon Sep 17 00:00:00 2001 From: Jeff DeCew Date: Sun, 17 Nov 2024 20:08:03 +0000 Subject: [PATCH 190/656] [RONs] Fix over-restrictive requirement in promotion logic. We were checking that a notification met existing colorized requirements before making it promoted, which was too restrictive. It needs to just check that the app requested to be colorized. Change-Id: I6202e0c10f7481ef88656d154b7b3a57aac2ebd7 Flag: android.app.ui_rich_ongoing Bug: 379335139 Test: atest NotificationTest Test: atest NotificationManagerServiceTest --- core/java/android/app/Notification.java | 33 +++++++++++++++++-- .../NotificationManagerServiceTest.java | 14 -------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index cfe0ff9440e7..16937c0bd516 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3245,7 +3245,7 @@ public class Notification implements Parcelable */ @FlaggedApi(Flags.FLAG_UI_RICH_ONGOING) public boolean hasPromotableCharacteristics() { - return isColorized() + return isColorizedRequested() && hasTitle() && !containsCustomViews() && hasPromotableStyle(); @@ -4071,6 +4071,12 @@ public class Notification implements Parcelable flags &= ~FLAG_LIFETIME_EXTENDED_BY_DIRECT_REPLY; } } + if (Flags.apiRichOngoing()) { + if ((flags & FLAG_PROMOTED_ONGOING) != 0) { + flagStrings.add("PROMOTED_ONGOING"); + flags &= ~FLAG_PROMOTED_ONGOING; + } + } if (android.service.notification.Flags.notificationSilentFlag()) { if ((flags & FLAG_SILENT) != 0) { @@ -7776,8 +7782,16 @@ public class Notification implements Parcelable * @hide */ public boolean isColorized() { - return extras.getBoolean(EXTRA_COLORIZED) - && (hasColorizedPermission() || isFgsOrUij()); + return isColorizedRequested() + && (hasColorizedPermission() || isFgsOrUij() || isPromotedOngoing()); + } + + /** + * @return true if this notification has requested to be colorized, regardless of whether it + * meets the requirements to be displayed that way. + */ + private boolean isColorizedRequested() { + return extras.getBoolean(EXTRA_COLORIZED); } /** @@ -7790,6 +7804,19 @@ public class Notification implements Parcelable return (flags & Notification.FLAG_CAN_COLORIZE) != 0; } + /** + * Returns whether this notification is a promoted ongoing notification. + * + * This requires the Notification.FLAG_PROMOTED_ONGOING flag to be set + * (which may be true once the api_rich_ongoing feature flag is enabled), + * and requires that the ui_rich_ongoing feature flag is enabled. + * + * @hide + */ + public boolean isPromotedOngoing() { + return Flags.uiRichOngoing() && (flags & Notification.FLAG_PROMOTED_ONGOING) != 0; + } + /** * @return true if this is a media style notification with a media session * diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 704c1b858b8d..e6b4bc98ea4c 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -17178,8 +17178,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testSetCanBePromoted_granted() throws Exception { - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); // qualifying posted notification Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) @@ -17254,8 +17252,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testSetCanBePromoted_granted_onlyNotifiesOnce() throws Exception { - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); // qualifying posted notification Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) @@ -17285,8 +17281,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testSetCanBePromoted_revoked() throws Exception { - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); // start from true state mBinderService.setCanBePromoted(mPkg, mUid, true, true); @@ -17350,8 +17344,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testSetCanBePromoted_revoked_onlyNotifiesOnce() throws Exception { - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); // start from true state mBinderService.setCanBePromoted(mPkg, mUid, true, true); @@ -17387,8 +17379,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testPostPromotableNotification() throws Exception { mBinderService.setCanBePromoted(mPkg, mUid, true, true); assertThat(mBinderService.appCanBePromoted(mPkg, mUid)).isTrue(); - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) @@ -17415,8 +17405,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testPostPromotableNotification_noPermission() throws Exception { - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG")) @@ -17444,8 +17432,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @EnableFlags(android.app.Flags.FLAG_API_RICH_ONGOING) public void testPostPromotableNotification_unimportantNotification() throws Exception { mBinderService.setCanBePromoted(mPkg, mUid, true, true); - mContext.getTestablePermissions().setPermission( - android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED); Notification n = new Notification.Builder(mContext, mMinChannel.getId()) .setSmallIcon(android.R.drawable.sym_def_app_icon) .setStyle(new Notification.BigTextStyle().setBigContentTitle("BIG")) -- GitLab From ca826fe6d9823c12c1ef5de5778f40ce0c16eda7 Mon Sep 17 00:00:00 2001 From: Wenhao Wang Date: Sun, 17 Nov 2024 14:00:00 -0800 Subject: [PATCH 191/656] [ID] Replace "forensic" with "intrusion detection" Improve terminology: Use "intrusion detection" instead of "forensic" for accuracy Bug: 365994454 Test: atest IntrusionDetectionServiceTest Test: atest IntrusionDetectionManagerTest Flag: android.security.afl_api Ignore-AOSP-First: security feature Change-Id: Ib60db2d91ef8b0a40605fc43fec7df32bb15c806 --- core/api/system-current.txt | 20 +- .../android/app/SystemServiceRegistry.java | 21 ++- core/java/android/content/Context.java | 6 +- .../security/forensic/IForensicService.aidl | 35 ---- .../IIntrusionDetectionEventTransport.aidl} | 14 +- .../IIntrusionDetectionService.aidl | 35 ++++ ...usionDetectionServiceCommandCallback.aidl} | 4 +- ...trusionDetectionServiceStateCallback.aidl} | 4 +- .../IntrusionDetectionEvent.aidl} | 4 +- .../IntrusionDetectionEvent.java} | 36 ++-- .../IntrusionDetectionManager.java} | 100 +++++----- .../{forensic => intrusiondetection}/OWNERS | 0 core/res/AndroidManifest.xml | 18 +- core/res/res/values/config.xml | 4 +- core/res/res/values/symbols.xml | 4 +- data/etc/privapp-permissions-platform.xml | 6 +- packages/Shell/AndroidManifest.xml | 6 +- .../android/server/security/forensic/OWNERS | 1 - .../DataAggregator.java | 30 +-- .../DataSource.java | 2 +- ...ionDetectionEventTransportConnection.java} | 42 ++--- .../IntrusionDetectionService.java} | 135 ++++++++------ .../server/security/intrusiondetection/OWNERS | 1 + .../SecurityLogSource.java | 12 +- .../java/com/android/server/SystemServer.java | 6 +- services/tests/security/forensic/OWNERS | 3 - services/tests/security/forensic/TEST_MAPPING | 7 - .../Android.bp | 2 +- .../AndroidManifest.xml | 6 +- .../AndroidTest.xml | 8 +- .../tests/security/intrusiondetection/OWNERS | 3 + .../security/intrusiondetection/TEST_MAPPING | 7 + .../IntrusionDetectionServiceTest.java} | 176 +++++++++--------- 33 files changed, 394 insertions(+), 364 deletions(-) delete mode 100644 core/java/android/security/forensic/IForensicService.aidl rename core/java/android/security/{forensic/IForensicEventTransport.aidl => intrusiondetection/IIntrusionDetectionEventTransport.aidl} (67%) create mode 100644 core/java/android/security/intrusiondetection/IIntrusionDetectionService.aidl rename core/java/android/security/{forensic/IForensicServiceCommandCallback.aidl => intrusiondetection/IIntrusionDetectionServiceCommandCallback.aidl} (89%) rename core/java/android/security/{forensic/IForensicServiceStateCallback.aidl => intrusiondetection/IIntrusionDetectionServiceStateCallback.aidl} (88%) rename core/java/android/security/{forensic/ForensicEvent.aidl => intrusiondetection/IntrusionDetectionEvent.aidl} (88%) rename core/java/android/security/{forensic/ForensicEvent.java => intrusiondetection/IntrusionDetectionEvent.java} (80%) rename core/java/android/security/{forensic/ForensicManager.java => intrusiondetection/IntrusionDetectionManager.java} (62%) rename core/java/android/security/{forensic => intrusiondetection}/OWNERS (100%) delete mode 100644 services/core/java/com/android/server/security/forensic/OWNERS rename services/core/java/com/android/server/security/{forensic => intrusiondetection}/DataAggregator.java (78%) rename services/core/java/com/android/server/security/{forensic => intrusiondetection}/DataSource.java (93%) rename services/core/java/com/android/server/security/{forensic/ForensicEventTransportConnection.java => intrusiondetection/IntrusionDetectionEventTransportConnection.java} (74%) rename services/core/java/com/android/server/security/{forensic/ForensicService.java => intrusiondetection/IntrusionDetectionService.java} (59%) create mode 100644 services/core/java/com/android/server/security/intrusiondetection/OWNERS rename services/core/java/com/android/server/security/{forensic => intrusiondetection}/SecurityLogSource.java (87%) delete mode 100644 services/tests/security/forensic/OWNERS delete mode 100644 services/tests/security/forensic/TEST_MAPPING rename services/tests/security/{forensic => intrusiondetection}/Android.bp (95%) rename services/tests/security/{forensic => intrusiondetection}/AndroidManifest.xml (83%) rename services/tests/security/{forensic => intrusiondetection}/AndroidTest.xml (83%) create mode 100644 services/tests/security/intrusiondetection/OWNERS create mode 100644 services/tests/security/intrusiondetection/TEST_MAPPING rename services/tests/security/{forensic/src/com/android/server/security/forensic/ForensicServiceTest.java => intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java} (59%) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 14a6c8c4928d..f6f0ffb05eb1 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -66,10 +66,10 @@ package android { field @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") public static final String BIND_EXPLICIT_HEALTH_CHECK_SERVICE = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"; field public static final String BIND_EXTERNAL_STORAGE_SERVICE = "android.permission.BIND_EXTERNAL_STORAGE_SERVICE"; field public static final String BIND_FIELD_CLASSIFICATION_SERVICE = "android.permission.BIND_FIELD_CLASSIFICATION_SERVICE"; - field @FlaggedApi("android.security.afl_api") public static final String BIND_FORENSIC_EVENT_TRANSPORT_SERVICE = "android.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE"; field public static final String BIND_GBA_SERVICE = "android.permission.BIND_GBA_SERVICE"; field public static final String BIND_HOTWORD_DETECTION_SERVICE = "android.permission.BIND_HOTWORD_DETECTION_SERVICE"; field public static final String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE"; + field @FlaggedApi("android.security.afl_api") public static final String BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE = "android.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE"; field public static final String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE"; field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE"; @@ -213,12 +213,12 @@ package android { field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES"; field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS"; field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; - field @FlaggedApi("android.security.afl_api") public static final String MANAGE_FORENSIC_STATE = "android.permission.MANAGE_FORENSIC_STATE"; field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY"; field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE"; field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE"; field @FlaggedApi("android.media.tv.flags.media_quality_fw") public static final String MANAGE_GLOBAL_SOUND_QUALITY_SERVICE = "android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE"; field public static final String MANAGE_HOTWORD_DETECTION = "android.permission.MANAGE_HOTWORD_DETECTION"; + field @FlaggedApi("android.security.afl_api") public static final String MANAGE_INTRUSION_DETECTION_STATE = "android.permission.MANAGE_INTRUSION_DETECTION_STATE"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final String MANAGE_LOW_POWER_STANDBY = "android.permission.MANAGE_LOW_POWER_STANDBY"; field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION"; @@ -310,10 +310,10 @@ package android { field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; - field @FlaggedApi("android.security.afl_api") public static final String READ_FORENSIC_STATE = "android.permission.READ_FORENSIC_STATE"; field public static final String READ_GLOBAL_APP_SEARCH_DATA = "android.permission.READ_GLOBAL_APP_SEARCH_DATA"; field @FlaggedApi("android.content.pm.get_resolved_apk_path") public static final String READ_INSTALLED_SESSION_PATHS = "android.permission.READ_INSTALLED_SESSION_PATHS"; field public static final String READ_INSTALL_SESSIONS = "android.permission.READ_INSTALL_SESSIONS"; + field @FlaggedApi("android.security.afl_api") public static final String READ_INTRUSION_DETECTION_STATE = "android.permission.READ_INTRUSION_DETECTION_STATE"; field public static final String READ_NETWORK_USAGE_HISTORY = "android.permission.READ_NETWORK_USAGE_HISTORY"; field public static final String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE"; field public static final String READ_PEOPLE_DATA = "android.permission.READ_PEOPLE_DATA"; @@ -12891,13 +12891,13 @@ package android.security.authenticationpolicy { } -package android.security.forensic { +package android.security.intrusiondetection { - @FlaggedApi("android.security.afl_api") public class ForensicManager { - method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void addStateCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); - method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void disable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback); - method @RequiresPermission(android.Manifest.permission.MANAGE_FORENSIC_STATE) public void enable(@NonNull java.util.concurrent.Executor, @NonNull android.security.forensic.ForensicManager.CommandCallback); - method @RequiresPermission(android.Manifest.permission.READ_FORENSIC_STATE) public void removeStateCallback(@NonNull java.util.function.Consumer); + @FlaggedApi("android.security.afl_api") public class IntrusionDetectionManager { + method @RequiresPermission(android.Manifest.permission.READ_INTRUSION_DETECTION_STATE) public void addStateCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); + method @RequiresPermission(android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE) public void disable(@NonNull java.util.concurrent.Executor, @NonNull android.security.intrusiondetection.IntrusionDetectionManager.CommandCallback); + method @RequiresPermission(android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE) public void enable(@NonNull java.util.concurrent.Executor, @NonNull android.security.intrusiondetection.IntrusionDetectionManager.CommandCallback); + method @RequiresPermission(android.Manifest.permission.READ_INTRUSION_DETECTION_STATE) public void removeStateCallback(@NonNull java.util.function.Consumer); field public static final int ERROR_DATA_SOURCE_UNAVAILABLE = 4; // 0x4 field public static final int ERROR_PERMISSION_DENIED = 1; // 0x1 field public static final int ERROR_TRANSPORT_UNAVAILABLE = 3; // 0x3 @@ -12907,7 +12907,7 @@ package android.security.forensic { field public static final int STATE_UNKNOWN = 0; // 0x0 } - public static interface ForensicManager.CommandCallback { + public static interface IntrusionDetectionManager.CommandCallback { method public void onFailure(int); method public void onSuccess(); } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 6a23349bf8aa..2bd2d34d54a2 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -243,8 +243,8 @@ import android.security.attestationverification.AttestationVerificationManager; import android.security.attestationverification.IAttestationVerificationManagerService; import android.security.authenticationpolicy.AuthenticationPolicyManager; import android.security.authenticationpolicy.IAuthenticationPolicyService; -import android.security.forensic.ForensicManager; -import android.security.forensic.IForensicService; +import android.security.intrusiondetection.IIntrusionDetectionService; +import android.security.intrusiondetection.IntrusionDetectionManager; import android.security.keystore.KeyStoreManager; import android.service.oemlock.IOemLockService; import android.service.oemlock.OemLockManager; @@ -1818,15 +1818,20 @@ public final class SystemServiceRegistry { } }); - registerService(Context.FORENSIC_SERVICE, ForensicManager.class, - new CachedServiceFetcher() { + registerService(Context.INTRUSION_DETECTION_SERVICE, IntrusionDetectionManager.class, + new CachedServiceFetcher() { @Override - public ForensicManager createService(ContextImpl ctx) + public IntrusionDetectionManager createService(ContextImpl ctx) throws ServiceNotFoundException { + if (!android.security.Flags.aflApi()) { + throw new ServiceNotFoundException( + "Intrusion Detection is not supported"); + } IBinder b = ServiceManager.getServiceOrThrow( - Context.FORENSIC_SERVICE); - IForensicService service = IForensicService.Stub.asInterface(b); - return new ForensicManager(service); + Context.INTRUSION_DETECTION_SERVICE); + IIntrusionDetectionService service = + IIntrusionDetectionService.Stub.asInterface(b); + return new IntrusionDetectionManager(service); } }); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 19cd2e69cfdf..acad92c90c59 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5713,12 +5713,12 @@ public abstract class Context { public static final String BINARY_TRANSPARENCY_SERVICE = "transparency"; /** - * System service name for ForensicService. - * The service manages the forensic info on device. + * System service name for IntrusionDetectionService. + * The service manages the intrusion detection info on device. * @hide */ @FlaggedApi(android.security.Flags.FLAG_AFL_API) - public static final String FORENSIC_SERVICE = "forensic"; + public static final String INTRUSION_DETECTION_SERVICE = "intrusion_detection"; /** * System service name for the DeviceIdleManager. diff --git a/core/java/android/security/forensic/IForensicService.aidl b/core/java/android/security/forensic/IForensicService.aidl deleted file mode 100644 index 8039b264f0e5..000000000000 --- a/core/java/android/security/forensic/IForensicService.aidl +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.security.forensic; - -import android.security.forensic.IForensicServiceCommandCallback; -import android.security.forensic.IForensicServiceStateCallback; - -/** - * Binder interface to communicate with ForensicService. - * @hide - */ -interface IForensicService { - @EnforcePermission("READ_FORENSIC_STATE") - void addStateCallback(IForensicServiceStateCallback callback); - @EnforcePermission("READ_FORENSIC_STATE") - void removeStateCallback(IForensicServiceStateCallback callback); - @EnforcePermission("MANAGE_FORENSIC_STATE") - void enable(IForensicServiceCommandCallback callback); - @EnforcePermission("MANAGE_FORENSIC_STATE") - void disable(IForensicServiceCommandCallback callback); -} diff --git a/core/java/android/security/forensic/IForensicEventTransport.aidl b/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl similarity index 67% rename from core/java/android/security/forensic/IForensicEventTransport.aidl rename to core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl index 80e78eb9cf49..8759f72ca08a 100644 --- a/core/java/android/security/forensic/IForensicEventTransport.aidl +++ b/core/java/android/security/intrusiondetection/IIntrusionDetectionEventTransport.aidl @@ -14,25 +14,25 @@ * limitations under the License. */ -package android.security.forensic; -import android.security.forensic.ForensicEvent; +package android.security.intrusiondetection; +import android.security.intrusiondetection.IntrusionDetectionEvent; import com.android.internal.infra.AndroidFuture; /** {@hide} */ -oneway interface IForensicEventTransport { +oneway interface IIntrusionDetectionEventTransport { /** * Initialize the server side. */ void initialize(in AndroidFuture resultFuture); /** - * Send forensic logging data to the backup destination. - * The data is a list of ForensicEvent. - * The ForensicEvent is an abstract class that represents + * Send intrusiondetection logging data to the backup destination. + * The data is a list of IntrusionDetectionEvent. + * The IntrusionDetectionEvent is an abstract class that represents * different type of events. */ - void addData(in List events, in AndroidFuture resultFuture); + void addData(in List events, in AndroidFuture resultFuture); /** * Release the binder to the server. diff --git a/core/java/android/security/intrusiondetection/IIntrusionDetectionService.aidl b/core/java/android/security/intrusiondetection/IIntrusionDetectionService.aidl new file mode 100644 index 000000000000..0ba94185ddec --- /dev/null +++ b/core/java/android/security/intrusiondetection/IIntrusionDetectionService.aidl @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.intrusiondetection; + +import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback; +import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback; + +/** + * Binder interface to communicate with IntrusionDetectionService. + * @hide + */ +interface IIntrusionDetectionService { + @EnforcePermission("READ_INTRUSION_DETECTION_STATE") + void addStateCallback(IIntrusionDetectionServiceStateCallback callback); + @EnforcePermission("READ_INTRUSION_DETECTION_STATE") + void removeStateCallback(IIntrusionDetectionServiceStateCallback callback); + @EnforcePermission("MANAGE_INTRUSION_DETECTION_STATE") + void enable(IIntrusionDetectionServiceCommandCallback callback); + @EnforcePermission("MANAGE_INTRUSION_DETECTION_STATE") + void disable(IIntrusionDetectionServiceCommandCallback callback); +} diff --git a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl b/core/java/android/security/intrusiondetection/IIntrusionDetectionServiceCommandCallback.aidl similarity index 89% rename from core/java/android/security/forensic/IForensicServiceCommandCallback.aidl rename to core/java/android/security/intrusiondetection/IIntrusionDetectionServiceCommandCallback.aidl index 6d1456ea0426..80f09d520cc8 100644 --- a/core/java/android/security/forensic/IForensicServiceCommandCallback.aidl +++ b/core/java/android/security/intrusiondetection/IIntrusionDetectionServiceCommandCallback.aidl @@ -14,12 +14,12 @@ * limitations under the License. */ -package android.security.forensic; +package android.security.intrusiondetection; /** * @hide */ - oneway interface IForensicServiceCommandCallback { + oneway interface IIntrusionDetectionServiceCommandCallback { @Backing(type="int") enum ErrorCode{ UNKNOWN = 0, diff --git a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl b/core/java/android/security/intrusiondetection/IIntrusionDetectionServiceStateCallback.aidl similarity index 88% rename from core/java/android/security/forensic/IForensicServiceStateCallback.aidl rename to core/java/android/security/intrusiondetection/IIntrusionDetectionServiceStateCallback.aidl index 1b68c7b14bca..c88dc210320c 100644 --- a/core/java/android/security/forensic/IForensicServiceStateCallback.aidl +++ b/core/java/android/security/intrusiondetection/IIntrusionDetectionServiceStateCallback.aidl @@ -14,12 +14,12 @@ * limitations under the License. */ -package android.security.forensic; +package android.security.intrusiondetection; /** * @hide */ - oneway interface IForensicServiceStateCallback { + oneway interface IIntrusionDetectionServiceStateCallback { @Backing(type="int") enum State{ UNKNOWN = 0, diff --git a/core/java/android/security/forensic/ForensicEvent.aidl b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.aidl similarity index 88% rename from core/java/android/security/forensic/ForensicEvent.aidl rename to core/java/android/security/intrusiondetection/IntrusionDetectionEvent.aidl index a321fb0cb939..80b439673b3d 100644 --- a/core/java/android/security/forensic/ForensicEvent.aidl +++ b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.aidl @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.security.forensic; +package android.security.intrusiondetection; /** {@hide} */ -parcelable ForensicEvent; +parcelable IntrusionDetectionEvent; diff --git a/core/java/android/security/forensic/ForensicEvent.java b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java similarity index 80% rename from core/java/android/security/forensic/ForensicEvent.java rename to core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java index 3d908cca150c..538acf99c9fe 100644 --- a/core/java/android/security/forensic/ForensicEvent.java +++ b/core/java/android/security/intrusiondetection/IntrusionDetectionEvent.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.security.forensic; +package android.security.intrusiondetection; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -30,12 +30,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * A class that represents a forensic event. + * A class that represents a intrusiondetection event. * @hide */ @FlaggedApi(Flags.FLAG_AFL_API) -public final class ForensicEvent implements Parcelable { - private static final String TAG = "ForensicEvent"; +public final class IntrusionDetectionEvent implements Parcelable { + private static final String TAG = "IntrusionDetectionEvent"; public static final int SECURITY_EVENT = 0; public static final int NETWORK_EVENT_DNS = 1; @@ -44,9 +44,9 @@ public final class ForensicEvent implements Parcelable { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef({ - ForensicEvent.SECURITY_EVENT, - ForensicEvent.NETWORK_EVENT_DNS, - ForensicEvent.NETWORK_EVENT_CONNECT, + IntrusionDetectionEvent.SECURITY_EVENT, + IntrusionDetectionEvent.NETWORK_EVENT_DNS, + IntrusionDetectionEvent.NETWORK_EVENT_CONNECT, }) public @interface EventType {} @@ -56,39 +56,39 @@ public final class ForensicEvent implements Parcelable { private final DnsEvent mNetworkEventDns; private final ConnectEvent mNetworkEventConnect; - public static final @NonNull Parcelable.Creator CREATOR = + public static final @NonNull Parcelable.Creator CREATOR = new Parcelable.Creator<>() { - public ForensicEvent createFromParcel(Parcel in) { - return new ForensicEvent(in); + public IntrusionDetectionEvent createFromParcel(Parcel in) { + return new IntrusionDetectionEvent(in); } - public ForensicEvent[] newArray(int size) { - return new ForensicEvent[size]; + public IntrusionDetectionEvent[] newArray(int size) { + return new IntrusionDetectionEvent[size]; } }; - public ForensicEvent(@NonNull SecurityEvent securityEvent) { + public IntrusionDetectionEvent(@NonNull SecurityEvent securityEvent) { mType = SECURITY_EVENT; mSecurityEvent = securityEvent; mNetworkEventDns = null; mNetworkEventConnect = null; } - public ForensicEvent(@NonNull DnsEvent dnsEvent) { + public IntrusionDetectionEvent(@NonNull DnsEvent dnsEvent) { mType = NETWORK_EVENT_DNS; mNetworkEventDns = dnsEvent; mSecurityEvent = null; mNetworkEventConnect = null; } - public ForensicEvent(@NonNull ConnectEvent connectEvent) { + public IntrusionDetectionEvent(@NonNull ConnectEvent connectEvent) { mType = NETWORK_EVENT_CONNECT; mNetworkEventConnect = connectEvent; mSecurityEvent = null; mNetworkEventDns = null; } - private ForensicEvent(@NonNull Parcel in) { + private IntrusionDetectionEvent(@NonNull Parcel in) { mType = in.readInt(); switch (mType) { case SECURITY_EVENT: @@ -111,7 +111,7 @@ public final class ForensicEvent implements Parcelable { } } - /** Returns the type of the forensic event. */ + /** Returns the type of the IntrusionDetectionEvent. */ @NonNull public @EventType int getType() { return mType; @@ -170,7 +170,7 @@ public final class ForensicEvent implements Parcelable { @Override public String toString() { - return "ForensicEvent{" + return "IntrusionDetectionEvent{" + "mType=" + mType + '}'; } diff --git a/core/java/android/security/forensic/ForensicManager.java b/core/java/android/security/intrusiondetection/IntrusionDetectionManager.java similarity index 62% rename from core/java/android/security/forensic/ForensicManager.java rename to core/java/android/security/intrusiondetection/IntrusionDetectionManager.java index 9126182eda7b..e2463384d0c8 100644 --- a/core/java/android/security/forensic/ForensicManager.java +++ b/core/java/android/security/intrusiondetection/IntrusionDetectionManager.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.security.forensic; +package android.security.intrusiondetection; -import static android.Manifest.permission.MANAGE_FORENSIC_STATE; -import static android.Manifest.permission.READ_FORENSIC_STATE; +import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE; +import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; @@ -41,23 +41,23 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * ForensicManager manages the forensic logging on Android devices. - * Upon user consent, forensic logging collects various device events for + * IntrusionDetectionManager manages the intrusion detection on Android devices. + * Upon user consent, intrusion detection collects various device events for * off-device investigation of potential device compromise. *

- * Forensic logging can either be enabled ({@link #STATE_ENABLED} + * Intrusion detection logging can either be enabled ({@link #STATE_ENABLED} * or disabled ({@link #STATE_DISABLED}). *

- * The Forensic logs will be transferred to - * {@link android.security.forensic.ForensicEventTransport}. + * The intrusion detection logs will be transferred to + * {@link android.security.intrusiondetection.IntrusionDetectionEventTransport}. * * @hide */ @SystemApi @FlaggedApi(Flags.FLAG_AFL_API) -@SystemService(Context.FORENSIC_SERVICE) -public class ForensicManager { - private static final String TAG = "ForensicManager"; +@SystemService(Context.INTRUSION_DETECTION_SERVICE) +public class IntrusionDetectionManager { + private static final String TAG = "IntrusionDetectionManager"; /** @hide */ @Target(ElementType.TYPE_USE) @@ -67,7 +67,7 @@ public class ForensicManager { STATE_DISABLED, STATE_ENABLED }) - public @interface ForensicState {} + public @interface IntrusionDetectionState {} /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -77,64 +77,65 @@ public class ForensicManager { ERROR_TRANSPORT_UNAVAILABLE, ERROR_DATA_SOURCE_UNAVAILABLE }) - public @interface ForensicError {} + public @interface IntrusionDetectionError {} /** * Indicates an unknown state */ - public static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN; + public static final int STATE_UNKNOWN = IIntrusionDetectionServiceStateCallback.State.UNKNOWN; /** - * Indicates an state that the forensic is turned off. + * Indicates an state that the intrusion detection is turned off. */ - public static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED; + public static final int STATE_DISABLED = IIntrusionDetectionServiceStateCallback.State.DISABLED; /** - * Indicates an state that the forensic is turned on. + * Indicates an state that the intrusion detection is turned on. */ - public static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED; + public static final int STATE_ENABLED = IIntrusionDetectionServiceStateCallback.State.ENABLED; /** * Indicates an unknown error */ - public static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN; + public static final int ERROR_UNKNOWN = + IIntrusionDetectionServiceCommandCallback.ErrorCode.UNKNOWN; /** * Indicates an error due to insufficient access rights. */ public static final int ERROR_PERMISSION_DENIED = - IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED; + IIntrusionDetectionServiceCommandCallback.ErrorCode.PERMISSION_DENIED; /** - * Indicates an error due to unavailability of the forensic event transport. + * Indicates an error due to unavailability of the intrusion detection event transport. */ public static final int ERROR_TRANSPORT_UNAVAILABLE = - IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE; + IIntrusionDetectionServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE; /** * Indicates an error due to unavailability of the data source. */ public static final int ERROR_DATA_SOURCE_UNAVAILABLE = - IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; - private final IForensicService mService; + private final IIntrusionDetectionService mService; - private final ConcurrentHashMap, IForensicServiceStateCallback> + private final ConcurrentHashMap, IIntrusionDetectionServiceStateCallback> mStateCallbacks = new ConcurrentHashMap<>(); /** * Constructor * - * @param service A valid instance of IForensicService. + * @param service A valid instance of IIntrusionDetectionService. * @hide */ - public ForensicManager(IForensicService service) { + public IntrusionDetectionManager(IIntrusionDetectionService service) { mService = service; } /** - * Add a callback to monitor the state of the ForensicService. + * Add a callback to monitor the state of the IntrusionDetectionService. * * @param executor The executor through which the callback should be invoked. * @param callback The callback for state change. @@ -142,9 +143,9 @@ public class ForensicManager { * to reflect the init state. * The callback can be registered only once. */ - @RequiresPermission(READ_FORENSIC_STATE) + @RequiresPermission(READ_INTRUSION_DETECTION_STATE) public void addStateCallback(@NonNull @CallbackExecutor Executor executor, - @NonNull @ForensicState Consumer callback) { + @NonNull @IntrusionDetectionState Consumer callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); @@ -153,8 +154,8 @@ public class ForensicManager { return; } - final IForensicServiceStateCallback wrappedCallback = - new IForensicServiceStateCallback.Stub() { + final IIntrusionDetectionServiceStateCallback wrappedCallback = + new IIntrusionDetectionServiceStateCallback.Stub() { @Override public void onStateChange(int state) { executor.execute(() -> callback.accept(state)); @@ -170,19 +171,19 @@ public class ForensicManager { } /** - * Remove a callback to monitor the state of the ForensicService. + * Remove a callback to monitor the state of the IntrusionDetectionService. * * @param callback The callback to remove. */ - @RequiresPermission(READ_FORENSIC_STATE) - public void removeStateCallback(@NonNull Consumer<@ForensicState Integer> callback) { + @RequiresPermission(READ_INTRUSION_DETECTION_STATE) + public void removeStateCallback(@NonNull Consumer<@IntrusionDetectionState Integer> callback) { Objects.requireNonNull(callback); if (!mStateCallbacks.containsKey(callback)) { Log.d(TAG, "removeStateCallback callback not present"); return; } - IForensicServiceStateCallback wrappedCallback = mStateCallbacks.get(callback); + IIntrusionDetectionServiceStateCallback wrappedCallback = mStateCallbacks.get(callback); try { mService.removeStateCallback(wrappedCallback); @@ -194,22 +195,23 @@ public class ForensicManager { } /** - * Enable forensic logging. - * If successful, ForensicService will transition to {@link #STATE_ENABLED} state. + * Enable intrusion detection. + * If successful, IntrusionDetectionService will transition to {@link #STATE_ENABLED} state. *

- * When forensic logging is enabled, various device events will be collected and - * sent over to the registered {@link android.security.forensic.ForensicEventTransport}. + * When intrusion detection is enabled, various device events will be collected and + * sent over to the registered + * {@link android.security.intrusiondetection.IntrusionDetectionEventTransport}. * * @param executor The executor through which the callback should be invoked. * @param callback The callback for the command result. */ - @RequiresPermission(MANAGE_FORENSIC_STATE) + @RequiresPermission(MANAGE_INTRUSION_DETECTION_STATE) public void enable(@NonNull @CallbackExecutor Executor executor, @NonNull CommandCallback callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { - mService.enable(new IForensicServiceCommandCallback.Stub() { + mService.enable(new IIntrusionDetectionServiceCommandCallback.Stub() { @Override public void onSuccess() { executor.execute(callback::onSuccess); @@ -226,23 +228,23 @@ public class ForensicManager { } /** - * Disable forensic logging. - * If successful, ForensicService will transition to {@link #STATE_DISABLED}. + * Disable intrusion detection. + * If successful, IntrusionDetectionService will transition to {@link #STATE_DISABLED}. *

- * When forensic logging is disabled, device events will no longer be collected. - * Any events that have been collected but not yet sent to ForensicEventTransport + * When intrusion detection is disabled, device events will no longer be collected. + * Any events that have been collected but not yet sent to IntrusionDetectionEventTransport * will be transferred as a final batch. * * @param executor The executor through which the callback should be invoked. * @param callback The callback for the command result. */ - @RequiresPermission(MANAGE_FORENSIC_STATE) + @RequiresPermission(MANAGE_INTRUSION_DETECTION_STATE) public void disable(@NonNull @CallbackExecutor Executor executor, @NonNull CommandCallback callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); try { - mService.disable(new IForensicServiceCommandCallback.Stub() { + mService.disable(new IIntrusionDetectionServiceCommandCallback.Stub() { @Override public void onSuccess() { executor.execute(callback::onSuccess); @@ -271,6 +273,6 @@ public class ForensicManager { * Called when command fails. * @param error The error number. */ - void onFailure(@ForensicError int error); + void onFailure(@IntrusionDetectionError int error); } } diff --git a/core/java/android/security/forensic/OWNERS b/core/java/android/security/intrusiondetection/OWNERS similarity index 100% rename from core/java/android/security/forensic/OWNERS rename to core/java/android/security/intrusiondetection/OWNERS diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 8cc7b0b8f942..cd05a67fa424 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4203,35 +4203,35 @@ - - - - - - - - - diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3f4ea2df9266..41fefb5e6bca 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -7225,8 +7225,8 @@ com\u002eandroid\u002esettings - - + + false diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5a6b66cc1504..d3ccd0798bfa 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -5703,8 +5703,8 @@ - - + + diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 329e5de15f5e..836870e1d798 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -605,9 +605,9 @@ applications that come with the platform - - - + + + diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 1070ebdbb946..0df47ef8b4d3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -959,10 +959,10 @@ - - + - diff --git a/services/core/java/com/android/server/security/forensic/OWNERS b/services/core/java/com/android/server/security/forensic/OWNERS deleted file mode 100644 index 3bc3eb5d1412..000000000000 --- a/services/core/java/com/android/server/security/forensic/OWNERS +++ /dev/null @@ -1 +0,0 @@ -file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS diff --git a/services/core/java/com/android/server/security/forensic/DataAggregator.java b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java similarity index 78% rename from services/core/java/com/android/server/security/forensic/DataAggregator.java rename to services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java index cc473caa0ee7..06e9dcdcbedd 100644 --- a/services/core/java/com/android/server/security/forensic/DataAggregator.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataAggregator.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.android.server.security.forensic; +package com.android.server.security.intrusiondetection; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.security.forensic.ForensicEvent; +import android.security.intrusiondetection.IntrusionDetectionEvent; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -30,22 +30,22 @@ import java.util.ArrayList; import java.util.List; public class DataAggregator { - private static final String TAG = "Forensic DataAggregator"; + private static final String TAG = "IntrusionDetection DataAggregator"; private static final int MSG_SINGLE_DATA = 0; private static final int MSG_BATCH_DATA = 1; private static final int MSG_DISABLE = 2; private static final int STORED_EVENTS_SIZE_LIMIT = 1024; - private final ForensicService mForensicService; + private final IntrusionDetectionService mIntrusionDetectionService; private final ArrayList mDataSources; private Context mContext; - private List mStoredEvents = new ArrayList<>(); + private List mStoredEvents = new ArrayList<>(); private ServiceThread mHandlerThread; private Handler mHandler; - public DataAggregator(Context context, ForensicService forensicService) { - mForensicService = forensicService; + public DataAggregator(Context context, IntrusionDetectionService intrusionDetectionService) { + mIntrusionDetectionService = intrusionDetectionService; mContext = context; mDataSources = new ArrayList(); } @@ -83,14 +83,14 @@ public class DataAggregator { /** * DataSource calls it to transmit a single event. */ - public void addSingleData(ForensicEvent event) { + public void addSingleData(IntrusionDetectionEvent event) { mHandler.obtainMessage(MSG_SINGLE_DATA, event).sendToTarget(); } /** * DataSource calls it to transmit list of events. */ - public void addBatchData(List events) { + public void addBatchData(List events) { mHandler.obtainMessage(MSG_BATCH_DATA, events).sendToTarget(); } @@ -104,17 +104,17 @@ public class DataAggregator { } } - private void onNewSingleData(ForensicEvent event) { + private void onNewSingleData(IntrusionDetectionEvent event) { if (mStoredEvents.size() < STORED_EVENTS_SIZE_LIMIT) { mStoredEvents.add(event); } else { - mForensicService.addNewData(mStoredEvents); + mIntrusionDetectionService.addNewData(mStoredEvents); mStoredEvents = new ArrayList<>(); } } - private void onNewBatchData(List events) { - mForensicService.addNewData(events); + private void onNewBatchData(List events) { + mIntrusionDetectionService.addNewData(events); } private void onDisable() { @@ -135,10 +135,10 @@ public class DataAggregator { public void handleMessage(Message msg) { switch (msg.what) { case MSG_SINGLE_DATA: - mDataAggregator.onNewSingleData((ForensicEvent) msg.obj); + mDataAggregator.onNewSingleData((IntrusionDetectionEvent) msg.obj); break; case MSG_BATCH_DATA: - mDataAggregator.onNewBatchData((List) msg.obj); + mDataAggregator.onNewBatchData((List) msg.obj); break; case MSG_DISABLE: mDataAggregator.onDisable(); diff --git a/services/core/java/com/android/server/security/forensic/DataSource.java b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java similarity index 93% rename from services/core/java/com/android/server/security/forensic/DataSource.java rename to services/core/java/com/android/server/security/intrusiondetection/DataSource.java index da7ee210281b..0bc448245b76 100644 --- a/services/core/java/com/android/server/security/forensic/DataSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/DataSource.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.security.forensic; +package com.android.server.security.intrusiondetection; public interface DataSource { /** diff --git a/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java similarity index 74% rename from services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java rename to services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java index b85199ed9218..82f39b327cea 100644 --- a/services/core/java/com/android/server/security/forensic/ForensicEventTransportConnection.java +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionEventTransportConnection.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.security.forensic; +package com.android.server.security.intrusiondetection; -import static android.Manifest.permission.BIND_FORENSIC_EVENT_TRANSPORT_SERVICE; +import static android.Manifest.permission.BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE; import android.content.ComponentName; import android.content.Context; @@ -27,8 +27,8 @@ import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; -import android.security.forensic.ForensicEvent; -import android.security.forensic.IForensicEventTransport; +import android.security.intrusiondetection.IIntrusionDetectionEventTransport; +import android.security.intrusiondetection.IntrusionDetectionEvent; import android.text.TextUtils; import android.util.Slog; @@ -40,20 +40,20 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class ForensicEventTransportConnection implements ServiceConnection { - private static final String TAG = "ForensicEventTransportConnection"; +public class IntrusionDetectionEventTransportConnection implements ServiceConnection { + private static final String TAG = "IntrusionDetectionEventTransportConnection"; private static final long FUTURE_TIMEOUT_MILLIS = 60 * 1000; // 1 mins private final Context mContext; - private String mForensicEventTransportConfig; - volatile IForensicEventTransport mService; + private String mIntrusionDetectionEventTransportConfig; + volatile IIntrusionDetectionEventTransport mService; - public ForensicEventTransportConnection(Context context) { + public IntrusionDetectionEventTransportConnection(Context context) { mContext = context; mService = null; } /** - * Initialize the ForensicEventTransport binder service. + * Initialize the IntrusionDetectionEventTransport binder service. * @return Whether the initialization succeed. */ public boolean initialize() { @@ -78,11 +78,11 @@ public class ForensicEventTransportConnection implements ServiceConnection { } /** - * Add data to the ForensicEventTransport binder service. - * @param data List of ForensicEvent. + * Add data to the IntrusionDetectionEventTransport binder service. + * @param data List of IntrusionDetectionEvent. * @return Whether the data is added to the binder service. */ - public boolean addData(List data) { + public boolean addData(List data) { AndroidFuture resultFuture = new AndroidFuture<>(); try { mService.addData(data, resultFuture); @@ -119,15 +119,15 @@ public class ForensicEventTransportConnection implements ServiceConnection { } private boolean bindService() { - mForensicEventTransportConfig = mContext.getString( - com.android.internal.R.string.config_forensicEventTransport); - if (TextUtils.isEmpty(mForensicEventTransportConfig)) { - Slog.e(TAG, "config_forensicEventTransport is empty"); + mIntrusionDetectionEventTransportConfig = mContext.getString( + com.android.internal.R.string.config_intrusionDetectionEventTransport); + if (TextUtils.isEmpty(mIntrusionDetectionEventTransportConfig)) { + Slog.e(TAG, "config_intrusionDetectionEventTransport is empty"); return false; } ComponentName serviceComponent = - ComponentName.unflattenFromString(mForensicEventTransportConfig); + ComponentName.unflattenFromString(mIntrusionDetectionEventTransportConfig); if (serviceComponent == null) { Slog.e(TAG, "Can't get serviceComponent name"); return false; @@ -136,10 +136,10 @@ public class ForensicEventTransportConnection implements ServiceConnection { try { ServiceInfo serviceInfo = mContext.getPackageManager().getServiceInfo(serviceComponent, 0 /* flags */); - if (!BIND_FORENSIC_EVENT_TRANSPORT_SERVICE.equals(serviceInfo.permission)) { + if (!BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE.equals(serviceInfo.permission)) { Slog.e(TAG, serviceComponent.flattenToShortString() + " is not declared with the permission " - + "\"" + BIND_FORENSIC_EVENT_TRANSPORT_SERVICE + "\""); + + "\"" + BIND_INTRUSION_DETECTION_EVENT_TRANSPORT_SERVICE + "\""); return false; } } catch (PackageManager.NameNotFoundException e) { @@ -163,7 +163,7 @@ public class ForensicEventTransportConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { - mService = IForensicEventTransport.Stub.asInterface(service); + mService = IIntrusionDetectionEventTransport.Stub.asInterface(service); } @Override diff --git a/services/core/java/com/android/server/security/forensic/ForensicService.java b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java similarity index 59% rename from services/core/java/com/android/server/security/forensic/ForensicService.java rename to services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java index 2be068fa2f83..0287b415b9c2 100644 --- a/services/core/java/com/android/server/security/forensic/ForensicService.java +++ b/services/core/java/com/android/server/security/intrusiondetection/IntrusionDetectionService.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.android.server.security.forensic; +package com.android.server.security.intrusiondetection; -import static android.Manifest.permission.MANAGE_FORENSIC_STATE; -import static android.Manifest.permission.READ_FORENSIC_STATE; +import static android.Manifest.permission.MANAGE_INTRUSION_DETECTION_STATE; +import static android.Manifest.permission.READ_INTRUSION_DETECTION_STATE; import android.annotation.EnforcePermission; import android.annotation.NonNull; @@ -27,10 +27,10 @@ import android.os.Looper; import android.os.Message; import android.os.PermissionEnforcer; import android.os.RemoteException; -import android.security.forensic.ForensicEvent; -import android.security.forensic.IForensicService; -import android.security.forensic.IForensicServiceCommandCallback; -import android.security.forensic.IForensicServiceStateCallback; +import android.security.intrusiondetection.IIntrusionDetectionService; +import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback; +import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback; +import android.security.intrusiondetection.IntrusionDetectionEvent; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -43,8 +43,8 @@ import java.util.List; /** * @hide */ -public class ForensicService extends SystemService { - private static final String TAG = "ForensicService"; +public class IntrusionDetectionService extends SystemService { + private static final String TAG = "IntrusionDetectionService"; private static final int MAX_STATE_CALLBACK_NUM = 16; private static final int MSG_ADD_STATE_CALLBACK = 0; @@ -53,39 +53,46 @@ public class ForensicService extends SystemService { private static final int MSG_DISABLE = 3; private static final int MSG_TRANSPORT = 4; - private static final int STATE_UNKNOWN = IForensicServiceStateCallback.State.UNKNOWN; - private static final int STATE_DISABLED = IForensicServiceStateCallback.State.DISABLED; - private static final int STATE_ENABLED = IForensicServiceStateCallback.State.ENABLED; + private static final int STATE_UNKNOWN = + IIntrusionDetectionServiceStateCallback.State.UNKNOWN; + private static final int STATE_DISABLED = + IIntrusionDetectionServiceStateCallback.State.DISABLED; + private static final int STATE_ENABLED = + IIntrusionDetectionServiceStateCallback.State.ENABLED; - private static final int ERROR_UNKNOWN = IForensicServiceCommandCallback.ErrorCode.UNKNOWN; + private static final int ERROR_UNKNOWN = + IIntrusionDetectionServiceCommandCallback.ErrorCode.UNKNOWN; private static final int ERROR_PERMISSION_DENIED = - IForensicServiceCommandCallback.ErrorCode.PERMISSION_DENIED; + IIntrusionDetectionServiceCommandCallback.ErrorCode.PERMISSION_DENIED; private static final int ERROR_INVALID_STATE_TRANSITION = - IForensicServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION; + IIntrusionDetectionServiceCommandCallback.ErrorCode.INVALID_STATE_TRANSITION; private static final int ERROR_TRANSPORT_UNAVAILABLE = - IForensicServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE; + IIntrusionDetectionServiceCommandCallback.ErrorCode.TRANSPORT_UNAVAILABLE; private static final int ERROR_DATA_SOURCE_UNAVAILABLE = - IForensicServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; + IIntrusionDetectionServiceCommandCallback.ErrorCode.DATA_SOURCE_UNAVAILABLE; private final Context mContext; private final Handler mHandler; - private final ForensicEventTransportConnection mForensicEventTransportConnection; + private final IntrusionDetectionEventTransportConnection + mIntrusionDetectionEventTransportConnection; private final DataAggregator mDataAggregator; private final BinderService mBinderService; - private final ArrayList mStateCallbacks = new ArrayList<>(); + private final ArrayList mStateCallbacks = + new ArrayList<>(); private volatile int mState = STATE_DISABLED; - public ForensicService(@NonNull Context context) { + public IntrusionDetectionService(@NonNull Context context) { this(new InjectorImpl(context)); } @VisibleForTesting - ForensicService(@NonNull Injector injector) { + IntrusionDetectionService(@NonNull Injector injector) { super(injector.getContext()); mContext = injector.getContext(); mHandler = new EventHandler(injector.getLooper(), this); - mForensicEventTransportConnection = injector.getForensicEventransportConnection(); + mIntrusionDetectionEventTransportConnection = + injector.getIntrusionDetectionEventransportConnection(); mDataAggregator = injector.getDataAggregator(this); mBinderService = new BinderService(this, injector.getPermissionEnforcer()); } @@ -95,47 +102,48 @@ public class ForensicService extends SystemService { mState = state; } - private static final class BinderService extends IForensicService.Stub { - final ForensicService mService; + private static final class BinderService extends IIntrusionDetectionService.Stub { + final IntrusionDetectionService mService; - BinderService(ForensicService service, @NonNull PermissionEnforcer permissionEnforcer) { + BinderService(IntrusionDetectionService service, + @NonNull PermissionEnforcer permissionEnforcer) { super(permissionEnforcer); mService = service; } @Override - @EnforcePermission(READ_FORENSIC_STATE) - public void addStateCallback(IForensicServiceStateCallback callback) { + @EnforcePermission(READ_INTRUSION_DETECTION_STATE) + public void addStateCallback(IIntrusionDetectionServiceStateCallback callback) { addStateCallback_enforcePermission(); mService.mHandler.obtainMessage(MSG_ADD_STATE_CALLBACK, callback).sendToTarget(); } @Override - @EnforcePermission(READ_FORENSIC_STATE) - public void removeStateCallback(IForensicServiceStateCallback callback) { + @EnforcePermission(READ_INTRUSION_DETECTION_STATE) + public void removeStateCallback(IIntrusionDetectionServiceStateCallback callback) { removeStateCallback_enforcePermission(); mService.mHandler.obtainMessage(MSG_REMOVE_STATE_CALLBACK, callback).sendToTarget(); } @Override - @EnforcePermission(MANAGE_FORENSIC_STATE) - public void enable(IForensicServiceCommandCallback callback) { + @EnforcePermission(MANAGE_INTRUSION_DETECTION_STATE) + public void enable(IIntrusionDetectionServiceCommandCallback callback) { enable_enforcePermission(); mService.mHandler.obtainMessage(MSG_ENABLE, callback).sendToTarget(); } @Override - @EnforcePermission(MANAGE_FORENSIC_STATE) - public void disable(IForensicServiceCommandCallback callback) { + @EnforcePermission(MANAGE_INTRUSION_DETECTION_STATE) + public void disable(IIntrusionDetectionServiceCommandCallback callback) { disable_enforcePermission(); mService.mHandler.obtainMessage(MSG_DISABLE, callback).sendToTarget(); } } private static class EventHandler extends Handler { - private final ForensicService mService; + private final IntrusionDetectionService mService; - EventHandler(Looper looper, ForensicService service) { + EventHandler(Looper looper, IntrusionDetectionService service) { super(looper); mService = service; } @@ -146,7 +154,7 @@ public class ForensicService extends SystemService { case MSG_ADD_STATE_CALLBACK: try { mService.addStateCallback( - (IForensicServiceStateCallback) msg.obj); + (IIntrusionDetectionServiceStateCallback) msg.obj); } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } @@ -154,27 +162,27 @@ public class ForensicService extends SystemService { case MSG_REMOVE_STATE_CALLBACK: try { mService.removeStateCallback( - (IForensicServiceStateCallback) msg.obj); + (IIntrusionDetectionServiceStateCallback) msg.obj); } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } break; case MSG_ENABLE: try { - mService.enable((IForensicServiceCommandCallback) msg.obj); + mService.enable((IIntrusionDetectionServiceCommandCallback) msg.obj); } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } break; case MSG_DISABLE: try { - mService.disable((IForensicServiceCommandCallback) msg.obj); + mService.disable((IIntrusionDetectionServiceCommandCallback) msg.obj); } catch (RemoteException e) { Slog.e(TAG, "RemoteException", e); } break; case MSG_TRANSPORT: - mService.transport((List) msg.obj); + mService.transport((List) msg.obj); break; default: Slog.w(TAG, "Unknown message: " + msg.what); @@ -182,7 +190,8 @@ public class ForensicService extends SystemService { } } - private void addStateCallback(IForensicServiceStateCallback callback) throws RemoteException { + private void addStateCallback(IIntrusionDetectionServiceStateCallback callback) + throws RemoteException { for (int i = 0; i < mStateCallbacks.size(); i++) { if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) { return; @@ -192,7 +201,7 @@ public class ForensicService extends SystemService { callback.onStateChange(mState); } - private void removeStateCallback(IForensicServiceStateCallback callback) + private void removeStateCallback(IIntrusionDetectionServiceStateCallback callback) throws RemoteException { for (int i = 0; i < mStateCallbacks.size(); i++) { if (mStateCallbacks.get(i).asBinder() == callback.asBinder()) { @@ -216,15 +225,16 @@ public class ForensicService extends SystemService { } } - private void enable(IForensicServiceCommandCallback callback) throws RemoteException { + private void enable(IIntrusionDetectionServiceCommandCallback callback) + throws RemoteException { if (mState == STATE_ENABLED) { callback.onSuccess(); return; } - // TODO: temporarily disable the following for the CTS ForensicManagerTest. + // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest. // Enable it when the transport component is ready. - // if (!mForensicEventTransportConnection.initialize()) { + // if (!mIntrusionDetectionEventTransportConnection.initialize()) { // callback.onFailure(ERROR_TRANSPORT_UNAVAILABLE); // return; // } @@ -235,15 +245,16 @@ public class ForensicService extends SystemService { callback.onSuccess(); } - private void disable(IForensicServiceCommandCallback callback) throws RemoteException { + private void disable(IIntrusionDetectionServiceCommandCallback callback) + throws RemoteException { if (mState == STATE_DISABLED) { callback.onSuccess(); return; } - // TODO: temporarily disable the following for the CTS ForensicManagerTest. + // TODO: temporarily disable the following for the CTS IntrusionDetectionManagerTest. // Enable it when the transport component is ready. - // mForensicEventTransportConnection.release(); + // mIntrusionDetectionEventTransportConnection.release(); mDataAggregator.disable(); mState = STATE_DISABLED; notifyStateMonitors(); @@ -251,27 +262,27 @@ public class ForensicService extends SystemService { } /** - * Add a list of ForensicEvent. + * Add a list of IntrusionDetectionEvent. */ - public void addNewData(List events) { + public void addNewData(List events) { mHandler.obtainMessage(MSG_TRANSPORT, events).sendToTarget(); } - private void transport(List events) { - mForensicEventTransportConnection.addData(events); + private void transport(List events) { + mIntrusionDetectionEventTransportConnection.addData(events); } @Override public void onStart() { try { - publishBinderService(Context.FORENSIC_SERVICE, mBinderService); + publishBinderService(Context.INTRUSION_DETECTION_SERVICE, mBinderService); } catch (Throwable t) { - Slog.e(TAG, "Could not start the ForensicService.", t); + Slog.e(TAG, "Could not start the IntrusionDetectionService.", t); } } @VisibleForTesting - IForensicService getBinderService() { + IIntrusionDetectionService getBinderService() { return mBinderService; } @@ -282,9 +293,9 @@ public class ForensicService extends SystemService { Looper getLooper(); - ForensicEventTransportConnection getForensicEventransportConnection(); + IntrusionDetectionEventTransportConnection getIntrusionDetectionEventransportConnection(); - DataAggregator getDataAggregator(ForensicService forensicService); + DataAggregator getDataAggregator(IntrusionDetectionService intrusionDetectionService); } private static final class InjectorImpl implements Injector { @@ -314,13 +325,15 @@ public class ForensicService extends SystemService { } @Override - public ForensicEventTransportConnection getForensicEventransportConnection() { - return new ForensicEventTransportConnection(mContext); + public IntrusionDetectionEventTransportConnection + getIntrusionDetectionEventransportConnection() { + return new IntrusionDetectionEventTransportConnection(mContext); } @Override - public DataAggregator getDataAggregator(ForensicService forensicService) { - return new DataAggregator(mContext, forensicService); + public DataAggregator getDataAggregator( + IntrusionDetectionService intrusionDetectionService) { + return new DataAggregator(mContext, intrusionDetectionService); } } } diff --git a/services/core/java/com/android/server/security/intrusiondetection/OWNERS b/services/core/java/com/android/server/security/intrusiondetection/OWNERS new file mode 100644 index 000000000000..05080670ca2c --- /dev/null +++ b/services/core/java/com/android/server/security/intrusiondetection/OWNERS @@ -0,0 +1 @@ +file:platform/frameworks/base:main:/core/java/android/security/intrusiondetection/OWNERS diff --git a/services/core/java/com/android/server/security/forensic/SecurityLogSource.java b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java similarity index 87% rename from services/core/java/com/android/server/security/forensic/SecurityLogSource.java rename to services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java index e1b49c42018b..226f9d879cab 100644 --- a/services/core/java/com/android/server/security/forensic/SecurityLogSource.java +++ b/services/core/java/com/android/server/security/intrusiondetection/SecurityLogSource.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.security.forensic; +package com.android.server.security.intrusiondetection; import android.Manifest.permission; import android.annotation.RequiresPermission; import android.app.admin.DevicePolicyManager; import android.app.admin.SecurityLog.SecurityEvent; import android.content.Context; -import android.security.forensic.ForensicEvent; +import android.security.intrusiondetection.IntrusionDetectionEvent; import java.util.List; import java.util.concurrent.Executor; @@ -31,7 +31,7 @@ import java.util.stream.Collectors; public class SecurityLogSource implements DataSource { - private static final String TAG = "Forensic SecurityLogSource"; + private static final String TAG = "IntrusionDetection SecurityLogSource"; private SecurityEventCallback mEventCallback = new SecurityEventCallback(); private DevicePolicyManager mDpm; @@ -85,12 +85,12 @@ public class SecurityLogSource implements DataSource { @Override public void accept(List events) { - List forensicEvents = + List intrusionDetectionEvents = events.stream() .filter(event -> event != null) - .map(event -> new ForensicEvent(event)) + .map(event -> new IntrusionDetectionEvent(event)) .collect(Collectors.toList()); - mDataAggregator.addBatchData(forensicEvents); + mDataAggregator.addBatchData(intrusionDetectionEvents); } } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index dc10dd9f47a2..980fcac051cb 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -252,7 +252,7 @@ import com.android.server.security.KeyChainSystemService; import com.android.server.security.advancedprotection.AdvancedProtectionService; import com.android.server.security.authenticationpolicy.AuthenticationPolicyService; import com.android.server.security.authenticationpolicy.SecureLockDeviceService; -import com.android.server.security.forensic.ForensicService; +import com.android.server.security.intrusiondetection.IntrusionDetectionService; import com.android.server.security.rkp.RemoteProvisioningService; import com.android.server.selinux.SelinuxAuditLogsService; import com.android.server.sensorprivacy.SensorPrivacyService; @@ -1764,8 +1764,8 @@ public final class SystemServer implements Dumpable { if (!isWatch && !isTv && !isAutomotive && android.security.Flags.aflApi()) { - t.traceBegin("StartForensicService"); - mSystemServiceManager.startService(ForensicService.class); + t.traceBegin("StartIntrusionDetectionService"); + mSystemServiceManager.startService(IntrusionDetectionService.class); t.traceEnd(); } diff --git a/services/tests/security/forensic/OWNERS b/services/tests/security/forensic/OWNERS deleted file mode 100644 index 80c9afb96033..000000000000 --- a/services/tests/security/forensic/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 36824 - -file:platform/frameworks/base:main:/core/java/android/security/forensic/OWNERS diff --git a/services/tests/security/forensic/TEST_MAPPING b/services/tests/security/forensic/TEST_MAPPING deleted file mode 100644 index bd8b2ab7c41f..000000000000 --- a/services/tests/security/forensic/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "postsubmit": [ - { - "name": "ForensicServiceTests" - } - ] -} diff --git a/services/tests/security/forensic/Android.bp b/services/tests/security/intrusiondetection/Android.bp similarity index 95% rename from services/tests/security/forensic/Android.bp rename to services/tests/security/intrusiondetection/Android.bp index 77a87afba6a7..00ac90807dff 100644 --- a/services/tests/security/forensic/Android.bp +++ b/services/tests/security/intrusiondetection/Android.bp @@ -9,7 +9,7 @@ package { } android_test { - name: "ForensicServiceTests", + name: "IntrusionDetectionServiceTests", srcs: [ "src/**/*.java", ], diff --git a/services/tests/security/forensic/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml similarity index 83% rename from services/tests/security/forensic/AndroidManifest.xml rename to services/tests/security/intrusiondetection/AndroidManifest.xml index c5b3d40ce254..f388e7ea8590 100644 --- a/services/tests/security/forensic/AndroidManifest.xml +++ b/services/tests/security/intrusiondetection/AndroidManifest.xml @@ -15,7 +15,7 @@ --> + package="com.android.server.security.intrusiondetection.tests"> @@ -25,6 +25,6 @@ + android:targetPackage="com.android.server.security.intrusiondetection.tests" + android:label="Frameworks IntrusionDetection Services Tests"/> diff --git a/services/tests/security/forensic/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml similarity index 83% rename from services/tests/security/forensic/AndroidTest.xml rename to services/tests/security/intrusiondetection/AndroidTest.xml index bbe2e9c303ce..42cb9e3236e0 100644 --- a/services/tests/security/forensic/AndroidTest.xml +++ b/services/tests/security/intrusiondetection/AndroidTest.xml @@ -13,19 +13,19 @@ See the License for the specific language governing permissions and limitations under the License. --> - +

+ * A security review is required for any namespace that's added to this list. To add to + * the list, create a change and tag the OWNER. In the commit message, include a + * description of the flag's functionality, and a justification for why it needs to be + * allowlisted. + */ +final class WritableNamespaces { + public static final Set ALLOWLIST = + new ArraySet(Arrays.asList( + "exo" + )); +} -- GitLab From 1895e2e9de073860cca89c9f94c0af548d6eb6aa Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 13 Nov 2024 11:48:29 -0500 Subject: [PATCH 193/656] Don't create layers for negative-sized RenderNodes Fixes: 257954570 Test: n/a Flag: EXEMPT trivial bug fix Change-Id: I82a00ec21b58ea55779d02db4a54d1ef2d37dcc7 --- libs/hwui/DeviceInfo.h | 1 + libs/hwui/RenderNode.cpp | 2 +- libs/hwui/RenderProperties.h | 3 ++- libs/hwui/renderthread/CanvasContext.cpp | 5 +++++ libs/hwui/tests/unit/RenderPropertiesTests.cpp | 8 ++++++-- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index fb58a69747b3..b72e066e64ae 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -84,6 +84,7 @@ public: // this value is only valid after the GPU has been initialized and there is a valid graphics // context or if you are using the HWUI_NULL_GPU int maxTextureSize() const; + bool hasMaxTextureSize() const { return mMaxTextureSize > 0; } sk_sp getWideColorSpace() const { return mWideColorSpace; } SkColorType getWideColorType() { static std::once_flag kFlag; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 2c23864317a4..4801bd1038a3 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -186,7 +186,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { // If we are not a layer OR we cannot be rendered (eg, view was detached) // we need to destroy any Layers we may have had previously if (CC_LIKELY(layerType != LayerType::RenderLayer) || CC_UNLIKELY(!isRenderable()) || - CC_UNLIKELY(properties().getWidth() == 0) || CC_UNLIKELY(properties().getHeight() == 0) || + CC_UNLIKELY(properties().getWidth() <= 0) || CC_UNLIKELY(properties().getHeight() <= 0) || CC_UNLIKELY(!properties().fitsOnLayer())) { if (CC_UNLIKELY(hasLayer())) { this->setLayerSurface(nullptr); diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index b1ad8b2eb1b9..4dc57004e401 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -545,7 +545,8 @@ public: bool fitsOnLayer() const { const DeviceInfo* deviceInfo = DeviceInfo::get(); return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize() && - mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize(); + mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize() && + mPrimitiveFields.mWidth > 0 && mPrimitiveFields.mHeight > 0; } bool promotedToLayer() const { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8ec04304a808..b36b8be10779 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -418,6 +418,11 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy RenderNode* target) { mRenderThread.removeFrameCallback(this); + // Make sure we have a valid device info + if (!DeviceInfo::get()->hasMaxTextureSize()) { + (void)mRenderThread.requireGrContext(); + } + // If the previous frame was dropped we don't need to hold onto it, so // just keep using the previous frame's structure instead const auto reason = wasSkipped(mCurrentFrameInfo); diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp index 3e8e0576bf49..6ec042cf23b0 100644 --- a/libs/hwui/tests/unit/RenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp @@ -40,7 +40,11 @@ TEST(RenderProperties, layerValidity) { props.setLeftTopRightBottom(0, 0, maxTextureSize + 1, maxTextureSize + 1); ASSERT_FALSE(props.fitsOnLayer()); - // Too small, but still 'fits'. Not fitting is an error case, so don't report empty as such. + // Too small, we can't create a layer for a 0 width or height props.setLeftTopRightBottom(0, 0, 100, 0); - ASSERT_TRUE(props.fitsOnLayer()); + ASSERT_FALSE(props.fitsOnLayer()); + + // Can't create a negative-sized layer + props.setLeftTopRightBottom(0, 0, -100, 300); + ASSERT_FALSE(props.fitsOnLayer()); } -- GitLab From dca3053db1c9c680e77859dfd347b1886f275aae Mon Sep 17 00:00:00 2001 From: Diya Bera Date: Mon, 18 Nov 2024 21:05:07 +0000 Subject: [PATCH 194/656] Call InvalidationCallback#onCompleted when client is cancelled Fixes: 366384601 Test: atest FingerprintInvalidationClientTest, atest FaceInvalidationClientTest Flag: EXEMPT bug fix (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:da582a35c9ce60fbdae6360cf042faef7d0bbe8d) Merged-In: Id5ed4bc5f8e38052b8011528015945ed51639fe9 Change-Id: Id5ed4bc5f8e38052b8011528015945ed51639fe9 --- .../sensors/InvalidationClient.java | 10 ++ .../face/aidl/FaceInvalidationClientTest.java | 97 +++++++++++++++++++ .../FingerprintInvalidationClientTest.java | 90 +++++++++++++++++ 3 files changed, 197 insertions(+) create mode 100644 services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClientTest.java create mode 100644 services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java index d5aa5e2c9db6..43414c0a64f5 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java @@ -76,6 +76,16 @@ public abstract class InvalidationClient mFace, USER_ID, + mAidlResponseHandler); + final FaceInvalidationClient faceInvalidationClient = + new FaceInvalidationClient(mContext, () -> aidlSession, USER_ID, + SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(), + mInvalidationCallback); + + doAnswer((Answer) invocationOnMock -> { + faceInvalidationClient.cancel(); + return null; + }).when(mAidlResponseHandler).onUnsupportedClientScheduled(); + + faceInvalidationClient.start(mClientMonitorCallback); + + verify(mInvalidationCallback).onCompleted(); + } +} + diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java new file mode 100644 index 000000000000..1ee2fd1e8af9 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClientTest.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; + +import android.hardware.biometrics.IInvalidationCallback; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; + +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; + +@Presubmit +@SmallTest +public class FingerprintInvalidationClientTest { + + private static final int SENSOR_ID = 4; + private static final int USER_ID = 0; + + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IBiometricsFingerprint mFingerprint; + @Mock + private AidlResponseHandler mAidlResponseHandler; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private IInvalidationCallback mInvalidationCallback; + @Mock + private ClientMonitorCallback mClientMonitorCallback; + + @Test + public void testStartInvalidationClient_whenHalIsHidl() throws RemoteException { + final AidlSession aidlSession = new AidlSession( + () -> mFingerprint, USER_ID, mAidlResponseHandler); + final FingerprintInvalidationClient fingerprintInvalidationClient = + new FingerprintInvalidationClient(mContext, () -> aidlSession, USER_ID, + SENSOR_ID, mBiometricLogger, mBiometricContext, new HashMap<>(), + mInvalidationCallback); + + doAnswer((Answer) invocationOnMock -> { + fingerprintInvalidationClient.cancel(); + return null; + }).when(mAidlResponseHandler).onUnsupportedClientScheduled( + FingerprintInvalidationClient.class); + + fingerprintInvalidationClient.start(mClientMonitorCallback); + + verify(mInvalidationCallback).onCompleted(); + } +} -- GitLab From abff2386df0fb9edb5a6b304a1ddb6deb91cbc52 Mon Sep 17 00:00:00 2001 From: Sally Kovacs Date: Wed, 9 Oct 2024 23:12:15 +0000 Subject: [PATCH 195/656] Make Javadoc changes to TtsSpan related to new API We can't submit Javadoc changes to existing APIs that reference FlaggedAPIs. So we put these changes in a different CL, which we should submit before SDK finalization but after the quarterly releases that come between API bumps. This CL adds references to durations and seconds in the Javadocs related to TYPE_TIME. Flag: DOCS_ONLY Bug: 337103893 Bug: 372323279 Test: builds Change-Id: I75e55864379c104c47bb7bb50b475e8b8b14a1db --- core/java/android/text/style/TtsSpan.java | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java index b7b8f0b1b5d8..e257f3409beb 100644 --- a/core/java/android/text/style/TtsSpan.java +++ b/core/java/android/text/style/TtsSpan.java @@ -107,11 +107,13 @@ public class TtsSpan implements ParcelableSpan { /** * The text associated with this span is a time, consisting of a number of - * hours and minutes, specified with {@link #ARG_HOURS} and - * {@link #ARG_MINUTES}. + * hours, minutes, and seconds specified with {@link #ARG_HOURS}, {@link #ARG_MINUTES}, and + * {@link #ARG_SECONDS}. * Also accepts the arguments {@link #ARG_GENDER}, * {@link #ARG_ANIMACY}, {@link #ARG_MULTIPLICITY} and - * {@link #ARG_CASE}. + * {@link #ARG_CASE}. This is different from {@link #TYPE_DURATION}. This should be used to + * convey a particular moment in time, such as a clock time, while {@link #TYPE_DURATION} should + * be used to convey an interval of time. */ public static final String TYPE_TIME = "android.type.time"; @@ -309,16 +311,18 @@ public class TtsSpan implements ParcelableSpan { public static final String ARG_UNIT = "android.arg.unit"; /** - * Argument used to specify the hours of a time. The hours should be - * provided as an integer in the range from 0 up to and including 24. - * Can be used with {@link #TYPE_TIME}. + * Argument used to specify the hours of a time or duration. The hours should be + * provided as an integer in the range from 0 up to and including 24 for + * {@link #TYPE_TIME}. + * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. */ public static final String ARG_HOURS = "android.arg.hours"; /** - * Argument used to specify the minutes of a time. The minutes should be - * provided as an integer in the range from 0 up to and including 59. - * Can be used with {@link #TYPE_TIME}. + * Argument used to specify the minutes of a time or duration. The minutes should be + * provided as an integer in the range from 0 up to and including 59 for + * {@link #TYPE_TIME}. + * Can be used with {@link #TYPE_TIME} or {@link #TYPE_DURATION}. */ public static final String ARG_MINUTES = "android.arg.minutes"; -- GitLab From ef5051df2c517a186b9d3cd6b1ea054eeee368e1 Mon Sep 17 00:00:00 2001 From: Yu-Ting Tseng Date: Mon, 18 Nov 2024 17:19:17 +0000 Subject: [PATCH 196/656] Revert^2 "Address frozen notification API feedback" This is a clean revert. Previously the test failed because the kernel has not been updated to support frozen notifications. Try rolling forward again, this time merging the frameworks/base change first, then waiting for kernel update before merging the CTS. 0b1fe74a64dc38800d1405e59f467514e30e2297 Change-Id: Id1deb6ea308658c0331859ac9b1ac695283e461b API-Coverage-Bug: 367706442 --- core/api/current.txt | 4 +- core/java/android/os/BinderProxy.java | 34 +++++++++----- core/java/android/os/IBinder.java | 39 +++++++++++++++- core/java/android/os/RemoteCallbackList.java | 46 +++++++++++++++++-- .../bfscctestapp/BfsccTestAppCmdService.java | 3 ++ ...nderFrozenStateChangeNotificationTest.java | 4 +- .../os/BinderDeathDispatcherTest.java | 3 +- 7 files changed, 112 insertions(+), 21 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 519deac25177..bc2a25dbc877 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -33261,7 +33261,7 @@ package android.os { } public interface IBinder { - method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; + method @FlaggedApi("android.os.binder_frozen_state_change_callback") public default void addFrozenStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.os.IBinder.FrozenStateChangeCallback) throws android.os.RemoteException; method public void dump(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method public void dumpAsync(@NonNull java.io.FileDescriptor, @Nullable String[]) throws android.os.RemoteException; method @Nullable public String getInterfaceDescriptor() throws android.os.RemoteException; @@ -33885,6 +33885,7 @@ package android.os { method public void finishBroadcast(); method public Object getBroadcastCookie(int); method public E getBroadcastItem(int); + method @FlaggedApi("android.os.binder_frozen_state_change_callback") @Nullable public java.util.concurrent.Executor getExecutor(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getFrozenCalleePolicy(); method @FlaggedApi("android.os.binder_frozen_state_change_callback") public int getMaxQueueSize(); method public Object getRegisteredCallbackCookie(int); @@ -33905,6 +33906,7 @@ package android.os { @FlaggedApi("android.os.binder_frozen_state_change_callback") public static final class RemoteCallbackList.Builder { ctor public RemoteCallbackList.Builder(int); method @NonNull public android.os.RemoteCallbackList build(); + method @NonNull public android.os.RemoteCallbackList.Builder setExecutor(@NonNull java.util.concurrent.Executor); method @NonNull public android.os.RemoteCallbackList.Builder setInterfaceDiedCallback(@NonNull android.os.RemoteCallbackList.Builder.InterfaceDiedCallback); method @NonNull public android.os.RemoteCallbackList.Builder setMaxQueueSize(int); } diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index 3b5a99ed089a..01222cdd38b3 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -36,6 +36,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -651,28 +652,39 @@ public final class BinderProxy implements IBinder { private native boolean unlinkToDeathNative(DeathRecipient recipient, int flags); /** - * This list is to hold strong reference to the frozen state callbacks. The callbacks are only - * weakly referenced by JNI so the strong references here are needed to keep the callbacks - * around until the proxy is GC'ed. + * This map is to hold strong reference to the frozen state callbacks. + * + * The callbacks are only weakly referenced by JNI so the strong references here are needed to + * keep the callbacks around until the proxy is GC'ed. + * + * The key is the original callback passed into {@link #addFrozenStateChangeCallback}. The value + * is the wrapped callback created in {@link #addFrozenStateChangeCallback} to dispatch the + * calls on the desired executor. */ - private List mFrozenStateChangeCallbacks = - Collections.synchronizedList(new ArrayList<>()); + private Map mFrozenStateChangeCallbacks = + Collections.synchronizedMap(new HashMap<>()); /** * See {@link IBinder#addFrozenStateChangeCallback(FrozenStateChangeCallback)} */ - public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(Executor executor, FrozenStateChangeCallback callback) throws RemoteException { - addFrozenStateChangeCallbackNative(callback); - mFrozenStateChangeCallbacks.add(callback); + FrozenStateChangeCallback wrappedCallback = (who, state) -> + executor.execute(() -> callback.onFrozenStateChanged(who, state)); + addFrozenStateChangeCallbackNative(wrappedCallback); + mFrozenStateChangeCallbacks.put(callback, wrappedCallback); } /** * See {@link IBinder#removeFrozenStateChangeCallback} */ - public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) { - mFrozenStateChangeCallbacks.remove(callback); - return removeFrozenStateChangeCallbackNative(callback); + public boolean removeFrozenStateChangeCallback(FrozenStateChangeCallback callback) + throws IllegalArgumentException { + FrozenStateChangeCallback wrappedCallback = mFrozenStateChangeCallbacks.remove(callback); + if (wrappedCallback == null) { + throw new IllegalArgumentException("callback not found"); + } + return removeFrozenStateChangeCallbackNative(wrappedCallback); } private native void addFrozenStateChangeCallbackNative(FrozenStateChangeCallback callback) diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index a997f4c86704..8cfd32449537 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -25,6 +26,7 @@ import android.compat.annotation.UnsupportedAppUsage; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Base interface for a remotable object, the core part of a lightweight @@ -397,12 +399,31 @@ public interface IBinder { @interface State { } + /** + * Represents the frozen state of the remote process. + * + * While in this state, the remote process won't be able to receive and handle a + * transaction. Therefore, any asynchronous transactions will be buffered and delivered when + * the process is unfrozen, and any synchronous transactions will result in an error. + * + * Buffered transactions may be stale by the time that the process is unfrozen and handles + * them. To avoid overwhelming the remote process with stale events or overflowing their + * buffers, it's best to avoid sending binder transactions to a frozen process. + */ int STATE_FROZEN = 0; + + /** + * Represents the unfrozen state of the remote process. + * + * In this state, the process hosting the object can execute and is not restricted + * by the freezer from using the CPU or responding to binder transactions. + */ int STATE_UNFROZEN = 1; /** * Interface for receiving a callback when the process hosting an IBinder * has changed its frozen state. + * * @param who The IBinder whose hosting process has changed state. * @param state The latest state. */ @@ -427,15 +448,31 @@ public interface IBinder { *

You will only receive state change notifications for remote binders, as local binders by * definition can't be frozen without you being frozen too.

* + * @param executor The executor on which to run the callback. + * @param callback The callback used to deliver state change notifications. + * *

@throws {@link UnsupportedOperationException} if the kernel binder driver does not support * this feature. */ @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) - default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) + default void addFrozenStateChangeCallback( + @NonNull @CallbackExecutor Executor executor, + @NonNull FrozenStateChangeCallback callback) throws RemoteException { throw new UnsupportedOperationException(); } + /** + * Same as {@link #addFrozenStateChangeCallback(Executor, FrozenStateChangeCallback)} except + * that callbacks are invoked on a binder thread. + * + * @hide + */ + default void addFrozenStateChangeCallback(@NonNull FrozenStateChangeCallback callback) + throws RemoteException { + addFrozenStateChangeCallback(Runnable::run, callback); + } + /** * Unregister a {@link FrozenStateChangeCallback}. The callback will no longer be invoked when * the hosting process changes its frozen state. diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java index 91c482faf7d7..d5630fd46eb4 100644 --- a/core/java/android/os/RemoteCallbackList.java +++ b/core/java/android/os/RemoteCallbackList.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -29,6 +30,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executor; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -134,6 +136,7 @@ public class RemoteCallbackList { private final @FrozenCalleePolicy int mFrozenCalleePolicy; private final int mMaxQueueSize; + private final Executor mExecutor; private final class Interface implements IBinder.DeathRecipient, IBinder.FrozenStateChangeCallback { @@ -197,7 +200,7 @@ public class RemoteCallbackList { void maybeSubscribeToFrozenCallback() throws RemoteException { if (mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { try { - mBinder.addFrozenStateChangeCallback(this); + mBinder.addFrozenStateChangeCallback(mExecutor, this); } catch (UnsupportedOperationException e) { // The kernel does not support frozen notifications. In this case we want to // silently fall back to FROZEN_CALLEE_POLICY_UNSET. This is done by simply @@ -237,6 +240,7 @@ public class RemoteCallbackList { private @FrozenCalleePolicy int mFrozenCalleePolicy; private int mMaxQueueSize = DEFAULT_MAX_QUEUE_SIZE; private InterfaceDiedCallback mInterfaceDiedCallback; + private Executor mExecutor; /** * Creates a Builder for {@link RemoteCallbackList}. @@ -284,6 +288,18 @@ public class RemoteCallbackList { return this; } + /** + * Sets the executor to be used when invoking callbacks asynchronously. + * + * This is only used when callbacks need to be invoked asynchronously, e.g. when the process + * hosting a callback becomes unfrozen. Callbacks that can be invoked immediately run on the + * same thread that calls {@link #broadcast} synchronously. + */ + public @NonNull Builder setExecutor(@NonNull @CallbackExecutor Executor executor) { + mExecutor = executor; + return this; + } + /** * For notifying when the process hosting a callback interface has died. * @@ -308,15 +324,21 @@ public class RemoteCallbackList { * @return The built {@link RemoteCallbackList} object. */ public @NonNull RemoteCallbackList build() { + Executor executor = mExecutor; + if (executor == null && mFrozenCalleePolicy != FROZEN_CALLEE_POLICY_UNSET) { + // TODO Throw an exception here once the existing API caller is updated to provide + // an executor. + executor = new HandlerExecutor(Handler.getMain()); + } if (mInterfaceDiedCallback != null) { - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize) { + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor) { @Override public void onCallbackDied(E deadInterface, Object cookie) { mInterfaceDiedCallback.onInterfaceDied(this, deadInterface, cookie); } }; } - return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize); + return new RemoteCallbackList(mFrozenCalleePolicy, mMaxQueueSize, executor); } } @@ -340,6 +362,16 @@ public class RemoteCallbackList { return mMaxQueueSize; } + /** + * Returns the executor used when invoking callbacks asynchronously. + * + * @return The executor. + */ + @FlaggedApi(Flags.FLAG_BINDER_FROZEN_STATE_CHANGE_CALLBACK) + public @Nullable Executor getExecutor() { + return mExecutor; + } + /** * Creates a RemoteCallbackList with {@link #FROZEN_CALLEE_POLICY_UNSET}. This is equivalent to *

@@ -347,7 +379,7 @@ public class RemoteCallbackList {
      * 
*/ public RemoteCallbackList() { - this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE); + this(FROZEN_CALLEE_POLICY_UNSET, DEFAULT_MAX_QUEUE_SIZE, null); } /** @@ -362,10 +394,14 @@ public class RemoteCallbackList { * recipient's process is frozen. Once the limit is reached, the oldest callbacks would be * dropped to keep the size under limit. Ignored except for * {@link #FROZEN_CALLEE_POLICY_ENQUEUE_ALL}. + * + * @param executor The executor used when invoking callbacks asynchronously. */ - private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize) { + private RemoteCallbackList(@FrozenCalleePolicy int frozenCalleePolicy, int maxQueueSize, + @CallbackExecutor Executor executor) { mFrozenCalleePolicy = frozenCalleePolicy; mMaxQueueSize = maxQueueSize; + mExecutor = executor; } /** diff --git a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java index fe54aa8d87f0..945147db1ef5 100644 --- a/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java +++ b/core/tests/coretests/BinderFrozenStateChangeCallbackTestApp/src/com/android/frameworks/coretests/bfscctestapp/BfsccTestAppCmdService.java @@ -18,6 +18,8 @@ package com.android.frameworks.coretests.bfscctestapp; import android.app.Service; import android.content.Intent; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; @@ -36,6 +38,7 @@ public class BfsccTestAppCmdService extends Service { @Override public void listenTo(IBinder binder) throws RemoteException { binder.addFrozenStateChangeCallback( + new HandlerExecutor(Handler.getMain()), (IBinder who, int state) -> mNotifications.offer(state)); } diff --git a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java index 195a18a5f521..523fe1a8aa5d 100644 --- a/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java +++ b/core/tests/coretests/src/android/os/BinderFrozenStateChangeNotificationTest.java @@ -200,7 +200,7 @@ public class BinderFrozenStateChangeNotificationTest { IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> results.offer(who); try { - binder.addFrozenStateChangeCallback(callback); + binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); } catch (UnsupportedOperationException e) { return; } @@ -227,7 +227,7 @@ public class BinderFrozenStateChangeNotificationTest { final IBinder.FrozenStateChangeCallback callback = (IBinder who, int state) -> queue.offer(state == IBinder.FrozenStateChangeCallback.STATE_FROZEN); - binder.addFrozenStateChangeCallback(callback); + binder.addFrozenStateChangeCallback(new HandlerExecutor(Handler.getMain()), callback); return callback; } catch (UnsupportedOperationException e) { return null; diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java index d0070678d4fa..f888c9ba93a9 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java @@ -42,6 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.FileDescriptor; +import java.util.concurrent.Executor; @SmallTest @RunWith(AndroidJUnit4.class) @@ -125,7 +126,7 @@ public class BinderDeathDispatcherTest { } @Override - public void addFrozenStateChangeCallback(FrozenStateChangeCallback callback) + public void addFrozenStateChangeCallback(Executor e, FrozenStateChangeCallback callback) throws RemoteException { } -- GitLab From 6779e556c4fe4abc1d2c51db3144e507b182cfde Mon Sep 17 00:00:00 2001 From: Shamali Patwa Date: Mon, 18 Nov 2024 20:12:43 +0000 Subject: [PATCH 197/656] Change flag type for use_smaller_app_widget_radius flag Renamed it as purpose cannot be changed once flag is submitted. Bug: 373351337 Flag: android.appwidget.flags.use_smaller_app_widget_system_radius Change-Id: Idc49ff5afa679742b2d14681a2592a32e11e0d3f --- core/java/android/appwidget/flags.aconfig | 5 ++++- core/res/res/values/dimens.xml | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig index fb33348d9c26..4499a723cfb8 100644 --- a/core/java/android/appwidget/flags.aconfig +++ b/core/java/android/appwidget/flags.aconfig @@ -76,12 +76,15 @@ flag { } flag { - name: "use_smaller_app_widget_radius" + name: "use_smaller_app_widget_system_radius" namespace: "app_widgets" description: "Updates system corner radius for app widgets to 24.dp instead of 28.dp" bug: "373351337" is_exported: true is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 375748786817..c8df662eed6d 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -1055,12 +1055,12 @@ 280dp - 28dp - 24dp + 28dp + 24dp - 20dp + 20dp - 16dp + 16dp 16dp -- GitLab From b8082c0f05a52193e43bbf44c4a4a522db5765b1 Mon Sep 17 00:00:00 2001 From: Roshan Pius Date: Fri, 8 Nov 2024 11:44:31 -0800 Subject: [PATCH 198/656] nfc(api): Add API to set/get default NFC SubscriptionId For devices that have UICC/EUICC configured to serve as NFCEE, this API will return the subscription ID of default NFCEE. Bug: 321314635 Test: atest CtsNfcTestCases Change-Id: I9d49c807ffa28653c205855448094435c30b2f02 --- nfc/api/current.txt | 1 + nfc/api/system-current.txt | 9 +- nfc/java/android/nfc/INfcCardEmulation.aidl | 2 + .../nfc/cardemulation/CardEmulation.java | 95 +++++++++++++++++++ 4 files changed, 105 insertions(+), 2 deletions(-) diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 7ae2eafaf461..2aa73db06204 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -202,6 +202,7 @@ package android.nfc.cardemulation { method public boolean categoryAllowsForegroundPreference(String); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public java.util.List getAidsForPreferredPaymentService(); method public java.util.List getAidsForService(android.content.ComponentName, String); + method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public int getDefaultNfcSubscriptionId(); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public CharSequence getDescriptionForPreferredPaymentService(); method public static android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter); method @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public String getRouteDestinationForPreferredPaymentService(); diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 15814edcd86a..9ada14bf248f 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -186,14 +186,19 @@ package android.nfc.cardemulation { public final class CardEmulation { method @FlaggedApi("android.permission.flags.wallet_role_enabled") @Nullable @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static android.content.ComponentName getPreferredPaymentService(@NonNull android.content.Context); method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List getServices(@NonNull String, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void overrideRoutingTable(@NonNull android.app.Activity, int, int); - method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") public void recoverRoutingTable(@NonNull android.app.Activity); + method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void overrideRoutingTable(@NonNull android.app.Activity, int, int); + method @FlaggedApi("android.nfc.nfc_override_recover_routing_table") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void recoverRoutingTable(@NonNull android.app.Activity); + method @FlaggedApi("android.nfc.enable_card_emulation_euicc") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setDefaultNfcSubscriptionId(int); method @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setServiceEnabledForCategoryOther(@NonNull android.content.ComponentName, boolean); field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_ALREADY_SET = 3; // 0x3 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_FEATURE_UNSUPPORTED = 1; // 0x1 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_INVALID_SERVICE = 2; // 0x2 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_FAILURE_UNKNOWN_ERROR = 4; // 0x4 field @FlaggedApi("android.nfc.nfc_set_service_enabled_for_category_other") public static final int SET_SERVICE_ENABLED_STATUS_OK = 0; // 0x0 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; // 0x2 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; // 0x1 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; // 0x3 + field @FlaggedApi("android.nfc.enable_card_emulation_euicc") public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; // 0x0 } } diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl index 633d8bfbbb67..bb9fe959dc06 100644 --- a/nfc/java/android/nfc/INfcCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcCardEmulation.aidl @@ -53,6 +53,8 @@ interface INfcCardEmulation void overrideRoutingTable(int userHandle, String protocol, String technology, in String pkg); void recoverRoutingTable(int userHandle); boolean isEuiccSupported(); + int getDefaultNfcSubscriptionId(in String pkg); + int setDefaultNfcSubscriptionId(int subscriptionId, in String pkg); void setAutoChangeStatus(boolean state); boolean isAutoChangeEnabled(); List getRoutingStatus(); diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index e9ec7215e4d0..3ccdbe400c0c 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -22,6 +22,7 @@ import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -45,6 +46,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.Log; @@ -1010,6 +1012,7 @@ public final class CardEmulation { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) public void overrideRoutingTable( @NonNull Activity activity, @ProtocolAndTechnologyRoute int protocol, @@ -1037,6 +1040,7 @@ public final class CardEmulation { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) @FlaggedApi(Flags.FLAG_NFC_OVERRIDE_RECOVER_ROUTING_TABLE) public void recoverRoutingTable(@NonNull Activity activity) { if (!activity.isResumed()) { @@ -1057,6 +1061,97 @@ public final class CardEmulation { return callServiceReturn(() -> sService.isEuiccSupported(), false); } + /** + * Setting the default subscription ID succeeded. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_SUCCESS = 0; + + /** + * Setting the default subscription ID failed because the subscription ID is invalid. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID = 1; + + /** + * Setting the default subscription ID failed because there was an internal error processing + * the request. For ex: NFC service died in the middle of handling the API. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR = 2; + + /** + * Setting the default subscription ID failed because this feature is not supported on the + * device. + * @hide + */ + @SystemApi + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public static final int SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED = 3; + + /** @hide */ + @IntDef(prefix = "SET_SUBSCRIPTION_ID_STATUS_", + value = { + SET_SUBSCRIPTION_ID_STATUS_SUCCESS, + SET_SUBSCRIPTION_ID_STATUS_FAILED_INVALID_SUBSCRIPTION_ID, + SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR, + SET_SUBSCRIPTION_ID_STATUS_FAILED_NOT_SUPPORTED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SetSubscriptionIdStatus {} + + /** + * Sets the system's default NFC subscription id. + * + *

For devices with multiple UICC/EUICC that is configured to be NFCEE, this sets the + * default UICC NFCEE that will handle NFC offhost CE transactoions

+ * + * @param subscriptionId the default NFC subscription Id to set. + * @return status of the operation. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + * @hide + */ + @SystemApi + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public @SetSubscriptionIdStatus int setDefaultNfcSubscriptionId(int subscriptionId) { + return callServiceReturn(() -> + sService.setDefaultNfcSubscriptionId( + subscriptionId, mContext.getPackageName()), + SET_SUBSCRIPTION_ID_STATUS_FAILED_INTERNAL_ERROR); + } + + /** + * Returns the system's default NFC subscription id. + * + *

For devices with multiple UICC/EUICC that is configured to be NFCEE, this returns the + * default UICC NFCEE that will handle NFC offhost CE transactoions

+ *

If the device has no UICC that can serve as NFCEE, this will return + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.

+ * + * @return the default NFC subscription Id if set, + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} otherwise. + * + * @throws UnsupportedOperationException If the device does not have + * {@link PackageManager#FEATURE_TELEPHONY_SUBSCRIPTION}. + */ + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) + @FlaggedApi(android.nfc.Flags.FLAG_ENABLE_CARD_EMULATION_EUICC) + public int getDefaultNfcSubscriptionId() { + return callServiceReturn(() -> + sService.getDefaultNfcSubscriptionId(mContext.getPackageName()), + SubscriptionManager.INVALID_SUBSCRIPTION_ID); + } + /** * Returns the value of {@link Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT}. * -- GitLab From 1967121c24bb2101e459f462da3ad9b34c8f0ef4 Mon Sep 17 00:00:00 2001 From: Kevin Chyn Date: Mon, 18 Nov 2024 21:21:42 +0000 Subject: [PATCH 199/656] Add missing RequiresFlagsEnabled to test The bugfix for b/362914724 was merged, but the fix is only applicable for devices that have the FLAG_DEVICE_STATE_PROPERTY_MIGRATION enabled. This change gates the test with that flag. Test is properly skipped. Fixes: 376986242 Flag: EXEMPT bugfix Test: Locally disable the flag and run the test Change-Id: Ic358d2e7b627b584d444c929bc08f4b4015035cd --- .../android/server/display/LogicalDisplayMapperTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java index ff652a2727de..ad30f22fe060 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java @@ -17,6 +17,7 @@ package com.android.server.display; import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_MIGRATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY_GROUP; import static android.view.Display.FLAG_REAR; @@ -77,6 +78,9 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.test.TestLooper; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayInfo; @@ -144,6 +148,9 @@ public class LogicalDisplayMapperTest { @Rule public LocalServiceKeeperRule mLocalServiceKeeperRule = new LocalServiceKeeperRule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Mock LogicalDisplayMapper.Listener mListenerMock; @Mock Context mContextMock; @@ -691,6 +698,7 @@ public class LogicalDisplayMapperTest { } @Test + @RequiresFlagsEnabled(FLAG_DEVICE_STATE_PROPERTY_MIGRATION) public void testDeviceShouldNotBeWokenWhenExitingEmulatedState() { assertFalse(mLogicalDisplayMapper.shouldDeviceBeWoken(DEVICE_STATE_OPEN, DEVICE_STATE_EMULATED, -- GitLab From fa5cd980b906f5dd1e361ecb1b3ca5e298535b35 Mon Sep 17 00:00:00 2001 From: Ted Bauer Date: Mon, 18 Nov 2024 21:20:50 +0000 Subject: [PATCH 200/656] Add wear_watchfaces to SettingsToPropertiesMapper Bug: 378851402 Change-Id: Id70899d3a7cb02a2377f36f880bb5ab1ed1930af Test: m Flag: EXEMPT flag infra onboarding --- .../java/com/android/server/am/SettingsToPropertiesMapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index ef5296eef492..78c4f74f3afa 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -257,6 +257,7 @@ public class SettingsToPropertiesMapper { "wear_systems", "wear_sysui", "wear_system_managed_surfaces", + "wear_watchfaces", "window_surfaces", "windowing_frontend", "xr", -- GitLab From dcb328a68c2c137853d8b145637eda2e332ffad3 Mon Sep 17 00:00:00 2001 From: Ikram Gabiyev Date: Mon, 18 Nov 2024 13:17:19 -0800 Subject: [PATCH 201/656] Override flag in PipController/PipTaskOrg.Test PipController and PipTaskOrganizer in pip1 package check for whether PiP2 flag is off before registering certain listeners. These calls are actually checked through verify() assertions in their respective unit tests. So we should override the PiP2 flag to be disabled for these unit tests (they still run but with enable_pip2=false always). Also added myself to the list OWNERS in the wm.shell tests package. Bug: 375599908 Flag: com.android.wm.shell.enable_pip2 Test: atest WMShellUnitTests Change-Id: Iba57605d83c4e182dd30f06a34f534b70a938c2c --- libs/WindowManager/Shell/tests/OWNERS | 1 + .../android/wm/shell/pip/PipTaskOrganizerTest.java | 11 +++++++++++ .../android/wm/shell/pip/phone/PipControllerTest.java | 9 +++++++++ 3 files changed, 21 insertions(+) diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS index 65e50f86e8fe..19829e7e5677 100644 --- a/libs/WindowManager/Shell/tests/OWNERS +++ b/libs/WindowManager/Shell/tests/OWNERS @@ -6,6 +6,7 @@ pablogamito@google.com lbill@google.com madym@google.com hwwang@google.com +gabiyev@google.com chenghsiuchang@google.com atsjenk@google.com jorgegil@google.com diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index bcb7461bfae7..70e0f53d537e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -36,6 +36,8 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Rational; @@ -46,6 +48,7 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; +import com.android.wm.shell.Flags; import com.android.wm.shell.MockSurfaceControlHelper; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; @@ -65,6 +68,8 @@ import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.splitscreen.SplitScreenController; import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; @@ -79,7 +84,13 @@ import java.util.Optional; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper +@DisableFlags(Flags.FLAG_ENABLE_PIP2) public class PipTaskOrganizerTest extends ShellTestCase { + @ClassRule + public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule(); + private PipTaskOrganizer mPipTaskOrganizer; @Mock private DisplayController mMockDisplayController; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 96003515a485..b123f4dfac9e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -42,11 +42,14 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.RemoteException; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.wm.shell.Flags; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; @@ -74,6 +77,8 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -88,7 +93,11 @@ import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper +@DisableFlags(Flags.FLAG_ENABLE_PIP2) public class PipControllerTest extends ShellTestCase { + @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule(); + @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule(); + private PipController mPipController; private ShellInit mShellInit; private ShellController mShellController; -- GitLab From 08c01a33df4b4b419678c1af2d34c6d49896d7d7 Mon Sep 17 00:00:00 2001 From: Michael Mikhail Date: Wed, 6 Nov 2024 14:40:41 +0000 Subject: [PATCH 202/656] Add expansion animation for ringer drawer Flag: com.android.systemui.volume_redesign Bug: 369995871 Test: Checked UI Change-Id: I7722f7eef4763c3511120697d6da5fd0487f1f42 --- .../SystemUI/res/layout/volume_dialog.xml | 4 +- .../res/layout/volume_ringer_button.xml | 2 + .../res/layout/volume_ringer_drawer.xml | 55 +----- ...lume_dialog_ringer_drawer_motion_scene.xml | 49 +++++ .../ui/binder/VolumeDialogRingerViewBinder.kt | 172 ++++++++++++++---- .../VolumeDialogRingerDrawerViewModel.kt | 4 +- 6 files changed, 201 insertions(+), 85 deletions(-) create mode 100644 packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 694357d534fb..b8544a64d9da 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -31,10 +31,10 @@ app:layout_constraintBottom_toBottomOf="@id/volume_dialog_settings" app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container" app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container" - app:layout_constraintTop_toTopOf="@id/volume_ringer_and_drawer_container" /> + app:layout_constraintTop_toTopOf="@id/volume_ringer_drawer" /> @@ -25,6 +26,7 @@ android:layout_marginBottom="@dimen/volume_dialog_components_spacing" android:contentDescription="@string/volume_ringer_mode" android:gravity="center" + android:tint="?androidprv:attr/materialColorOnSurface" android:src="@drawable/volume_ringer_item_bg" android:background="@drawable/volume_ringer_item_bg"/> diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml index b71c4700c0fa..d850bbe63afd 100644 --- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml +++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml @@ -14,55 +14,18 @@ ~ limitations under the License. --> - + android:layoutDirection="ltr" + android:orientation="vertical" + app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene"> - - + - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml b/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml new file mode 100644 index 000000000000..877637e0b0d8 --- /dev/null +++ b/packages/SystemUI/res/xml/volume_dialog_ringer_drawer_motion_scene.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt index c4b028d7d98b..1963ba22d444 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt @@ -18,14 +18,18 @@ package com.android.systemui.volume.dialog.ringer.ui.binder import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.widget.ImageButton import androidx.annotation.LayoutRes import androidx.compose.ui.util.fastForEachIndexed +import androidx.constraintlayout.motion.widget.MotionLayout +import androidx.constraintlayout.widget.ConstraintSet +import com.android.internal.R as internalR +import com.android.settingslib.Utils import com.android.systemui.lifecycle.WindowLifecycleState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.viewModel import com.android.systemui.res.R +import com.android.systemui.util.children import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerButtonViewModel import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerDrawerState @@ -33,7 +37,6 @@ import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModel import com.android.systemui.volume.dialog.ringer.ui.viewmodel.RingerViewModelState import com.android.systemui.volume.dialog.ringer.ui.viewmodel.VolumeDialogRingerDrawerViewModel import javax.inject.Inject -import kotlin.math.abs import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -44,12 +47,8 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact fun bind(view: View) { with(view) { - val drawerAndRingerContainer = - requireViewById(R.id.volume_ringer_and_drawer_container) - val drawerContainer = requireViewById(R.id.volume_drawer_container) - val selectedButtonView = - requireViewById(R.id.volume_new_ringer_active_button) val volumeDialogBackgroundView = requireViewById(R.id.volume_dialog_background) + val drawerContainer = requireViewById(R.id.volume_ringer_drawer) repeatWhenAttached { viewModel( traceName = "VolumeDialogRingerViewBinder", @@ -62,29 +61,26 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact is RingerViewModelState.Available -> { val uiModel = ringerState.uiModel - bindSelectedButton(viewModel, uiModel, selectedButtonView) - bindDrawerButtons(viewModel, uiModel.availableButtons) + bindDrawerButtons(viewModel, uiModel) - // Set up views background and visibility - drawerAndRingerContainer.visibility = View.VISIBLE + // Set up view background and visibility + drawerContainer.visibility = View.VISIBLE when (uiModel.drawerState) { is RingerDrawerState.Initial -> { - drawerContainer.visibility = View.GONE - selectedButtonView.visibility = View.VISIBLE + drawerContainer.closeDrawer(uiModel.currentButtonIndex) volumeDialogBackgroundView.setBackgroundResource( R.drawable.volume_dialog_background ) } is RingerDrawerState.Closed -> { - drawerContainer.visibility = View.GONE - selectedButtonView.visibility = View.VISIBLE + drawerContainer.closeDrawer(uiModel.currentButtonIndex) volumeDialogBackgroundView.setBackgroundResource( R.drawable.volume_dialog_background ) } is RingerDrawerState.Open -> { - drawerContainer.visibility = View.VISIBLE - selectedButtonView.visibility = View.GONE + // Open drawer + drawerContainer.transitionToEnd() if ( uiModel.currentButtonIndex != uiModel.availableButtons.size - 1 @@ -97,7 +93,7 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact } } is RingerViewModelState.Unavailable -> { - drawerAndRingerContainer.visibility = View.GONE + drawerContainer.visibility = View.GONE volumeDialogBackgroundView.setBackgroundResource( R.drawable.volume_dialog_background ) @@ -112,15 +108,21 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact private fun View.bindDrawerButtons( viewModel: VolumeDialogRingerDrawerViewModel, - availableButtons: List, + uiModel: RingerViewModel, ) { - val drawerOptions = requireViewById(R.id.volume_drawer_options) - val count = availableButtons.size - drawerOptions.ensureChildCount(R.layout.volume_ringer_button, count) + val drawerContainer = requireViewById(R.id.volume_ringer_drawer) + val count = uiModel.availableButtons.size + drawerContainer.ensureChildCount(R.layout.volume_ringer_button, count) - availableButtons.fastForEachIndexed { index, ringerButton -> + uiModel.availableButtons.fastForEachIndexed { index, ringerButton -> ringerButton?.let { - drawerOptions.getChildAt(count - index - 1).bindDrawerButton(it, viewModel) + val view = drawerContainer.getChildAt(count - index - 1) + // TODO (b/369995871): object animator for button switch ( active <-> inactive ) + if (index == uiModel.currentButtonIndex) { + view.bindDrawerButton(uiModel.selectedButton, viewModel, isSelected = true) + } else { + view.bindDrawerButton(it, viewModel) + } } } } @@ -128,15 +130,29 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact private fun View.bindDrawerButton( buttonViewModel: RingerButtonViewModel, viewModel: VolumeDialogRingerDrawerViewModel, + isSelected: Boolean = false, ) { with(requireViewById(R.id.volume_drawer_button)) { setImageResource(buttonViewModel.imageResId) contentDescription = context.getString(buttonViewModel.contentDescriptionResId) - setOnClickListener { viewModel.onRingerButtonClicked(buttonViewModel.ringerMode) } + if (isSelected) { + setBackgroundResource(R.drawable.volume_drawer_selection_bg) + setColorFilter( + Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnPrimary) + ) + } else { + setBackgroundResource(R.drawable.volume_ringer_item_bg) + setColorFilter( + Utils.getColorAttrDefaultColor(context, internalR.attr.materialColorOnSurface) + ) + } + setOnClickListener { + viewModel.onRingerButtonClicked(buttonViewModel.ringerMode, isSelected) + } } } - private fun ViewGroup.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) { + private fun MotionLayout.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) { val childCountDelta = childCount - count when { childCountDelta > 0 -> { @@ -144,21 +160,107 @@ constructor(private val viewModelFactory: VolumeDialogRingerDrawerViewModel.Fact } childCountDelta < 0 -> { val inflater = LayoutInflater.from(context) - repeat(abs(childCountDelta)) { inflater.inflate(viewLayoutId, this, true) } + repeat(-childCountDelta) { + inflater.inflate(viewLayoutId, this, true) + getChildAt(childCount - 1).id = View.generateViewId() + } + cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open) + .adjustOpenConstraintsForDrawer(this) } } } - private fun bindSelectedButton( - viewModel: VolumeDialogRingerDrawerViewModel, - uiModel: RingerViewModel, - selectedButtonView: ImageButton, + private fun MotionLayout.closeDrawer(selectedIndex: Int) { + cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close) + .adjustClosedConstraintsForDrawer(selectedIndex, this) + transitionToStart() + } + + private fun ConstraintSet.adjustOpenConstraintsForDrawer(motionLayout: MotionLayout) { + motionLayout.children.forEachIndexed { index, button -> + setButtonPositionConstraints(motionLayout, index, button) + setAlpha(button.id, 1.0F) + constrainWidth( + button.id, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_ringer_drawer_button_size + ), + ) + constrainHeight( + button.id, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_ringer_drawer_button_size + ), + ) + if (index != motionLayout.childCount - 1) { + setMargin( + button.id, + ConstraintSet.BOTTOM, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_components_spacing + ), + ) + } + } + motionLayout.updateState(R.id.volume_dialog_ringer_drawer_open, this) + } + + private fun ConstraintSet.adjustClosedConstraintsForDrawer( + selectedIndex: Int, + motionLayout: MotionLayout, ) { - with(uiModel) { - selectedButtonView.setImageResource(selectedButton.imageResId) - selectedButtonView.setOnClickListener { - viewModel.onRingerButtonClicked(selectedButton.ringerMode) + motionLayout.children.forEachIndexed { index, button -> + setButtonPositionConstraints(motionLayout, index, button) + constrainWidth( + button.id, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_ringer_drawer_button_size + ), + ) + if (selectedIndex != motionLayout.childCount - index - 1) { + setAlpha(button.id, 0.0F) + constrainHeight(button.id, 0) + setMargin(button.id, ConstraintSet.BOTTOM, 0) + } else { + setAlpha(button.id, 1.0F) + constrainHeight( + button.id, + motionLayout.context.resources.getDimensionPixelSize( + R.dimen.volume_dialog_ringer_drawer_button_size + ), + ) } } + motionLayout.updateState(R.id.volume_dialog_ringer_drawer_close, this) + } + + private fun ConstraintSet.setButtonPositionConstraints( + motionLayout: MotionLayout, + index: Int, + button: View, + ) { + if (motionLayout.getChildAt(index - 1) == null) { + connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP) + } else { + connect( + button.id, + ConstraintSet.TOP, + motionLayout.getChildAt(index - 1).id, + ConstraintSet.BOTTOM, + ) + } + + if (motionLayout.getChildAt(index + 1) == null) { + connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM) + } else { + connect( + button.id, + ConstraintSet.BOTTOM, + motionLayout.getChildAt(index + 1).id, + ConstraintSet.TOP, + ) + } + connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START) + connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt index e040638324ac..624dcc71e2a9 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt @@ -84,8 +84,8 @@ constructor( .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) .build() - fun onRingerButtonClicked(ringerMode: RingerMode) { - if (drawerState.value is RingerDrawerState.Open) { + fun onRingerButtonClicked(ringerMode: RingerMode, isSelectedButton: Boolean = false) { + if (drawerState.value is RingerDrawerState.Open && !isSelectedButton) { Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value) provideTouchFeedback(ringerMode) maybeShowToast(ringerMode) -- GitLab From 967518d7a5ce5f4bf8cbac47390d9a25a4f3e0b4 Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Mon, 18 Nov 2024 13:39:46 -0800 Subject: [PATCH 203/656] [flexiglass] Remove Lockscreen scene from backstack when face unlocked ...even if the alternate bouncer becomes invisible earlier than when the device becomes unlocked. There's a race condition between face unlock and alternate bouncer visibility which basically causes the successful device unlock to hide the alternate bouncer before the code in SceneContainerStartable is hit. This leads to leaving the Lockscreen scene in the navigation back stack even though it shouldn't be there. This CL fixes that issue by just replacing the Lockscreen scene in the bottom position of the navigation back stack when the device is unlocked, regardless of whether the alternate bouncer was visible. Fix: 375191368 Test: unit test added; older tests pass Test: manually verified that the bug could no longer reproduce on a UDFPS device Flag: com.android.systemui.scene_container Change-Id: Ie34dd753ef947c197282c9e794aebcd5102c66d8 --- .../startable/SceneContainerStartableTest.kt | 77 ++++++++++++++++--- .../startable/SceneContainerStartable.kt | 25 +++--- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index af0274b1caaa..2940158d8fb4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -2411,6 +2411,64 @@ class SceneContainerStartableTest : SysuiTestCase() { assertThat(isAlternateBouncerVisible).isFalse() } + @Test + fun replacesLockscreenSceneOnBackStack_whenFaceUnlocked_fromShade_noAlternateBouncer() = + testScope.runTest { + val transitionState = + prepareState( + isDeviceUnlocked = false, + initialSceneKey = Scenes.Lockscreen, + authenticationMethod = AuthenticationMethodModel.Pin, + ) + underTest.start() + + val isUnlocked by + collectLastValue( + kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked } + ) + val currentScene by collectLastValue(sceneInteractor.currentScene) + val backStack by collectLastValue(sceneBackInteractor.backStack) + val isAlternateBouncerVisible by + collectLastValue(kosmos.alternateBouncerInteractor.isVisible) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Change to shade. + sceneInteractor.changeScene(Scenes.Shade, "") + transitionState.value = ObservableTransitionState.Idle(Scenes.Shade) + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Show the alternate bouncer. + kosmos.alternateBouncerInteractor.forceShow() + kosmos.sysuiStatusBarStateController.leaveOpen = true // leave shade open + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isTrue() + + // Simulate race condition by hiding the alternate bouncer *before* the face unlock: + kosmos.alternateBouncerInteractor.hide() + runCurrent() + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Lockscreen) + assertThat(isAlternateBouncerVisible).isFalse() + + // Trigger a face unlock. + updateFaceAuthStatus(isSuccess = true) + runCurrent() + assertThat(isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(Scenes.Shade) + assertThat(backStack?.asIterable()?.first()).isEqualTo(Scenes.Gone) + assertThat(isAlternateBouncerVisible).isFalse() + } + private fun TestScope.emulateSceneTransition( transitionStateFlow: MutableStateFlow, toScene: SceneKey, @@ -2647,15 +2705,16 @@ class SceneContainerStartableTest : SysuiTestCase() { } private fun updateFaceAuthStatus(isSuccess: Boolean) { - if (isSuccess) { - kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus( - SuccessFaceAuthenticationStatus( - successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java) - ) - ) - } else { - kosmos.fakeDeviceEntryFaceAuthRepository.setAuthenticationStatus( - FailedFaceAuthenticationStatus() + with(kosmos.fakeDeviceEntryFaceAuthRepository) { + isAuthenticated.value = isSuccess + setAuthenticationStatus( + if (isSuccess) { + SuccessFaceAuthenticationStatus( + successResult = Mockito.mock(FaceManager.AuthenticationResult::class.java) + ) + } else { + FailedFaceAuthenticationStatus() + } ) } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index daeaaa52fd94..fe322dc520fa 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -417,8 +417,13 @@ constructor( "mechanism: ${deviceUnlockStatus.deviceUnlockSource}" else -> null } - // Not on lockscreen or bouncer, so remain in the current scene. - else -> null + // Not on lockscreen or bouncer, so remain in the current scene but since + // unlocked, replace the Lockscreen scene from the bottom of the navigation + // back stack with the Gone scene. + else -> { + replaceLockscreenSceneOnBackStack() + null + } } } .collect { (targetSceneKey, loggingReason) -> @@ -427,17 +432,19 @@ constructor( } } - /** If the [Scenes.Lockscreen] is on the backstack, replaces it with [Scenes.Gone]. */ + /** + * If the [Scenes.Lockscreen] is on the bottom of the navigation backstack, replaces it with + * [Scenes.Gone]. + */ private fun replaceLockscreenSceneOnBackStack() { sceneBackInteractor.updateBackStack { stack -> val list = stack.asIterable().toMutableList() - check(list.last() == Scenes.Lockscreen) { - "The bottommost/last SceneKey of the back stack isn't" + - " the Lockscreen scene like expected. The back" + - " stack is $stack." + if (list.lastOrNull() == Scenes.Lockscreen) { + list[list.size - 1] = Scenes.Gone + sceneStackOf(*list.toTypedArray()) + } else { + stack } - list[list.size - 1] = Scenes.Gone - sceneStackOf(*list.toTypedArray()) } } -- GitLab From 31e508495d7e219c8efee84fd598b3df7c7810e4 Mon Sep 17 00:00:00 2001 From: Darrell Shi Date: Fri, 15 Nov 2024 16:23:29 -0800 Subject: [PATCH 204/656] Fix dream overlay lifecycle when bouncer is showing in scene container The DreamOverlayService observes bouncer showing state to update its lifecycle so touch monitor is correctly started/stopped. This change makes it so this state is correctly handled when scene container is enabled. Test: atest DreamOverlayServiceTest Test: touch monitor stopped when bouncer is showing over dream when scene container is enabled Fix: 379179472 Flag: com.android.systemui.scene_container Change-Id: I3d6271c29db5b8059007b77993fd1b1252691edc --- .../dreams/DreamOverlayServiceTest.kt | 34 +++++++++++++++++ .../systemui/dreams/DreamOverlayService.java | 37 +++++++++++++------ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index a65e7ed48797..bbfc9607fed9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -1035,6 +1035,7 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } + @DisableFlags(FLAG_SCENE_CONTAINER) @Test fun testBouncerShown_setsLifecycleState() { val client = client @@ -1067,6 +1068,39 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() { assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) } + @EnableFlags(FLAG_SCENE_CONTAINER) + @Test + fun testBouncerShown_withSceneContainer_setsLifecycleState() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*isPreview*/, + false, /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) + + // Bouncer shows. + kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "test") + testScope.runCurrent() + mMainExecutor.runAllReady() + + // Lifecycle state goes from resumed back to started when the bouncer shows. + assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.STARTED) + + // Bouncer closes. + kosmos.sceneInteractor.changeScene(Scenes.Dream, "test") + testScope.runCurrent() + mMainExecutor.runAllReady() + + // Lifecycle state goes back to RESUMED. + assertThat(lifecycleRegistry.currentState).isEqualTo(Lifecycle.State.RESUMED) + } + @Test @DisableFlags(FLAG_SCENE_CONTAINER) fun testCommunalVisible_setsLifecycleState() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index b330ba376810..408fe831d74c 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -47,6 +47,7 @@ import androidx.lifecycle.ServiceLifecycleDispatcher; import androidx.lifecycle.ViewModelStore; import com.android.app.viewcapture.ViewCaptureAwareWindowManager; +import com.android.compose.animation.scene.SceneKey; import com.android.dream.lowlight.dagger.LowLightDreamModule; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -212,16 +213,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final Consumer mBouncerShowingConsumer = new Consumer<>() { @Override public void accept(Boolean bouncerShowing) { - mExecutor.execute(() -> { - if (mBouncerShowing == bouncerShowing) { - return; - } - - mBouncerShowing = bouncerShowing; + mExecutor.execute(() -> updateBouncerShowingLocked(bouncerShowing)); + } + }; - updateLifecycleStateLocked(); - updateGestureBlockingLocked(); - }); + private final Consumer mCurrentSceneConsumer = new Consumer<>() { + @Override + public void accept(SceneKey currentScene) { + mExecutor.execute(() -> updateBouncerShowingLocked(currentScene == Scenes.Bouncer)); } }; @@ -425,8 +424,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mIsCommunalAvailableCallback)); mFlows.add(collectFlow(getLifecycle(), communalInteractor.isCommunalVisible(), mCommunalVisibleConsumer)); - mFlows.add(collectFlow(getLifecycle(), keyguardInteractor.primaryBouncerShowing, - mBouncerShowingConsumer)); + if (SceneContainerFlag.isEnabled()) { + mFlows.add(collectFlow(getLifecycle(), sceneInteractor.getCurrentScene(), + mCurrentSceneConsumer)); + } else { + mFlows.add(collectFlow(getLifecycle(), keyguardInteractor.primaryBouncerShowing, + mBouncerShowingConsumer)); + } } @NonNull @@ -707,4 +711,15 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Log.w(TAG, "Removing dream overlay container view parent!"); parentView.removeView(containerView); } + + private void updateBouncerShowingLocked(boolean bouncerShowing) { + if (mBouncerShowing == bouncerShowing) { + return; + } + + mBouncerShowing = bouncerShowing; + + updateLifecycleStateLocked(); + updateGestureBlockingLocked(); + } } -- GitLab From 65b4b9ef8ccc8ca013449ac4fac34a7fcd78c754 Mon Sep 17 00:00:00 2001 From: David Liu Date: Mon, 18 Nov 2024 22:09:11 +0000 Subject: [PATCH 205/656] [Expressive design] IntroPreference should not be round corner Bug: 377266201 Test: visual test Flag: EXEMPT update library Change-Id: I62aae4228be42391a0d22a22b5e39dc82a8ade29 --- .../src/com/android/settingslib/widget/IntroPreference.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt index 0cdfc6610bbd..81337612e59a 100644 --- a/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt +++ b/packages/SettingsLib/IntroPreference/src/com/android/settingslib/widget/IntroPreference.kt @@ -31,7 +31,7 @@ class IntroPreference @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0 -) : Preference(context, attrs, defStyleAttr, defStyleRes) { +) : Preference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin { private var isCollapsable: Boolean = false private var minLines: Int = 2 -- GitLab From fbb249ef243b5a2430402e7fce39dc74680dc1f4 Mon Sep 17 00:00:00 2001 From: ziyiw Date: Mon, 18 Nov 2024 22:22:52 +0000 Subject: [PATCH 206/656] [framework] Add more description to routingTable entry. Bug: 377887955 Test: atest CtsNfcTestCases Change-Id: I639c6e6136a453ebd732790629729e43bde5979b --- nfc/api/system-current.txt | 5 ++ .../android/nfc/NfcRoutingTableEntry.java | 47 ++++++++++++++++++- .../android/nfc/RoutingTableAidEntry.java | 4 +- .../nfc/RoutingTableProtocolEntry.java | 2 +- .../nfc/RoutingTableSystemCodeEntry.java | 6 ++- .../nfc/RoutingTableTechnologyEntry.java | 15 ++++-- 6 files changed, 68 insertions(+), 11 deletions(-) diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 15814edcd86a..95f7d09931fb 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -120,6 +120,11 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public abstract class NfcRoutingTableEntry { method public int getNfceeId(); + method public int getType(); + field public static final int TYPE_AID = 0; // 0x0 + field public static final int TYPE_PROTOCOL = 1; // 0x1 + field public static final int TYPE_SYSTEM_CODE = 3; // 0x3 + field public static final int TYPE_TECHNOLOGY = 2; // 0x2 } @FlaggedApi("android.nfc.nfc_oem_extension") public final class OemLogItems implements android.os.Parcelable { diff --git a/nfc/java/android/nfc/NfcRoutingTableEntry.java b/nfc/java/android/nfc/NfcRoutingTableEntry.java index 4e913776a030..c2cbbede9b75 100644 --- a/nfc/java/android/nfc/NfcRoutingTableEntry.java +++ b/nfc/java/android/nfc/NfcRoutingTableEntry.java @@ -17,8 +17,12 @@ package android.nfc; import android.annotation.FlaggedApi; +import android.annotation.IntDef; import android.annotation.SystemApi; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Class to represent an entry of routing table. This class is abstract and extended by * {@link RoutingTableTechnologyEntry}, {@link RoutingTableProtocolEntry}, @@ -30,10 +34,42 @@ import android.annotation.SystemApi; @SystemApi public abstract class NfcRoutingTableEntry { private final int mNfceeId; + private final int mType; + + /** + * AID routing table type. + */ + public static final int TYPE_AID = 0; + /** + * Protocol routing table type. + */ + public static final int TYPE_PROTOCOL = 1; + /** + * Technology routing table type. + */ + public static final int TYPE_TECHNOLOGY = 2; + /** + * System Code routing table type. + */ + public static final int TYPE_SYSTEM_CODE = 3; + + /** + * Possible type of this routing table entry. + * @hide + */ + @IntDef(prefix = "TYPE_", value = { + TYPE_AID, + TYPE_PROTOCOL, + TYPE_TECHNOLOGY, + TYPE_SYSTEM_CODE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RoutingTableType {} /** @hide */ - protected NfcRoutingTableEntry(int nfceeId) { + protected NfcRoutingTableEntry(int nfceeId, @RoutingTableType int type) { mNfceeId = nfceeId; + mType = type; } /** @@ -43,4 +79,13 @@ public abstract class NfcRoutingTableEntry { public int getNfceeId() { return mNfceeId; } + + /** + * Get the type of this entry. + * @return an integer defined in {@link RoutingTableType} + */ + @RoutingTableType + public int getType() { + return mType; + } } diff --git a/nfc/java/android/nfc/RoutingTableAidEntry.java b/nfc/java/android/nfc/RoutingTableAidEntry.java index 7634fe342ab9..bf697d662bd6 100644 --- a/nfc/java/android/nfc/RoutingTableAidEntry.java +++ b/nfc/java/android/nfc/RoutingTableAidEntry.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; /** - * Represents an AID entry in current routing table. + * Represents an Application ID (AID) entry in current routing table. * @hide */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -30,7 +30,7 @@ public class RoutingTableAidEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableAidEntry(int nfceeId, String value) { - super(nfceeId); + super(nfceeId, TYPE_AID); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableProtocolEntry.java b/nfc/java/android/nfc/RoutingTableProtocolEntry.java index 0c5be7dd9d97..536de4d7430e 100644 --- a/nfc/java/android/nfc/RoutingTableProtocolEntry.java +++ b/nfc/java/android/nfc/RoutingTableProtocolEntry.java @@ -97,7 +97,7 @@ public class RoutingTableProtocolEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableProtocolEntry(int nfceeId, @ProtocolValue int value) { - super(nfceeId); + super(nfceeId, TYPE_PROTOCOL); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java index f87ad5f95195..f61892d31668 100644 --- a/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java +++ b/nfc/java/android/nfc/RoutingTableSystemCodeEntry.java @@ -20,7 +20,9 @@ import android.annotation.NonNull; import android.annotation.SystemApi; /** - * Represents a system code entry in current routing table. + * Represents a system code entry in current routing table, where system codes are two-byte values + * used in NFC-F technology (a type of NFC communication) to identify specific + * device configurations. * @hide */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) @@ -30,7 +32,7 @@ public class RoutingTableSystemCodeEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableSystemCodeEntry(int nfceeId, byte[] value) { - super(nfceeId); + super(nfceeId, TYPE_SYSTEM_CODE); this.mValue = value; } diff --git a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java index f51a5299be11..2dbc94232b0b 100644 --- a/nfc/java/android/nfc/RoutingTableTechnologyEntry.java +++ b/nfc/java/android/nfc/RoutingTableTechnologyEntry.java @@ -30,22 +30,27 @@ import java.lang.annotation.RetentionPolicy; @SystemApi public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry { /** - * Technology-A + * Technology-A. + *

Tech-A is mostly used for payment and ticketing applications. It supports various + * Tag platforms including Type 1, Type 2 and Type 4A tags.

*/ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_A = 0; /** - * Technology-B + * Technology-B which is based on ISO/IEC 14443-3 standard. */ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_B = 1; /** - * Technology-F + * Technology-F. + *

Tech-F is a standard which supports Type 3 Tags and NFC-DEP protocol etc.

*/ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_F = 2; /** - * Technology-V + * Technology-V. + *

Tech-V is an NFC technology used for communication with passive tags that operate + * at a longer range than other NFC technologies.

*/ @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) public static final int TECHNOLOGY_V = 3; @@ -73,7 +78,7 @@ public class RoutingTableTechnologyEntry extends NfcRoutingTableEntry { /** @hide */ public RoutingTableTechnologyEntry(int nfceeId, @TechnologyValue int value) { - super(nfceeId); + super(nfceeId, TYPE_TECHNOLOGY); this.mValue = value; } -- GitLab From 2f50009c19955fe0cad4868182c289a65bdd0a5c Mon Sep 17 00:00:00 2001 From: Brad Ebinger Date: Mon, 18 Nov 2024 20:32:33 +0000 Subject: [PATCH 207/656] Deprecate IMS TelephonyManager methods Deprecate the IMS methods in TelephonyManager and update docs to point users to the correct versions of those methods in ImsMmTelManager. Change-Id: Icfe4593036251a894824c042a29ead4af3c1ad2a Flag: EXEMPT dics only change Fixes: 316231661 Test: docs only change - presubmit --- .../android/telephony/TelephonyManager.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 6f2c8623fd71..9c961c1f0df0 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -12239,9 +12239,10 @@ public class TelephonyManager { * @param subId Subscription ID * @return true if IMS status is registered, false if the IMS status is not registered or a * RemoteException occurred. - * Use {@link ImsMmTelManager.RegistrationCallback} instead. * @hide + * @deprecated Use {@link ImsMmTelManager#getRegistrationState(Executor, Consumer)} instead. */ + @Deprecated public boolean isImsRegistered(int subId) { try { return getITelephony().isImsRegistered(subId); @@ -12259,8 +12260,10 @@ public class TelephonyManager { * @return true if IMS status is registered, false if the IMS status is not registered or a * RemoteException occurred. * @see SubscriptionManager#getDefaultSubscriptionId() + * @deprecated Use {@link ImsMmTelManager#getRegistrationState(Executor, Consumer)} instead. * @hide */ + @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public boolean isImsRegistered() { try { @@ -12277,9 +12280,10 @@ public class TelephonyManager { * @return true if Voice over LTE is available or false if it is unavailable or unknown. * @see SubscriptionManager#getDefaultSubscriptionId() *

- * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @Deprecated Use {@link ImsMmTelManager#isAvailable(int, int)} instead. * @hide */ + @Deprecated @UnsupportedAppUsage public boolean isVolteAvailable() { try { @@ -12297,9 +12301,10 @@ public class TelephonyManager { * used during creation, the default subscription ID will be used. To query the * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}. * @return true if VT is available, or false if it is unavailable or unknown. - * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @Deprecated Use {@link ImsMmTelManager#isAvailable(int, int)} instead. * @hide */ + @Deprecated @UnsupportedAppUsage public boolean isVideoTelephonyAvailable() { try { @@ -12313,9 +12318,10 @@ public class TelephonyManager { * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified. * @param subId the subscription ID. * @return true if VoWiFi is available, or false if it is unavailable or unknown. - * Use {@link ImsMmTelManager#isAvailable(int, int)} instead. + * @Deprecated Use {@link ImsMmTelManager#isAvailable(int, int)} instead. * @hide */ + @Deprecated @UnsupportedAppUsage public boolean isWifiCallingAvailable() { try { @@ -12336,9 +12342,11 @@ public class TelephonyManager { * other sim's internet, or * - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the * result is unavailable. - * Use {@link ImsMmTelManager.RegistrationCallback} instead. + * @Deprecated Use {@link ImsMmTelManager#registerImsRegistrationCallback(Executor, RegistrationCallback)} + * or {@link ImsMmTelManager#getRegistrationTransportType(Executor, Consumer)} instead. * @hide */ + @Deprecated public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() { try { return getITelephony().getImsRegTechnologyForMmTel(getSubId()); -- GitLab From 91053240b31b9544eca3530d6120ec8d4fa2a365 Mon Sep 17 00:00:00 2001 From: Jesse Melhuish Date: Wed, 30 Oct 2024 21:53:59 +0000 Subject: [PATCH 208/656] CarrierMessagingService: Add new enum values for granular errors New result codes for outbound SMS send requests, outbound MMS requests, and inbound MMS requests that correspond to SmsManager codes for the respective request type. Bug: 326610112 Test: atest CtsTelephonyTestCases Flag: com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service Change-Id: I9503d2da34c9ae7e9b5e179f5bedc592d729ca27 --- core/api/current.txt | 43 ++ .../carrier/CarrierMessagingService.java | 450 +++++++++++++++++- 2 files changed, 481 insertions(+), 12 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 005391f86810..76f3e5a3cd39 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -41181,6 +41181,18 @@ package android.service.carrier { method @Deprecated public void onSendTextSms(@NonNull String, int, @NonNull String, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback); method public void onSendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingService.ResultCallback); field public static final int DOWNLOAD_STATUS_ERROR = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606; // 0x25e + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610; // 0x262 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603; // 0x25b + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609; // 0x261 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601; // 0x259 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608; // 0x260 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604; // 0x25c + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611; // 0x263 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607; // 0x25f + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605; // 0x25d + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602; // 0x25a + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600; // 0x258 field public static final int DOWNLOAD_STATUS_OK = 0; // 0x0 field public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1 field public static final int RECEIVE_OPTIONS_DEFAULT = 0; // 0x0 @@ -41188,7 +41200,38 @@ package android.service.carrier { field public static final int RECEIVE_OPTIONS_SKIP_NOTIFY_WHEN_CREDENTIAL_PROTECTED_STORAGE_UNAVAILABLE = 2; // 0x2 field public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1; // 0x1 field public static final int SEND_STATUS_ERROR = 2; // 0x2 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406; // 0x196 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410; // 0x19a + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403; // 0x193 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409; // 0x199 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401; // 0x191 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408; // 0x198 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404; // 0x194 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411; // 0x19b + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407; // 0x197 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_RETRY = 405; // 0x195 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402; // 0x192 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400; // 0x190 field public static final int SEND_STATUS_OK = 0; // 0x0 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_CANCELLED = 215; // 0xd7 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212; // 0xd4 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204; // 0xcc + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200; // 0xc8 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203; // 0xcb + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202; // 0xca + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201; // 0xc9 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206; // 0xce + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205; // 0xcd + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208; // 0xd0 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213; // 0xd5 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210; // 0xd2 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_INVALID_STATE = 209; // 0xd1 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211; // 0xd3 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207; // 0xcf + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214; // 0xd6 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216; // 0xd8 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217; // 0xd9 + field @FlaggedApi("com.android.internal.telephony.flags.temporary_failures_in_carrier_messaging_service") public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218; // 0xda field public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; // 0x1 field public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierMessagingService"; } diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java index 61213e6293ba..a825a7e110f5 100644 --- a/core/java/android/service/carrier/CarrierMessagingService.java +++ b/core/java/android/service/carrier/CarrierMessagingService.java @@ -16,6 +16,7 @@ package android.service.carrier; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,6 +27,8 @@ import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; +import com.android.internal.telephony.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; @@ -97,16 +100,317 @@ public abstract class CarrierMessagingService extends Service { public static final int SEND_STATUS_RETRY_ON_CARRIER_NETWORK = 1; /** - * SMS/MMS sending failed. We should not retry via the carrier network. + * SMS/MMS sending failed due to an unspecified issue. Sending will not be retried via the + * carrier network. + * + *

Maps to SmsManager.RESULT_RIL_GENERIC_FAILURE for SMS and SmsManager.MMS_ERROR_UNSPECIFIED + * for MMS. */ public static final int SEND_STATUS_ERROR = 2; + /** + * More precise error reasons for outbound SMS send requests. These will not be retried on the + * carrier network. + * + *

Each code maps directly to an SmsManager code (e.g. SEND_STATS_RESULT_ERROR_NULL_PDU maps + * to SmsManager.RESULT_ERROR_NULL_PDU). + */ + + /** + * Generic failure cause. + * + * @see android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE = 200; + + /** + * Failed because no pdu provided. + * + * @see android.telephony.SmsManager.RESULT_ERROR_NULL_PDU + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_NULL_PDU = 201; + + /** + * Failed because service is currently unavailable. + * + * @see android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_NO_SERVICE = 202; + + /** + * Failed because we reached the sending queue limit. + * + * @see android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED = 203; + + /** + * Failed because FDN is enabled. + * + * @see android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE = 204; + + /** + * Failed because user denied the sending of this short code. + * + * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED = 205; + + /** + * Failed because the user has denied this app ever send premium short codes. + * + * @see android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED = 206; + + /** + * Failed because of network rejection. + * + * @see android.telephony.SmsManager.RESULT_NETWORK_REJECT + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_NETWORK_REJECT = 207; + + /** + * Failed because of invalid arguments. + * + * @see android.telephony.SmsManager.RESULT_INVALID_ARGUMENTS + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_INVALID_ARGUMENTS = 208; + + /** + * Failed because of an invalid state. + * + * @see android.telephony.SmsManager.RESULT_INVALID_STATE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_INVALID_STATE = 209; + + /** + * Failed because the sms format is not valid. + * + * @see android.telephony.SmsManager.RESULT_INVALID_SMS_FORMAT + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_INVALID_SMS_FORMAT = 210; + + /** + * Failed because of a network error. + * + * @see android.telephony.SmsManager.RESULT_NETWORK_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_NETWORK_ERROR = 211; + + /** + * Failed because of an encoding error. + * + * @see android.telephony.SmsManager.RESULT_ENCODING_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_ENCODING_ERROR = 212; + + /** + * Failed because of an invalid smsc address + * + * @see android.telephony.SmsManager.RESULT_INVALID_SMSC_ADDRESS + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS = 213; + + /** + * Failed because the operation is not allowed. + * + * @see android.telephony.SmsManager.RESULT_OPERATION_NOT_ALLOWED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED = 214; + + /** + * Failed because the operation was cancelled. + * + * @see android.telephony.SmsManager.RESULT_CANCELLED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_CANCELLED = 215; + + /** + * Failed because the request is not supported. + * + * @see android.telephony.SmsManager.RESULT_REQUEST_NOT_SUPPORTED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED = 216; + + /** + * Failed sending during an emergency call. + * + * @see android.telephony.SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY = 217; + + /** + * Failed to send an sms retry. + * + * @see android.telephony.SmsManager.RESULT_SMS_SEND_RETRY_FAILED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED = 218; + + /** + * More precise error reasons for outbound MMS send requests. These will not be retried on the + * carrier network. + * + *

Each code maps directly to an SmsManager code (e.g. + * SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS). + */ + + /** + * Unspecific MMS error occurred during send. + * + * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_UNSPECIFIED = 400; + + /** + * ApnException occurred during MMS network setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_INVALID_APN = 401; + + /** + * An error occurred during the MMS connection setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 402; + + /** + * An error occurred during the HTTP client setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_HTTP_FAILURE = 403; + + /** + * An I/O error occurred reading the PDU. + * + * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_IO_ERROR = 404; + + /** + * An error occurred while retrying sending the MMS. + * + * @see android.telephony.SmsManager.MMS_ERROR_RETRY + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_RETRY = 405; + + /** + * The carrier-dependent configuration values could not be loaded. + * + * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 406; + + /** + * There is neither Wi-Fi nor mobile data network. + * + * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK = 407; + + /** + * The subscription id for the send is invalid. + * + * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 408; + + /** + * The subscription id for the send is inactive. + * + * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 409; + + /** + * Data is disabled for the MMS APN. + * + * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_DATA_DISABLED = 410; + + /** + * MMS is disabled by a carrier. + * + * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 411; + /** @hide */ - @IntDef(prefix = { "SEND_STATUS_" }, value = { - SEND_STATUS_OK, - SEND_STATUS_RETRY_ON_CARRIER_NETWORK, - SEND_STATUS_ERROR - }) + @IntDef( + prefix = {"SEND_STATUS_"}, + value = { + SEND_STATUS_OK, + SEND_STATUS_RETRY_ON_CARRIER_NETWORK, + SEND_STATUS_ERROR, + SEND_STATUS_RESULT_ERROR_GENERIC_FAILURE, + SEND_STATUS_RESULT_ERROR_NULL_PDU, + SEND_STATUS_RESULT_ERROR_NO_SERVICE, + SEND_STATUS_RESULT_ERROR_LIMIT_EXCEEDED, + SEND_STATUS_RESULT_ERROR_FDN_CHECK_FAILURE, + SEND_STATUS_RESULT_ERROR_SHORT_CODE_NOT_ALLOWED, + SEND_STATUS_RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, + SEND_STATUS_RESULT_NETWORK_REJECT, + SEND_STATUS_RESULT_INVALID_ARGUMENTS, + SEND_STATUS_RESULT_INVALID_STATE, + SEND_STATUS_RESULT_INVALID_SMS_FORMAT, + SEND_STATUS_RESULT_NETWORK_ERROR, + SEND_STATUS_RESULT_ENCODING_ERROR, + SEND_STATUS_RESULT_INVALID_SMSC_ADDRESS, + SEND_STATUS_RESULT_OPERATION_NOT_ALLOWED, + SEND_STATUS_RESULT_CANCELLED, + SEND_STATUS_RESULT_REQUEST_NOT_SUPPORTED, + SEND_STATUS_RESULT_SMS_BLOCKED_DURING_EMERGENCY, + SEND_STATUS_RESULT_SMS_SEND_RETRY_FAILED, + SEND_STATUS_MMS_ERROR_UNSPECIFIED, + SEND_STATUS_MMS_ERROR_INVALID_APN, + SEND_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS, + SEND_STATUS_MMS_ERROR_HTTP_FAILURE, + SEND_STATUS_MMS_ERROR_IO_ERROR, + SEND_STATUS_MMS_ERROR_RETRY, + SEND_STATUS_MMS_ERROR_CONFIGURATION_ERROR, + SEND_STATUS_MMS_ERROR_NO_DATA_NETWORK, + SEND_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID, + SEND_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION, + SEND_STATUS_MMS_ERROR_DATA_DISABLED, + SEND_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER + }) @Retention(RetentionPolicy.SOURCE) public @interface SendResult {} @@ -121,16 +425,138 @@ public abstract class CarrierMessagingService extends Service { public static final int DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK = 1; /** - * MMS downloading failed. We should not retry via the carrier network. + * MMS downloading failed due to an unspecified issue. Downloading will not be retried via the + * carrier network. + * + *

Maps to SmsManager.MMR_ERROR_UNSPECIFIED. */ public static final int DOWNLOAD_STATUS_ERROR = 2; + /** + * More precise error reasons for inbound MMS download requests. These will not be retried on + * the carrier network. + * + *

Each code maps directly to an SmsManager code (e.g. + * DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS maps to + * SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS). + */ + + /** + * Unspecific MMS error occurred during download. + * + * @see android.telephony.SmsManager.MMS_ERROR_UNSPECIFIED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED = 600; + + /** + * ApnException occurred during MMS network setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_INVALID_APN + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN = 601; + + /** + * An error occurred during the MMS connection setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_UNABLE_CONNECT_MMS + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS = 602; + + /** + * An error occurred during the HTTP client setup. + * + * @see android.telephony.SmsManager.MMS_ERROR_HTTP_FAILURE + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE = 603; + + /** + * An I/O error occurred reading the PDU. + * + * @see android.telephony.SmsManager.MMS_ERROR_IO_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR = 604; + + /** + * An error occurred while retrying downloading the MMS. + * + * @see android.telephony.SmsManager.MMS_ERROR_RETRY + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_RETRY = 605; + + /** + * The carrier-dependent configuration values could not be loaded. + * + * @see android.telephony.SmsManager.MMS_ERROR_CONFIGURATION_ERROR + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR = 606; + + /** + * There is neither Wi-Fi nor mobile data network. + * + * @see android.telephony.SmsManager.MMS_ERROR_NO_DATA_NETWORK + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK = 607; + + /** + * The subscription id for the download is invalid. + * + * @see android.telephony.SmsManager.MMS_ERROR_INVALID_SUBSCRIPTION_ID + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID = 608; + + /** + * The subscription id for the download is inactive. + * + * @see android.telephony.SmsManager.MMS_ERROR_INACTIVE_SUBSCRIPTION + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION = 609; + + /** + * Data is disabled for the MMS APN. + * + * @see android.telephony.SmsManager.MMS_ERROR_DATA_DISABLED + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED = 610; + + /** + * MMS is disabled by a carrier. + * + * @see android.telephony.SmsManager.MMS_ERROR_MMS_DISABLED_BY_CARRIER + */ + @FlaggedApi(Flags.FLAG_TEMPORARY_FAILURES_IN_CARRIER_MESSAGING_SERVICE) + public static final int DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER = 611; + /** @hide */ - @IntDef(prefix = { "DOWNLOAD_STATUS_" }, value = { - DOWNLOAD_STATUS_OK, - DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK, - DOWNLOAD_STATUS_ERROR - }) + @IntDef( + prefix = {"DOWNLOAD_STATUS_"}, + value = { + DOWNLOAD_STATUS_OK, + DOWNLOAD_STATUS_RETRY_ON_CARRIER_NETWORK, + DOWNLOAD_STATUS_ERROR, + DOWNLOAD_STATUS_MMS_ERROR_UNSPECIFIED, + DOWNLOAD_STATUS_MMS_ERROR_INVALID_APN, + DOWNLOAD_STATUS_MMS_ERROR_UNABLE_CONNECT_MMS, + DOWNLOAD_STATUS_MMS_ERROR_HTTP_FAILURE, + DOWNLOAD_STATUS_MMS_ERROR_IO_ERROR, + DOWNLOAD_STATUS_MMS_ERROR_RETRY, + DOWNLOAD_STATUS_MMS_ERROR_CONFIGURATION_ERROR, + DOWNLOAD_STATUS_MMS_ERROR_NO_DATA_NETWORK, + DOWNLOAD_STATUS_MMS_ERROR_INVALID_SUBSCRIPTION_ID, + DOWNLOAD_STATUS_MMS_ERROR_INACTIVE_SUBSCRIPTION, + DOWNLOAD_STATUS_MMS_ERROR_DATA_DISABLED, + DOWNLOAD_STATUS_MMS_ERROR_MMS_DISABLED_BY_CARRIER + }) @Retention(RetentionPolicy.SOURCE) public @interface DownloadResult {} -- GitLab From 159843674c69de7593488247489853ed45e3665d Mon Sep 17 00:00:00 2001 From: Lyn Date: Fri, 15 Nov 2024 18:15:51 +0000 Subject: [PATCH 209/656] Fix ArrayIndexOutOfBoundsException after allowing reorder Fixes: 378795050 Test: HeadsUpManagerPhoneTest Test: send delayed HUN, open shade, close shade to allow reorder and removeEntry => no exception Test: send delayed HUN, open shade, manually swipe dismiss HUN to removeEntry => entry removed from mEntriesToRemoveWhenReorderingAllowed, no memory leak Flag: com.android.systemui.notification_avalanche_throttle_hun Change-Id: I31e74a524dcf406c2c01b4ea524ff757c7726dc1 --- .../policy/BaseHeadsUpManagerTest.java | 2 +- .../policy/HeadsUpManagerPhoneTest.kt | 20 +++++++++++++- .../statusbar/policy/BaseHeadsUpManager.java | 27 ++++++++++++------- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java index abb3e6e0c1f2..0fbee6d29441 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java @@ -321,7 +321,7 @@ public class BaseHeadsUpManagerTest extends SysuiTestCase { BaseHeadsUpManager.HeadsUpEntry.class); headsUpEntry.mEntry = notifEntry; - hum.onEntryRemoved(headsUpEntry); + hum.onEntryRemoved(headsUpEntry, "test"); verify(mLogger, times(1)).logNotificationActuallyRemoved(eq(notifEntry)); } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt index 8ebdbaaa6bf0..6175e05923a7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.kt @@ -82,7 +82,6 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager private val mJavaAdapter: JavaAdapter = JavaAdapter(testScope.backgroundScope) @Mock private lateinit var mShadeInteractor: ShadeInteractor - @Mock private lateinit var dumpManager: DumpManager private lateinit var mAvalancheController: AvalancheController @@ -205,6 +204,25 @@ class HeadsUpManagerPhoneTest(flags: FlagsParameterization) : BaseHeadsUpManager assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue() } + class TestAnimationStateHandler : AnimationStateHandler { + override fun setHeadsUpGoingAwayAnimationsAllowed(allowed: Boolean) {} + } + + @Test + @EnableFlags(NotificationThrottleHun.FLAG_NAME) + fun testReorderingAllowed_clearsListOfEntriesToRemove() { + whenever(mVSProvider.isReorderingAllowed).thenReturn(true) + val hmp = createHeadsUpManagerPhone() + + val notifEntry = HeadsUpManagerTestUtil.createEntry(/* id= */ 0, mContext) + hmp.showNotification(notifEntry) + assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)).isTrue() + + hmp.setAnimationStateHandler(TestAnimationStateHandler()) + hmp.mOnReorderingAllowedListener.onReorderingAllowed() + assertThat(hmp.mEntriesToRemoveWhenReorderingAllowed.isEmpty()).isTrue() + } + @Test @EnableFlags(NotificationThrottleHun.FLAG_NAME) fun testShowNotification_reorderNotAllowed_seenInShadeTrue() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java index f6f567f17077..298ef7ee4bfa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java @@ -88,7 +88,7 @@ public class BaseHeadsUpManager implements HeadsUpManager, HeadsUpRepository, OnHeadsUpChangedListener { private static final String TAG = "BaseHeadsUpManager"; private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; - + private static final String REASON_REORDER_ALLOWED = "mOnReorderingAllowedListener"; protected final ListenerSet mListeners = new ListenerSet<>(); protected final Context mContext; @@ -633,7 +633,7 @@ public class BaseHeadsUpManager } entry.demoteStickyHun(); mHeadsUpEntryMap.remove(key); - onEntryRemoved(finalHeadsUpEntry); + onEntryRemoved(finalHeadsUpEntry, reason); // TODO(b/328390331) move accessibility events to the view layer entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); if (NotificationThrottleHun.isEnabled()) { @@ -648,8 +648,9 @@ public class BaseHeadsUpManager /** * Manager-specific logic that should occur when an entry is removed. * @param headsUpEntry entry removed + * @param reason why onEntryRemoved was called */ - protected void onEntryRemoved(HeadsUpEntry headsUpEntry) { + protected void onEntryRemoved(HeadsUpEntry headsUpEntry, String reason) { NotificationEntry entry = headsUpEntry.mEntry; entry.setHeadsUp(false); setEntryPinned(headsUpEntry, false /* isPinned */, "onEntryRemoved"); @@ -664,10 +665,17 @@ public class BaseHeadsUpManager updateTopHeadsUpFlow(); updateHeadsUpFlow(); if (NotificationThrottleHun.isEnabled()) { - if (headsUpEntry.mEntry != null) { - if (mEntriesToRemoveWhenReorderingAllowed.contains(headsUpEntry.mEntry)) { - mEntriesToRemoveWhenReorderingAllowed.remove(headsUpEntry.mEntry); - } + NotificationEntry notifEntry = headsUpEntry.mEntry; + if (notifEntry == null) { + return; + } + // If reorder was just allowed and we called onEntryRemoved while iterating over + // mEntriesToRemoveWhenReorderingAllowed, we should not remove from this list (and cause + // ArrayIndexOutOfBoundsException). We don't need to in this case anyway, because we + // clear mEntriesToRemoveWhenReorderingAllowed after removing these entries. + if (!reason.equals(REASON_REORDER_ALLOWED) + && mEntriesToRemoveWhenReorderingAllowed.contains(notifEntry)) { + mEntriesToRemoveWhenReorderingAllowed.remove(notifEntry); } } } @@ -1135,7 +1143,8 @@ public class BaseHeadsUpManager && Notification.CATEGORY_CALL.equals(n.category)); } - private final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> { + @VisibleForTesting + public final OnReorderingAllowedListener mOnReorderingAllowedListener = () -> { if (NotificationThrottleHun.isEnabled()) { mAvalancheController.setEnableAtRuntime(true); if (mEntriesToRemoveWhenReorderingAllowed.isEmpty()) { @@ -1146,7 +1155,7 @@ public class BaseHeadsUpManager for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) { if (entry != null && isHeadsUpEntry(entry.getKey())) { // Maybe the heads-up was removed already - removeEntry(entry.getKey(), "mOnReorderingAllowedListener"); + removeEntry(entry.getKey(), REASON_REORDER_ALLOWED); } } mEntriesToRemoveWhenReorderingAllowed.clear(); -- GitLab From ce26f592484095c5f0375c52d6a7ac744a7bb9a6 Mon Sep 17 00:00:00 2001 From: Raphael Kim Date: Mon, 18 Nov 2024 22:38:03 +0000 Subject: [PATCH 210/656] Set default state for secure channel attestation result to non-success. Bug: 379565396 Change-Id: I90f16b1d3596aefa07cba116c1626b158b96dcbc Test: Manual Flag: EXEMPT bugfix --- .../android/server/companion/securechannel/SecureChannel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java index dbeca82ade89..2d3782fb3181 100644 --- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java +++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java @@ -16,6 +16,8 @@ package com.android.server.companion.securechannel; +import static android.security.attestationverification.AttestationVerificationManager.FLAG_FAILURE_UNKNOWN; + import android.annotation.NonNull; import android.content.Context; import android.os.Build; @@ -67,7 +69,7 @@ public class SecureChannel { private D2DConnectionContextV1 mConnectionContext; private String mAlias; - private int mVerificationResult; + private int mVerificationResult = FLAG_FAILURE_UNKNOWN; private boolean mPskVerified; -- GitLab From bf6b187a3ddc1a24098b099ba81659a7796c7a4d Mon Sep 17 00:00:00 2001 From: Yurii Zubrytskyi Date: Mon, 18 Nov 2024 15:05:36 -0800 Subject: [PATCH 211/656] Reapply "[res] Duplicate AssetManager when changes are needed" This reverts commit 9dd30fb6a453d71e6da3c12c00ba74697324ef1b Changes: filter out duplicate assets when making a new instance of AssetManager Original comment: [res] Duplicate AssetManager when changes are needed When adding new apk paths to the ResourcesImpl's AssetManager, we used to make it in a non-atomical way - first, the paths get added without recalculating the internal indices, then later it updates the indices and applies new configuration. This causes some lookups to fail until the index recalculation, as apk asset shift in the internal array, and the indices stop pointing to the correct apks. In this change, the asset paths update is now more parametrized: - When creating a ResourcesImpl we know it's not being used yet, so we just update the paths in place, and use the same non- atomic, but the fastest, code - When updating an existing object we instead create a new AssetManager object with the new paths, and then use that instead of the original one that could be in use - this helps to keep the currently running lookups to be consistent, as the object can't change under the caller and suddenly start returning different resources - an added bonus is that now we make sure that the paths actually change before running any update operations, potentially saving lots of time on heavy locking and JNI calls + Add a lock when accessing the registered shared libraries Bug: 362700391 Test: manual, run a test app with a resources getter in a loop and verify it doesn't get NotFoundException Flag: EXEMPT bugfix with no single point of entry Change-Id: Ia957fec786d0b9426a7b33aa6747ef5c5d7681e9 --- core/java/android/app/ResourcesManager.java | 124 +++++++++++++----- core/java/android/content/res/ApkAssets.java | 33 +++-- .../android/content/res/ResourcesImpl.java | 22 +++- 3 files changed, 133 insertions(+), 46 deletions(-) diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 599a46b131d5..3cffca796680 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -174,22 +174,58 @@ public class ResourcesManager { } /** - * Apply the registered library paths to the passed impl object - * @return the hash code for the current version of the registered paths + * Apply the registered library paths to the passed AssetManager. If may create a new + * AssetManager if any changes are needed and it isn't allowed to reuse the old one. + * + * @return new AssetManager and the hash code for the current version of the registered paths */ - public int updateResourceImplWithRegisteredLibs(@NonNull ResourcesImpl impl) { + public @NonNull Pair updateResourceImplAssetsWithRegisteredLibs( + @NonNull AssetManager assets, boolean reuseAssets) { if (!Flags.registerResourcePaths()) { - return 0; + return new Pair<>(assets, 0); } - final var collector = new PathCollector(null); - final int size = mSharedLibAssetsMap.size(); - for (int i = 0; i < size; i++) { - final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); - collector.appendKey(libraryKey); + final int size; + final PathCollector collector; + + synchronized (mLock) { + size = mSharedLibAssetsMap.size(); + if (assets == AssetManager.getSystem()) { + return new Pair<>(assets, size); + } + collector = new PathCollector(resourcesKeyFromAssets(assets)); + for (int i = 0; i < size; i++) { + final var libraryKey = mSharedLibAssetsMap.valueAt(i).getResourcesKey(); + collector.appendKey(libraryKey); + } } - impl.getAssets().addPresetApkKeys(extractApkKeys(collector.collectedKey())); - return size; + if (collector.isSameAsOriginal()) { + return new Pair<>(assets, size); + } + if (reuseAssets) { + assets.addPresetApkKeys(extractApkKeys(collector.collectedKey())); + return new Pair<>(assets, size); + } + final var newAssetsBuilder = new AssetManager.Builder().setNoInit(); + for (final var asset : assets.getApkAssets()) { + // Skip everything that's either default, or will get added by the collector (builder + // doesn't check for duplicates at all). + if (asset.isSystem() || asset.isForLoader() || asset.isOverlay() + || asset.isSharedLib()) { + continue; + } + newAssetsBuilder.addApkAssets(asset); + } + for (final var key : extractApkKeys(collector.collectedKey())) { + try { + final var asset = loadApkAssets(key); + newAssetsBuilder.addApkAssets(asset); + } catch (IOException e) { + Log.e(TAG, "Couldn't load assets for key " + key, e); + } + } + assets.getLoaders().forEach(newAssetsBuilder::addLoader); + return new Pair<>(newAssetsBuilder.build(), size); } public static class ApkKey { @@ -624,6 +660,23 @@ public class ResourcesManager { return apkKeys; } + private ResourcesKey resourcesKeyFromAssets(@NonNull AssetManager assets) { + final var libs = new ArrayList(); + final var overlays = new ArrayList(); + for (final ApkAssets asset : assets.getApkAssets()) { + if (asset.isSystem() || asset.isForLoader()) { + continue; + } + if (asset.isOverlay()) { + overlays.add(asset.getAssetPath()); + } else if (asset.isSharedLib()) { + libs.add(asset.getAssetPath()); + } + } + return new ResourcesKey(null, null, overlays.toArray(new String[0]), + libs.toArray(new String[0]), 0, null, null); + } + /** * Creates an AssetManager from the paths within the ResourcesKey. * @@ -752,7 +805,7 @@ public class ResourcesManager { final Configuration config = generateConfig(key); final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj); - final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj); + final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj, true); if (DEBUG) { Slog.d(TAG, "- creating impl=" + impl + " with key: " + key); @@ -1832,31 +1885,32 @@ public class ResourcesManager { for (int i = 0; i < resourcesCount; i++) { final WeakReference ref = mAllResourceReferences.get(i); final Resources r = ref != null ? ref.get() : null; - if (r != null) { - final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); - if (key != null) { - final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); - if (impl == null) { - throw new Resources.NotFoundException("failed to redirect ResourcesImpl"); - } - r.setImpl(impl); - } else { - // ResourcesKey is null which means the ResourcesImpl could belong to a - // Resources created by application through Resources constructor and was not - // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to - // have shared library asset paths appended if there are any. - if (r.getImpl() != null) { - final ResourcesImpl oldImpl = r.getImpl(); - final AssetManager oldAssets = oldImpl.getAssets(); - // ResourcesImpl constructor will help to append shared library asset paths. - if (oldAssets != AssetManager.getSystem() && oldAssets.isUpToDate()) { - final ResourcesImpl newImpl = new ResourcesImpl(oldAssets, - oldImpl.getMetrics(), oldImpl.getConfiguration(), - oldImpl.getDisplayAdjustments()); + if (r == null) { + continue; + } + final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); + if (key != null) { + final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); + if (impl == null) { + throw new Resources.NotFoundException("failed to redirect ResourcesImpl"); + } + r.setImpl(impl); + } else { + // ResourcesKey is null which means the ResourcesImpl could belong to a + // Resources created by application through Resources constructor and was not + // managed by ResourcesManager, so the ResourcesImpl needs to be recreated to + // have shared library asset paths appended if there are any. + final ResourcesImpl oldImpl = r.getImpl(); + if (oldImpl != null) { + final AssetManager oldAssets = oldImpl.getAssets(); + // ResourcesImpl constructor will help to append shared library asset paths. + if (oldAssets != AssetManager.getSystem()) { + if (oldAssets.isUpToDate()) { + final ResourcesImpl newImpl = new ResourcesImpl(oldImpl); r.setImpl(newImpl); } else { - Slog.w(TAG, "Skip appending shared library asset paths for the " - + "Resource as its assets are not up to date."); + Slog.w(TAG, "Skip appending shared library asset paths for " + + "the Resources as its assets are not up to date."); } } } diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 68b5d782bfbf..908999b64961 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -124,11 +124,13 @@ public final class ApkAssets { @Nullable @GuardedBy("this") - private final StringBlock mStringBlock; // null or closed if mNativePtr = 0. + private StringBlock mStringBlock; // null or closed if mNativePtr = 0. @PropertyFlags private final int mFlags; + private final boolean mIsOverlay; + @Nullable private final AssetsProvider mAssets; @@ -302,40 +304,43 @@ public final class ApkAssets { private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(path, "path"); - mFlags = flags; mNativePtr = nativeLoad(format, path, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); - mFlags = flags; mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { + this(format, flags, assets); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); - mFlags = flags; mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); - mAssets = assets; } private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) { - mFlags = flags; + this(FORMAT_APK, flags, assets); mNativePtr = nativeLoadEmpty(flags, assets); mStringBlock = null; + } + + private ApkAssets(@FormatType int format, @PropertyFlags int flags, + @Nullable AssetsProvider assets) { + mFlags = flags; mAssets = assets; + mIsOverlay = format == FORMAT_IDMAP; } @UnsupportedAppUsage @@ -425,6 +430,18 @@ public final class ApkAssets { } } + public boolean isSystem() { + return (mFlags & PROPERTY_SYSTEM) != 0; + } + + public boolean isSharedLib() { + return (mFlags & PROPERTY_DYNAMIC) != 0; + } + + public boolean isOverlay() { + return mIsOverlay; + } + @Override public String toString() { return "ApkAssets{path=" + getDebugName() + "}"; diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index e6b93427f413..bcaceb24d767 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -203,9 +203,25 @@ public class ResourcesImpl { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics, @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) { - mAssets = assets; - mAppliedSharedLibsHash = - ResourcesManager.getInstance().updateResourceImplWithRegisteredLibs(this); + // Don't reuse assets by default as we have no control over whether they're already + // inside some other ResourcesImpl. + this(assets, metrics, config, displayAdjustments, false); + } + + public ResourcesImpl(@NonNull ResourcesImpl orig) { + // We know for sure that the other assets are in use, so can't reuse the object here. + this(orig.getAssets(), orig.getMetrics(), orig.getConfiguration(), + orig.getDisplayAdjustments(), false); + } + + public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics, + @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments, + boolean reuseAssets) { + final var assetsAndHash = + ResourcesManager.getInstance().updateResourceImplAssetsWithRegisteredLibs(assets, + reuseAssets); + mAssets = assetsAndHash.first; + mAppliedSharedLibsHash = assetsAndHash.second; mMetrics.setToDefaults(); mDisplayAdjustments = displayAdjustments; mConfiguration.setToDefaults(); -- GitLab From 8bc1aaa45aec0beb2d9bd47ca64a724cb4133efc Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 18 Nov 2024 15:12:22 -0800 Subject: [PATCH 212/656] Make ravenizer less verbose Flag: EXEMPT host test change only Bug: 292141694 Test: m RavenwoodServicesTest Change-Id: Iad96a7ff8e09377c65f6583c155230bbc25ed64c --- .../com/android/platform/test/ravenwood/ravenizer/Validator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt index 8ec0932d89dd..61e254b225c3 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt @@ -43,7 +43,7 @@ fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean { } var allOk = true - log.i("Checking ${cn.name.toHumanReadableClassName()}") + log.v("Checking ${cn.name.toHumanReadableClassName()}") // See if there's any class that extends a legacy base class. // But ignore the base classes in android.test. -- GitLab From 2f7d14b9fe634cf85b239dfd4aff9ce57e5f3eb5 Mon Sep 17 00:00:00 2001 From: Bob Wang Date: Mon, 18 Nov 2024 23:48:50 +0000 Subject: [PATCH 213/656] Update javadoc for CardEmulation.setServiceEnabled() Bug: 379742761 Change-Id: I56248b5300f189b7bd1fcda25cbf546a3bde9440 Test: compile --- nfc/java/android/nfc/cardemulation/CardEmulation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index e9ec7215e4d0..24ff7ab9c2b6 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -947,7 +947,7 @@ public final class CardEmulation { * * @param service The ComponentName of the service * @param status true to enable, false to disable - * @return true if preferred service is successfully set or unset, otherwise return false. + * @return status code defined in {@link SetServiceEnabledStatusCode} * * @hide */ -- GitLab From 9d3c24bbfaa83364f2c4067a3d58656ccf813a22 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Mon, 18 Nov 2024 15:52:46 -0800 Subject: [PATCH 214/656] Dump JVM arguments on startup Flag: EXEMPT host test change only Bug: 371602426 Test: atest RavenwoodBivalentTest, check the log Change-Id: I061dad98f1e1f2ebea3dba41a48307c96b990010 --- .../RavenwoodRuntimeEnvironmentController.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 1c1f15761329..979076eaabfd 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -67,6 +67,7 @@ import com.android.ravenwood.common.SneakyThrow; import com.android.server.LocalServices; import com.android.server.compat.PlatformCompat; +import org.junit.internal.management.ManagementFactory; import org.junit.runner.Description; import java.io.File; @@ -204,6 +205,8 @@ public class RavenwoodRuntimeEnvironmentController { // Some process-wide initialization. (maybe redirect stdout/stderr) RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME); + dumpCommandLineArgs(); + // We haven't initialized liblog yet, so directly write to System.out here. RavenwoodCommonUtils.log(TAG, "globalInitInner()"); @@ -588,4 +591,18 @@ public class RavenwoodRuntimeEnvironmentController { + " access to system property '" + key + "' denied via RavenwoodConfig"); } } + + private static void dumpCommandLineArgs() { + Log.i(TAG, "JVM arguments:"); + + // Note, we use the wrapper in JUnit4, not the actual class ( + // java.lang.management.ManagementFactory), because we can't see the later at the build + // because this source file is compiled for the device target, where ManagementFactory + // doesn't exist. + var args = ManagementFactory.getRuntimeMXBean().getInputArguments(); + + for (var arg : args) { + Log.i(TAG, " " + arg); + } + } } -- GitLab From 907c7f48fc31d2990fd049314a81c7f50480a825 Mon Sep 17 00:00:00 2001 From: Liefu Liu Date: Mon, 18 Nov 2024 15:18:08 -0800 Subject: [PATCH 215/656] Privatize the constructor of DefaultAccountAndState. Bug: 367438047 Test: atest ContactsProviderTests and atest Contacts and atest CtsContactsProviderTestCases Flag: android.provider.new_default_account_api_enabled modified: api/current.txt modified: java/android/provider/ContactsContract.java Change-Id: I552bc43b0c4b9176a46a55cf334db45bafd74dcf --- core/api/current.txt | 1 - core/java/android/provider/ContactsContract.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 76f3e5a3cd39..7dde5dfd1255 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -37573,7 +37573,6 @@ package android.provider { } @FlaggedApi("android.provider.new_default_account_api_enabled") public static final class ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState { - ctor public ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState(int, @Nullable android.accounts.Account); method @Nullable public android.accounts.Account getAccount(); method public int getState(); method @NonNull public static android.provider.ContactsContract.RawContacts.DefaultAccount.DefaultAccountAndState ofCloud(@NonNull android.accounts.Account); diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 1b289fdabbd1..99ff38b43adc 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -3151,7 +3151,7 @@ public final class ContactsContract { * {@link #DEFAULT_ACCOUNT_STATE_CLOUD} or * {@link #DEFAULT_ACCOUNT_STATE_SIM}, or null otherwise. */ - public DefaultAccountAndState(@DefaultAccountState int state, + private DefaultAccountAndState(@DefaultAccountState int state, @Nullable Account account) { if (!isValidDefaultAccountState(state)) { throw new IllegalArgumentException("Invalid default account state."); -- GitLab From a5373d494ddaf2d814ffc1e4d2e3a80f1498a740 Mon Sep 17 00:00:00 2001 From: Brian Lindahl Date: Thu, 7 Nov 2024 07:36:59 -0700 Subject: [PATCH 216/656] Add APIs for applying picture profiles to a layer and listening Tested by faking a device to have picture processing enabled at the HWComposer.cpp layer, with PICTURE_PROCESSING display capability and getMaxLayerPictureProfiles returning '2'. Bug: 337330263 API-Coverage-Bug: 379165798 Test: atest SurfaceControlPictureProfileTest Flag: android.media.tv.flags.apply_picture_profiles Change-Id: I109beca2e97265fea434fe0b2e59916cc0d8f048 --- core/api/current.txt | 1 + core/api/system-current.txt | 12 + core/java/android/view/SurfaceControl.java | 100 ++++++- .../view/SurfaceControlActivePicture.java | 58 ++++ .../SurfaceControlActivePictureListener.java | 69 +++++ core/jni/Android.bp | 1 + core/jni/AndroidRuntime.cpp | 2 + core/jni/android_view_SurfaceControl.cpp | 34 +++ ...ew_SurfaceControlActivePictureListener.cpp | 191 +++++++++++++ .../media/quality/PictureProfileHandle.java | 38 ++- .../AndroidManifest.xml | 8 + .../AndroidTest.xml | 3 + .../SurfaceControlPictureProfileTest.java | 260 ++++++++++++++++++ ...faceControlPictureProfileTestActivity.java | 43 +++ .../layout/picture_profile_test_layout.xml | 32 +++ 15 files changed, 838 insertions(+), 14 deletions(-) create mode 100644 core/java/android/view/SurfaceControlActivePicture.java create mode 100644 core/java/android/view/SurfaceControlActivePictureListener.java create mode 100644 core/jni/android_view_SurfaceControlActivePictureListener.cpp create mode 100644 tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTest.java create mode 100644 tests/CtsSurfaceControlTestsStaging/src/main/java/android/view/surfacecontroltests/SurfaceControlPictureProfileTestActivity.java create mode 100644 tests/CtsSurfaceControlTestsStaging/src/main/res/layout/picture_profile_test_layout.xml diff --git a/core/api/current.txt b/core/api/current.txt index 76f3e5a3cd39..4584ce3d644d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -53406,6 +53406,7 @@ package android.view { method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer, @Nullable android.hardware.SyncFence, @Nullable java.util.function.Consumer); method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int); method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int); + method @FlaggedApi("android.media.tv.flags.apply_picture_profiles") @NonNull public android.view.SurfaceControl.Transaction setContentPriority(@NonNull android.view.SurfaceControl, int); method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect); method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region); method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index e03548e715f5..b8d5c199c241 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8171,6 +8171,14 @@ package android.media.quality { method @NonNull public android.media.quality.PictureProfile.Builder setProfileType(int); } + @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public final class PictureProfileHandle implements android.os.Parcelable { + method @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public int describeContents(); + method @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public long getId(); + method @FlaggedApi("android.media.tv.flags.apply_picture_profiles") public void writeToParcel(@NonNull android.os.Parcel, int); + field @FlaggedApi("android.media.tv.flags.apply_picture_profiles") @NonNull public static final android.os.Parcelable.Creator CREATOR; + field @NonNull public static final android.media.quality.PictureProfileHandle NONE; + } + } package android.media.session { @@ -18999,6 +19007,10 @@ package android.util { package android.view { + public static class SurfaceControl.Transaction implements java.io.Closeable android.os.Parcelable { + method @FlaggedApi("android.media.tv.flags.apply_picture_profiles") @NonNull public android.view.SurfaceControl.Transaction setPictureProfileHandle(@NonNull android.view.SurfaceControl, @NonNull android.media.quality.PictureProfileHandle); + } + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { method @NonNull public final java.util.List getUnrestrictedPreferKeepClearRects(); method @RequiresPermission(android.Manifest.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS) public final void setUnrestrictedPreferKeepClearRects(@NonNull java.util.List); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index d56768d2db03..8e9a84b4adce 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -37,6 +37,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.Size; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.ColorSpace; @@ -58,6 +59,7 @@ import android.hardware.display.DeviceProductInfo; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; import android.hardware.graphics.common.DisplayDecorationSupport; +import android.media.quality.PictureProfileHandle; import android.opengl.EGLDisplay; import android.opengl.EGLSync; import android.os.Build; @@ -234,7 +236,6 @@ public final class SurfaceControl implements Parcelable { long nativeObject, float currentBufferRatio, float desiredRatio); private static native void nativeSetDesiredHdrHeadroom(long transactionObj, long nativeObject, float desiredRatio); - private static native void nativeSetCachingHint(long transactionObj, long nativeObject, int cachingHint); private static native void nativeSetDamageRegion(long transactionObj, long nativeObject, @@ -314,6 +315,11 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetLuts(long transactionObj, long nativeObject, float[] buffers, int[] slots, int[] dimensions, int[] sizes, int[] samplingKeys); private static native void nativeEnableDebugLogCallPoints(long transactionObj); + private static native int nativeGetMaxPictureProfiles(); + private static native void nativeSetPictureProfileId(long transactionObj, + long nativeObject, long pictureProfileId); + private static native void nativeSetContentPriority(long transactionObj, long nativeObject, + int priority); /** * Transforms that can be applied to buffers as they are displayed to a window. @@ -2832,6 +2838,33 @@ public final class SurfaceControl implements Parcelable { return nativeBootFinished(); } + /** + * Retrieve the maximum number of concurrent picture profiles allowed across all displays. + * + * A picture profile is assigned to a layer via: + *

    + *
  • Picture processing via {@link MediaCodec.KEY_PICTURE_PROFILE}
  • + *
  • Picture processing via {@link SurfaceControl.Transaction#setPictureProfileHandle} + *
  • + *
+ * + * If the maximum number is exceeded, some layers will not receive profiles based on: + *
    + *
  • The content priority assigned by the app
  • + *
  • The system-determined priority of the app owning the layer
  • + *
+ * + * @see MediaCodec.KEY_PICTURE_PROFILE + * @see SurfaceControl.Transaction#setPictureProfileHandle + * @see SurfaceControl.Transaction#setContentPriority + * + * @hide + */ + @IntRange(from = 0) + public static int getMaxPictureProfiles() { + return nativeGetMaxPictureProfiles(); + } + /** * Interface to handle request to * {@link SurfaceControl.Transaction#addTransactionCommittedListener(Executor, TransactionCommittedListener)} @@ -4594,6 +4627,71 @@ public final class SurfaceControl implements Parcelable { return this; } + /** + * Sets the desired picture profile handle for a layer. + *

+ * A handle, retrieved from {@link MediaQualityManager#getProfileHandles}, which + * refers a set of parameters that are used to configure picture processing that is applied + * to all subsequent buffers to enhance the quality of their contents (e.g. gamma, color + * temperature, hue, saturation, etc.). + *

+ * Setting a handle does not guarantee access to limited picture processing. The content + * priority for the as well as the prominance of app to the current user experience plays a + * role in which layer(s) get access to the limited picture processing resources. A maximum + * number of {@link MediaQualityManager.getMaxPictureProfiles} can be applied at any given + * point in time. + * + * @param sc The SurfaceControl of the layer that should be updated + * @param handle The picture profile handle which refers to the set of desired parameters + * + * @see MediaQualityManager#getMaxPictureProfiles + * @see MediaQualityManager#getProfileHandles + * @see MediaCodec.KEY_PICTURE_PROFILE + * @see SurfaceControl.Transaction#setContentPriority + * + * @hide + */ + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) + @SystemApi + public @NonNull Transaction setPictureProfileHandle(@NonNull SurfaceControl sc, + @NonNull PictureProfileHandle handle) { + checkPreconditions(sc); + + nativeSetPictureProfileId(mNativeObject, sc.mNativeObject, handle.getId()); + return this; + } + + /** + * Sets the importance the layer's contents has to the app's user experience. + *

+ * When a two layers within the same app are competing for a limited rendering resource, + * the priority will determine which layer gets access to the resource. The lower the + * priority, the more likely the layer will get access to the resource. + *

+ * Resources managed by this priority: + *

    + *
  • Picture processing via {@link MediaCodec.KEY_PICTURE_PROFILE}
  • + *
  • Picture processing via {@link SurfaceControl.Transaction#setPictureProfileHandle} + *
  • + *
+ * + * @param sc The SurfaceControl of the layer that should be updated + * @param priority The priority this layer should have with respect to other layers in the + * app. The default priority is zero. + * + * @see MediaQualityManager#getMaxPictureProfiles + * @see MediaCodec.KEY_PICTURE_PROFILE + * @see SurfaceControl.Transaction#setPictureProfileHandle + */ + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) + public @NonNull Transaction setContentPriority(@NonNull SurfaceControl sc, + int priority) { + checkPreconditions(sc); + + nativeSetContentPriority(mNativeObject, sc.mNativeObject, priority); + return this; + } + /** * Sets the caching hint for the layer. By default, the caching hint is * {@link CACHING_ENABLED}. diff --git a/core/java/android/view/SurfaceControlActivePicture.java b/core/java/android/view/SurfaceControlActivePicture.java new file mode 100644 index 000000000000..569159d73d2d --- /dev/null +++ b/core/java/android/view/SurfaceControlActivePicture.java @@ -0,0 +1,58 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.media.quality.PictureProfileHandle; + +/** + * A record of a visible layer that is using picture processing. + * @hide + */ +public class SurfaceControlActivePicture { + private final int mLayerId; + private final int mOwnerUid; + private final @NonNull PictureProfileHandle mPictureProfileHandle; + + /** + * Create a record of a visible layer that is using picture processing. + * + * @param layerId the layer that is using picture processing + * @param ownerUid the UID of the package that owns the layer + * @param handle the handle for the picture profile that configured the processing + */ + private SurfaceControlActivePicture(int layerId, int ownerUid, PictureProfileHandle handle) { + mLayerId = layerId; + mOwnerUid = ownerUid; + mPictureProfileHandle = handle; + } + + /** The layer that is using picture processing. */ + public int getLayerId() { + return mLayerId; + } + + /** The UID of the package that owns the layer using picture processing. */ + public int getOwnerUid() { + return mOwnerUid; + } + + /** A handle that indicates which picture profile has configured the picture processing. */ + public @NonNull PictureProfileHandle getPictureProfileHandle() { + return mPictureProfileHandle; + } +} diff --git a/core/java/android/view/SurfaceControlActivePictureListener.java b/core/java/android/view/SurfaceControlActivePictureListener.java new file mode 100644 index 000000000000..d7c53748784f --- /dev/null +++ b/core/java/android/view/SurfaceControlActivePictureListener.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.RequiresPermission; + +import libcore.util.NativeAllocationRegistry; + +/** + * Allows for the monitoring of visible layers that are using picture processing. + * @hide + */ +public abstract class SurfaceControlActivePictureListener { + private static final NativeAllocationRegistry sRegistry = + NativeAllocationRegistry.createMalloced( + SurfaceControlActivePictureListener.class.getClassLoader(), + nativeGetDestructor()); + + /** + * Callback when there are changes in the visible layers that are using picture processing. + * + * @param activePictures The visible layers that are using picture processing. + */ + public abstract void onActivePicturesChanged(SurfaceControlActivePicture[] activePictures); + + /** + * Start listening to changes in active pictures. + */ + @RequiresPermission(android.Manifest.permission.OBSERVE_PICTURE_PROFILES) + public void startListening() { + synchronized (this) { + long nativePtr = nativeMakeAndStartListening(); + mDestructor = sRegistry.registerNativeAllocation(this, nativePtr); + } + } + + /** + * Stop listening to changes in active pictures. + */ + @RequiresPermission(android.Manifest.permission.OBSERVE_PICTURE_PROFILES) + public void stopListening() { + final Runnable destructor; + synchronized (this) { + destructor = mDestructor; + } + if (destructor != null) { + destructor.run(); + } + } + + private native long nativeMakeAndStartListening(); + private static native long nativeGetDestructor(); + + private Runnable mDestructor; +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index a21bf9abdd7b..5c03c5cca66d 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -161,6 +161,7 @@ cc_library_shared_for_libandroid_runtime { "android_view_MotionPredictor.cpp", "android_view_PointerIcon.cpp", "android_view_SurfaceControl.cpp", + "android_view_SurfaceControlActivePictureListener.cpp", "android_view_SurfaceControlHdrLayerInfoListener.cpp", "android_view_WindowManagerGlobal.cpp", "android_graphics_BLASTBufferQueue.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 821861efd59b..00a62977de43 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -128,6 +128,7 @@ extern int register_android_view_InputApplicationHandle(JNIEnv* env); extern int register_android_view_InputWindowHandle(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); +extern int register_android_view_SurfaceControlActivePictureListener(JNIEnv* env); extern int register_android_view_SurfaceControlHdrLayerInfoListener(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); extern int register_android_view_CompositionSamplingListener(JNIEnv* env); @@ -1563,6 +1564,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_DisplayEventReceiver), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), + REG_JNI(register_android_view_SurfaceControlActivePictureListener), REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener), REG_JNI(register_android_view_SurfaceSession), REG_JNI(register_android_view_InputApplicationHandle), diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 593b982d4cf2..68e642086636 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -820,6 +821,21 @@ static void nativeSetLuts(JNIEnv* env, jclass clazz, jlong transactionObj, jlong transaction->setLuts(ctrl, base::unique_fd(fd), offsets, dimensions, sizes, samplingKeys); } +static void nativeSetPictureProfileId(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong surfaceControlObj, jlong pictureProfileId) { + auto transaction = reinterpret_cast(transactionObj); + SurfaceControl* const surfaceControl = reinterpret_cast(surfaceControlObj); + PictureProfileHandle handle(pictureProfileId); + transaction->setPictureProfileHandle(surfaceControl, handle); +} + +static void nativeSetContentPriority(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong surfaceControlObj, jint priority) { + auto transaction = reinterpret_cast(transactionObj); + SurfaceControl* const surfaceControl = reinterpret_cast(surfaceControlObj); + transaction->setContentPriority(surfaceControl, priority); +} + static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint cachingHint) { auto transaction = reinterpret_cast(transactionObj); @@ -2351,6 +2367,20 @@ static jboolean nativeBootFinished(JNIEnv* env, jclass clazz) { return error == OK ? JNI_TRUE : JNI_FALSE; } +static jint nativeGetMaxPictureProfiles(JNIEnv* env, jclass clazz) { + const auto displayIds = SurfaceComposerClient::SurfaceComposerClient::getPhysicalDisplayIds(); + int largestMaxProfiles = 0; + for (auto displayId : displayIds) { + sp token = SurfaceComposerClient::getPhysicalDisplayToken(displayId); + int32_t maxProfiles = 0; + SurfaceComposerClient::getMaxLayerPictureProfiles(token, &maxProfiles); + if (maxProfiles > largestMaxProfiles) { + largestMaxProfiles = maxProfiles; + } + } + return largestMaxProfiles; +} + jlong nativeCreateTpc(JNIEnv* env, jclass clazz, jobject trustedPresentationCallback) { return reinterpret_cast( new TrustedPresentationCallbackWrapper(env, trustedPresentationCallback)); @@ -2672,6 +2702,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetDefaultApplyToken }, {"nativeBootFinished", "()Z", (void*)nativeBootFinished }, + {"nativeGetMaxPictureProfiles", "()I", + (void*)nativeGetMaxPictureProfiles }, {"nativeCreateTpc", "(Landroid/view/SurfaceControl$TrustedPresentationCallback;)J", (void*)nativeCreateTpc}, {"getNativeTrustedPresentationCallbackFinalizer", "()J", (void*)getNativeTrustedPresentationCallbackFinalizer }, @@ -2683,6 +2715,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeNotifyShutdown }, {"nativeSetLuts", "(JJ[F[I[I[I[I)V", (void*)nativeSetLuts }, {"nativeEnableDebugLogCallPoints", "(J)V", (void*)nativeEnableDebugLogCallPoints }, + {"nativeSetPictureProfileId", "(JJJ)V", (void*)nativeSetPictureProfileId }, + {"nativeSetContentPriority", "(JJI)V", (void*)nativeSetContentPriority }, // clang-format on }; diff --git a/core/jni/android_view_SurfaceControlActivePictureListener.cpp b/core/jni/android_view_SurfaceControlActivePictureListener.cpp new file mode 100644 index 000000000000..91849c1514cc --- /dev/null +++ b/core/jni/android_view_SurfaceControlActivePictureListener.cpp @@ -0,0 +1,191 @@ +/* + * Copyright 2021 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. + */ + +#define LOG_TAG "SurfaceControlActivePictureListener" + +#include +#include +#include +#include +#include +#include +#include + +#include "android_util_Binder.h" +#include "core_jni_helpers.h" + +namespace android { + +namespace { + +struct { + jclass clazz; + jmethodID onActivePicturesChanged; +} gListenerClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; +} gActivePictureClassInfo; + +static struct { + jclass clazz; + jmethodID constructor; + jfieldID id; +} gPictureProfileHandleClassInfo; + +struct SurfaceControlActivePictureListener : public gui::BnActivePictureListener { + SurfaceControlActivePictureListener(JNIEnv* env, jobject listener) + : mListener(env->NewGlobalRef(listener)) { + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mVm) != JNI_OK, "Failed to GetJavaVm"); + } + + binder::Status onActivePicturesChanged( + const std::vector& activePictures) override { + JNIEnv* env = requireEnv(); + + ScopedLocalRef activePictureArrayObj(env); + activePictureArrayObj.reset( + env->NewObjectArray(activePictures.size(), gActivePictureClassInfo.clazz, NULL)); + if (env->ExceptionCheck() || !activePictureArrayObj.get()) { + LOGE_EX(env); + LOG_ALWAYS_FATAL("Failed to create an active picture array."); + } + + { + std::vector> pictureProfileHandleObjs; + std::vector> activePictureObjs; + + for (size_t i = 0; i < activePictures.size(); ++i) { + pictureProfileHandleObjs.push_back(ScopedLocalRef(env)); + pictureProfileHandleObjs[i].reset( + env->NewObject(gPictureProfileHandleClassInfo.clazz, + gPictureProfileHandleClassInfo.constructor, + activePictures[i].pictureProfileId)); + if (env->ExceptionCheck() || !pictureProfileHandleObjs[i].get()) { + LOGE_EX(env); + LOG_ALWAYS_FATAL("Failed to create a picture profile handle."); + } + activePictureObjs.push_back(ScopedLocalRef(env)); + activePictureObjs[i].reset(env->NewObject(gActivePictureClassInfo.clazz, + gActivePictureClassInfo.constructor, + activePictures[i].layerId, + activePictures[i].ownerUid, + pictureProfileHandleObjs[i].get())); + if (env->ExceptionCheck() || !activePictureObjs[i].get()) { + LOGE_EX(env); + LOG_ALWAYS_FATAL("Failed to create an active picture."); + } + env->SetObjectArrayElement(activePictureArrayObj.get(), i, + activePictureObjs[i].get()); + } + + env->CallVoidMethod(mListener, gListenerClassInfo.onActivePicturesChanged, + activePictureArrayObj.get()); + } + + if (env->ExceptionCheck()) { + ALOGE("SurfaceControlActivePictureListener.onActivePicturesChanged failed"); + LOGE_EX(env); + env->ExceptionClear(); + } + return binder::Status::ok(); + } + + status_t startListening() { + // TODO(b/337330263): Make SF multiple-listener capable + return SurfaceComposerClient::setActivePictureListener(this); + } + + status_t stopListening() { + return SurfaceComposerClient::setActivePictureListener(nullptr); + } + +protected: + virtual ~SurfaceControlActivePictureListener() { + JNIEnv* env = requireEnv(); + env->DeleteGlobalRef(mListener); + } + + JNIEnv* requireEnv() { + JNIEnv* env = nullptr; + if (mVm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } + return env; + } + +private: + jobject mListener; + JavaVM* mVm; +}; + +jlong nativeMakeAndStartListening(JNIEnv* env, jobject jthis) { + auto listener = sp::make(env, jthis); + status_t err = listener->startListening(); + if (err != OK) { + auto errStr = statusToString(err); + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Failed to start listening, err = %d (%s)", err, errStr.c_str()); + return 0; + } + SurfaceControlActivePictureListener* listenerRawPtr = listener.get(); + listenerRawPtr->incStrong(0); + return static_cast(reinterpret_cast(listenerRawPtr)); +} + +static void destroy(SurfaceControlActivePictureListener* listener) { + listener->stopListening(); + listener->decStrong(0); +} + +static jlong nativeGetDestructor(JNIEnv* env, jobject clazz) { + return static_cast(reinterpret_cast(&destroy)); +} + +const JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + {"nativeGetDestructor", "()J", (void*)nativeGetDestructor}, + {"nativeMakeAndStartListening", "()J", (void*)nativeMakeAndStartListening}}; +} // namespace + +int register_android_view_SurfaceControlActivePictureListener(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "android/view/SurfaceControlActivePictureListener", + gMethods, NELEM(gMethods)); + LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass listenerClazz = env->FindClass("android/view/SurfaceControlActivePictureListener"); + gListenerClassInfo.clazz = MakeGlobalRefOrDie(env, listenerClazz); + gListenerClassInfo.onActivePicturesChanged = + env->GetMethodID(listenerClazz, "onActivePicturesChanged", + "([Landroid/view/SurfaceControlActivePicture;)V"); + + gActivePictureClassInfo.clazz = static_cast( + env->NewGlobalRef(env->FindClass("android/view/SurfaceControlActivePicture"))); + gActivePictureClassInfo.constructor = + env->GetMethodID(gActivePictureClassInfo.clazz, "", + "(IILandroid/media/quality/PictureProfileHandle;)V"); + + gPictureProfileHandleClassInfo.clazz = static_cast( + env->NewGlobalRef(env->FindClass("android/media/quality/PictureProfileHandle"))); + gPictureProfileHandleClassInfo.constructor = + env->GetMethodID(gPictureProfileHandleClassInfo.clazz, "", "(J)V"); + return 0; +} + +} // namespace android diff --git a/media/java/android/media/quality/PictureProfileHandle.java b/media/java/android/media/quality/PictureProfileHandle.java index 2b1cda4eb742..714fd36d664a 100644 --- a/media/java/android/media/quality/PictureProfileHandle.java +++ b/media/java/android/media/quality/PictureProfileHandle.java @@ -17,46 +17,58 @@ package android.media.quality; import android.annotation.FlaggedApi; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; -import androidx.annotation.NonNull; - -// TODO(b/337330263): Expose as public API after API review /** - * A type-safe handle to a picture profile, which represents a collection of parameters used to - * configure picture processing hardware to enhance the quality of graphic buffers. + * A type-safe handle to a picture profile used to apply picture processing to a SurfaceControl. + * + * A picture profile represents a collection of parameters used to configure picture processing + * to enhance the quality of graphic buffers. + * * @hide */ -@FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) +@SystemApi +@FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public final class PictureProfileHandle implements Parcelable { + public static final @NonNull PictureProfileHandle NONE = new PictureProfileHandle(0); + private final long mId; - @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + /** @hide */ public PictureProfileHandle(long id) { mId = id; } - @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + /** @hide */ + @SystemApi + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public long getId() { return mId; } - @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + /** @hide */ + @SystemApi @Override + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mId); } - @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) + /** @hide */ + @SystemApi @Override + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) public int describeContents() { return 0; } - @FlaggedApi(android.media.tv.flags.Flags.FLAG_MEDIA_QUALITY_FW) - @NonNull - public static final Creator CREATOR = + /** @hide */ + @SystemApi + @FlaggedApi(android.media.tv.flags.Flags.FLAG_APPLY_PICTURE_PROFILES) + public static final @NonNull Creator CREATOR = new Creator() { @Override public PictureProfileHandle createFromParcel(Parcel in) { diff --git a/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml b/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml index d8eb9ff37e78..da510fcd4d63 100644 --- a/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml +++ b/tests/CtsSurfaceControlTestsStaging/AndroidManifest.xml @@ -18,12 +18,20 @@ + + + +