diff options
227 files changed, 4807 insertions, 1335 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index ce3e985e22d5..b54022bf47f2 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -64,6 +64,7 @@ aconfig_srcjars = [ ":android.hardware.usb.flags-aconfig-java{.generated_srcjars}", ":android.tracing.flags-aconfig-java{.generated_srcjars}", ":android.appwidget.flags-aconfig-java{.generated_srcjars}", + ":android.webkit.flags-aconfig-java{.generated_srcjars}", ] filegroup { @@ -762,3 +763,19 @@ java_aconfig_library { aconfig_declarations: "android.appwidget.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } + +// WebView +aconfig_declarations { + name: "android.webkit.flags-aconfig", + package: "android.webkit", + srcs: [ + "core/java/android/webkit/*.aconfig", + "services/core/java/com/android/server/webkit/*.aconfig", + ], +} + +java_aconfig_library { + name: "android.webkit.flags-aconfig-java", + aconfig_declarations: "android.webkit.flags-aconfig", + defaults: ["framework-minus-apex-aconfig-java-defaults"], +} diff --git a/Ravenwood.bp b/Ravenwood.bp index 03a23ba15273..ca73378c8e81 100644 --- a/Ravenwood.bp +++ b/Ravenwood.bp @@ -32,7 +32,6 @@ java_genrule { cmd: "$(location hoststubgen) " + "@$(location ravenwood/ravenwood-standard-options.txt) " + - "--out-stub-jar $(location ravenwood_stub.jar) " + "--out-impl-jar $(location ravenwood.jar) " + "--gen-keep-all-file $(location hoststubgen_keep_all.txt) " + @@ -49,7 +48,6 @@ java_genrule { ], out: [ "ravenwood.jar", - "ravenwood_stub.jar", // It's not used. TODO: Update hoststubgen to make it optional. // Following files are created just as FYI. "hoststubgen_keep_all.txt", diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp index 9366ff2d81a9..e1b3241e051e 100644 --- a/apct-tests/perftests/core/Android.bp +++ b/apct-tests/perftests/core/Android.bp @@ -66,6 +66,7 @@ android_test { errorprone: { javacflags: [ "-Xep:ReturnValueIgnored:WARN", + "-Xep:UnnecessaryStringBuilder:OFF", ], }, } diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java index c42c7ca25133..2ace6028456e 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java @@ -128,6 +128,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration(); final InsetsState mOutInsetsState = new InsetsState(); final InsetsSourceControl.Array mOutControls = new InsetsSourceControl.Array(); + final Bundle mOutBundle = new Bundle(); final IWindow mWindow; final View mView; final WindowManager.LayoutParams mParams; @@ -136,7 +137,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final SurfaceControl mOutSurfaceControl; final IntSupplier mViewVisibility; - + int mRelayoutSeq; int mFlags; RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) { @@ -152,10 +153,11 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase void runBenchmark(BenchmarkState state) throws RemoteException { final IWindowSession session = WindowManagerGlobal.getWindowSession(); while (state.keepRunning()) { + mRelayoutSeq++; session.relayout(mWindow, mParams, mWidth, mHeight, - mViewVisibility.getAsInt(), mFlags, 0 /* seq */, 0 /* lastSyncSeqId */, + mViewVisibility.getAsInt(), mFlags, mRelayoutSeq, 0 /* lastSyncSeqId */, mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, - mOutControls, new Bundle()); + mOutControls, mOutBundle); } } } diff --git a/api/Android.bp b/api/Android.bp index d6c14fbdfae3..2b1cfcb82d04 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -404,3 +404,49 @@ build = [ "ApiDocs.bp", "StubLibraries.bp", ] + +genrule_defaults { + name: "flag-api-mapping-generation-defaults", + cmd: "$(location extract-flagged-apis) $(in) $(out)", + tools: ["extract-flagged-apis"], +} + +genrule { + name: "flag-api-mapping-PublicApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-current.txt"], + out: ["flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-SystemApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-system-current.txt"], + out: ["system_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-ModuleLibApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-module-lib-current.txt"], + out: ["module_lib_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} + +genrule { + name: "flag-api-mapping-SystemServerApi", + defaults: ["flag-api-mapping-generation-defaults"], + srcs: [":frameworks-base-api-system-server-current.txt"], + out: ["system_server_flag_api_map.textproto"], + dist: { + targets: ["droid"], + }, +} diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt index 948e64f22f20..9ffb70496c59 100644 --- a/api/coverage/tools/ExtractFlaggedApis.kt +++ b/api/coverage/tools/ExtractFlaggedApis.kt @@ -25,7 +25,7 @@ fun main(args: Array<String>) { var cb = ApiFile.parseApi(listOf(File(args[0]))) val flagToApi = mutableMapOf<String, MutableList<String>>() cb.getPackages() - .allTopLevelClasses() + .allClasses() .filter { it.methods().size > 0 } .forEach { for (method in it.methods()) { diff --git a/core/api/current.txt b/core/api/current.txt index 207abb216c37..812d4cd67ab7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -12781,6 +12781,7 @@ package android.content.pm { method public boolean isPackageSuspended(); method @CheckResult public abstract boolean isPermissionRevokedByPolicy(@NonNull String, @NonNull String); method public abstract boolean isSafeMode(); + method @FlaggedApi("android.content.pm.get_package_info") @WorkerThread public <T> T parseAndroidManifest(@NonNull String, @NonNull java.util.function.Function<android.content.res.XmlResourceParser,T>) throws java.io.IOException; method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryActivityProperty(@NonNull String); method @NonNull public java.util.List<android.content.pm.PackageManager.Property> queryApplicationProperty(@NonNull String); method @NonNull public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(@NonNull android.content.Intent, int); @@ -36759,6 +36760,7 @@ package android.provider { field public static final String ACTION_APP_OPEN_BY_DEFAULT_SETTINGS = "android.settings.APP_OPEN_BY_DEFAULT_SETTINGS"; field public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS"; field public static final String ACTION_APP_USAGE_SETTINGS = "android.settings.action.APP_USAGE_SETTINGS"; + field @FlaggedApi("android.app.modes_api") public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS"; field public static final String ACTION_AUTO_ROTATE_SETTINGS = "android.settings.AUTO_ROTATE_SETTINGS"; field public static final String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS"; field public static final String ACTION_BIOMETRIC_ENROLL = "android.settings.BIOMETRIC_ENROLL"; @@ -36846,6 +36848,7 @@ package android.provider { field public static final String EXTRA_AIRPLANE_MODE_ENABLED = "airplane_mode_enabled"; field public static final String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE"; field public static final String EXTRA_AUTHORITIES = "authorities"; + field @FlaggedApi("android.app.modes_api") public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID"; field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled"; field public static final String EXTRA_BIOMETRIC_AUTHENTICATORS_ALLOWED = "android.provider.extra.BIOMETRIC_AUTHENTICATORS_ALLOWED"; field public static final String EXTRA_CHANNEL_FILTER_LIST = "android.provider.extra.CHANNEL_FILTER_LIST"; @@ -45159,6 +45162,7 @@ package android.telephony { method public void addOnSubscriptionsChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void addSubscriptionsIntoGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); method public boolean canManageSubscription(android.telephony.SubscriptionInfo); + method @FlaggedApi("com.android.internal.telephony.flags.work_profile_api_split") @NonNull public android.telephony.SubscriptionManager createForAllUserProfiles(); method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.os.ParcelUuid createSubscriptionGroup(@NonNull java.util.List<java.lang.Integer>); method @Deprecated public static android.telephony.SubscriptionManager from(android.content.Context); method public java.util.List<android.telephony.SubscriptionInfo> getAccessibleSubscriptionInfoList(); @@ -54183,7 +54187,7 @@ package android.view.accessibility { method public boolean isEnabled(); method public boolean isFocusable(); method public boolean isFocused(); - method public boolean isGranularScrollingSupported(); + method @FlaggedApi("android.view.accessibility.granular_scrolling") public boolean isGranularScrollingSupported(); method public boolean isHeading(); method public boolean isImportantForAccessibility(); method public boolean isLongClickable(); @@ -54233,7 +54237,7 @@ package android.view.accessibility { method public void setError(CharSequence); method public void setFocusable(boolean); method public void setFocused(boolean); - method public void setGranularScrollingSupported(boolean); + method @FlaggedApi("android.view.accessibility.granular_scrolling") public void setGranularScrollingSupported(boolean); method public void setHeading(boolean); method public void setHintText(CharSequence); method public void setImportantForAccessibility(boolean); @@ -54288,7 +54292,7 @@ package android.view.accessibility { field public static final String ACTION_ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT = "android.view.accessibility.action.ARGUMENT_PRESS_AND_HOLD_DURATION_MILLIS_INT"; field public static final String ACTION_ARGUMENT_PROGRESS_VALUE = "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE"; field public static final String ACTION_ARGUMENT_ROW_INT = "android.view.accessibility.action.ARGUMENT_ROW_INT"; - field public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; + field @FlaggedApi("android.view.accessibility.granular_scrolling") public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; field public static final String ACTION_ARGUMENT_SELECTION_END_INT = "ACTION_ARGUMENT_SELECTION_END_INT"; field public static final String ACTION_ARGUMENT_SELECTION_START_INT = "ACTION_ARGUMENT_SELECTION_START_INT"; field public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE = "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE"; @@ -54391,10 +54395,9 @@ package android.view.accessibility { public static final class AccessibilityNodeInfo.CollectionInfo { ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean); ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int); - ctor public AccessibilityNodeInfo.CollectionInfo(int, int, boolean, int, int, int); method public int getColumnCount(); - method public int getImportantForAccessibilityItemCount(); - method public int getItemCount(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getImportantForAccessibilityItemCount(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") public int getItemCount(); method public int getRowCount(); method public int getSelectionMode(); method public boolean isHierarchical(); @@ -54403,18 +54406,18 @@ package android.view.accessibility { field public static final int SELECTION_MODE_MULTIPLE = 2; // 0x2 field public static final int SELECTION_MODE_NONE = 0; // 0x0 field public static final int SELECTION_MODE_SINGLE = 1; // 0x1 - field public static final int UNDEFINED = -1; // 0xffffffff - } - - public static final class AccessibilityNodeInfo.CollectionInfo.Builder { - ctor public AccessibilityNodeInfo.CollectionInfo.Builder(); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build(); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int); - method @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int); + field @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final int UNDEFINED = -1; // 0xffffffff + } + + @FlaggedApi("android.view.accessibility.collection_info_item_counts") public static final class AccessibilityNodeInfo.CollectionInfo.Builder { + ctor @FlaggedApi("android.view.accessibility.collection_info_item_counts") public AccessibilityNodeInfo.CollectionInfo.Builder(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo build(); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setColumnCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setHierarchical(boolean); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setImportantForAccessibilityItemCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setItemCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setRowCount(int); + method @FlaggedApi("android.view.accessibility.collection_info_item_counts") @NonNull public android.view.accessibility.AccessibilityNodeInfo.CollectionInfo.Builder setSelectionMode(int); } public static final class AccessibilityNodeInfo.CollectionItemInfo { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 32d252ebda29..275fe7790e84 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3868,8 +3868,9 @@ package android.content.pm { public class PackageInstaller { method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException; method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException; + method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void reportUnarchivalStatus(int, int, long, @Nullable android.app.PendingIntent) throws android.content.pm.PackageManager.NameNotFoundException; method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException; - method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; + method @FlaggedApi("android.content.pm.archiving") @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public void requestUnarchive(@NonNull String, @NonNull android.content.IntentSender) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL"; field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL"; @@ -3883,12 +3884,20 @@ package android.content.pm { field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ALL_USERS = "android.content.pm.extra.UNARCHIVE_ALL_USERS"; field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_ID = "android.content.pm.extra.UNARCHIVE_ID"; field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_PACKAGE_NAME = "android.content.pm.extra.UNARCHIVE_PACKAGE_NAME"; + field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS"; field public static final int LOCATION_DATA_APP = 0; // 0x0 field public static final int LOCATION_MEDIA_DATA = 2; // 0x2 field public static final int LOCATION_MEDIA_OBB = 1; // 0x1 field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0 field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1 field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; // 0x4 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; // 0x5 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; // 0x2 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; // 0x3 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; // 0x1 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_GENERIC_ERROR = 100; // 0x64 + field @FlaggedApi("android.content.pm.archiving") public static final int UNARCHIVAL_OK = 0; // 0x0 } public static class PackageInstaller.InstallInfo { diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bb335fae887b..b5f7f232660b 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIO import static android.Manifest.permission.DETECT_SCREEN_CAPTURE; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.inMultiWindowMode; import static android.os.Process.myUid; @@ -9439,6 +9440,15 @@ public class Activity extends ContextThemeWrapper ActivityClient.getInstance().enableTaskLocaleOverride(mToken); } + /** + * Request ActivityRecordInputSink to enable or disable blocking input events. + * @hide + */ + @RequiresPermission(INTERNAL_SYSTEM_WINDOW) + public void setActivityRecordInputSinkEnabled(boolean enabled) { + ActivityClient.getInstance().setActivityRecordInputSinkEnabled(mToken, enabled); + } + class HostCallbacks extends FragmentHostCallback<Activity> { public HostCallbacks() { super(Activity.this /*activity*/); diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index b35e87b541d3..b8bd030872c1 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -16,6 +16,8 @@ package android.app; +import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; + import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.ComponentName; @@ -614,6 +616,15 @@ public class ActivityClient { } } + @RequiresPermission(INTERNAL_SYSTEM_WINDOW) + void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) { + try { + getActivityClientController().setActivityRecordInputSinkEnabled(activityToken, enabled); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /** * Shows or hides a Camera app compat toggle for stretched issues with the requested state. * diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 02eaf0b3bbd3..8af12161cc08 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -232,6 +232,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.org.conscrypt.TrustedCertificateStore; import com.android.server.am.MemInfoDumpProto; +import com.android.window.flags.Flags; import dalvik.annotation.optimization.NeverCompile; import dalvik.system.AppSpecializationHooks; @@ -3713,7 +3714,13 @@ public final class ActivityThread extends ClientTransactionHandler final ArrayList<ResultInfo> list = new ArrayList<>(); list.add(new ResultInfo(id, requestCode, resultCode, data)); final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread); - clientTransaction.addCallback(ActivityResultItem.obtain(activityToken, list)); + final ActivityResultItem activityResultItem = ActivityResultItem.obtain( + activityToken, list); + if (Flags.bundleClientTransactionFlag()) { + clientTransaction.addTransactionItem(activityResultItem); + } else { + clientTransaction.addCallback(activityResultItem); + } try { mAppThread.scheduleTransaction(clientTransaction); } catch (RemoteException e) { @@ -4492,16 +4499,26 @@ public final class ActivityThread extends ClientTransactionHandler private void schedulePauseWithUserLeavingHint(ActivityClientRecord r) { final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.setLifecycleStateRequest(PauseActivityItem.obtain(r.token, + final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token, r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags, - /* dontReport */ false, /* autoEnteringPip */ false)); + /* dontReport */ false, /* autoEnteringPip */ false); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(pauseActivityItem); + } else { + transaction.setLifecycleStateRequest(pauseActivityItem); + } executeTransaction(transaction); } private void scheduleResume(ActivityClientRecord r) { final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.setLifecycleStateRequest(ResumeActivityItem.obtain(r.token, - /* isForward */ false, /* shouldSendCompatFakeFocus */ false)); + final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token, + /* isForward */ false, /* shouldSendCompatFakeFocus */ false); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(resumeActivityItem); + } else { + transaction.setLifecycleStateRequest(resumeActivityItem); + } executeTransaction(transaction); } @@ -6092,8 +6109,13 @@ public final class ActivityThread extends ClientTransactionHandler TransactionExecutorHelper.getLifecycleRequestForCurrentState(r); // Schedule the transaction. final ClientTransaction transaction = ClientTransaction.obtain(mAppThread); - transaction.addCallback(activityRelaunchItem); - transaction.setLifecycleStateRequest(lifecycleRequest); + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(activityRelaunchItem); + transaction.addTransactionItem(lifecycleRequest); + } else { + transaction.addCallback(activityRelaunchItem); + transaction.setLifecycleStateRequest(lifecycleRequest); + } executeTransaction(transaction); } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index ca6d8df1df12..a4c3bb824502 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -80,6 +80,8 @@ import android.content.pm.SuspendDialogInfo; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VersionedPackage; import android.content.pm.dex.ArtManager; +import android.content.pm.parsing.ApkLiteParseUtils; +import android.content.res.ApkAssets; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -144,6 +146,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.function.Function; /** @hide */ public class ApplicationPackageManager extends PackageManager { @@ -4024,4 +4027,34 @@ public class ApplicationPackageManager extends PackageManager { throw e.rethrowFromSystemServer(); } } + + @Override + public <T> T parseAndroidManifest(@NonNull String apkFilePath, + @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException { + Objects.requireNonNull(apkFilePath, "apkFilePath cannot be null"); + Objects.requireNonNull(parserFunction, "parserFunction cannot be null"); + try (XmlResourceParser xmlResourceParser = getAndroidManifestParser(apkFilePath)) { + return parserFunction.apply(xmlResourceParser); + } catch (IOException e) { + Log.w(TAG, "Failed to get the android manifest parser", e); + throw e; + } + } + + private static XmlResourceParser getAndroidManifestParser(@NonNull String apkFilePath) + throws IOException { + ApkAssets apkAssets = null; + try { + apkAssets = ApkAssets.loadFromPath(apkFilePath); + return apkAssets.openXml(ApkLiteParseUtils.ANDROID_MANIFEST_FILENAME); + } finally { + if (apkAssets != null) { + try { + apkAssets.close(); + } catch (Throwable ignored) { + Log.w(TAG, "Failed to close apkAssets", ignored); + } + } + } + } } diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index a3c5e1c67e1b..7370fc36c23e 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -191,4 +191,14 @@ interface IActivityClientController { */ boolean isRequestedToLaunchInTaskFragment(in IBinder activityToken, in IBinder taskFragmentToken); + + /** + * Enable or disable ActivityRecordInputSink to block input events. + * + * @param token The token for the activity that requests to toggle. + * @param enabled Whether the input evens are blocked by ActivityRecordInputSink. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.INTERNAL_SYSTEM_WINDOW)") + oneway void setActivityRecordInputSinkEnabled(in IBinder activityToken, boolean enabled); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 56d0d1f2843d..6c9c14fbc83a 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -51,6 +51,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; +import android.provider.Settings; import android.provider.Settings.Global; import android.service.notification.Adjustment; import android.service.notification.Condition; @@ -1253,7 +1254,8 @@ public class NotificationManager { * <p> * If this method returns true, calls to * {@link #updateAutomaticZenRule(String, AutomaticZenRule)} may fail and apps should defer - * rule management to system settings/uis. + * rule management to system settings/uis via + * {@link Settings#ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}. */ @FlaggedApi(Flags.FLAG_MODES_API) public boolean areAutomaticZenRulesUserManaged() { diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index cc56a1cee7a2..d8448dcb4f9c 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -72,6 +72,7 @@ per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS per-file *UiModeManager* = file:/packages/SystemUI/OWNERS +per-file notification.aconfig = file:/packages/SystemUI/OWNERS # PackageManager per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig index bbd07b8c21ea..35ce10223aa6 100644 --- a/core/java/android/app/admin/flags/flags.aconfig +++ b/core/java/android/app/admin/flags/flags.aconfig @@ -48,3 +48,10 @@ flag { description: "Update DumpSys to include information about migrated APIs in DPE" bug: "304999634" } + +flag { + name: "quiet_mode_credential_bug_fix" + namespace: "enterprise" + description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped." + bug: "293441361" +} diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 7c34cdefe95f..5e5526802fe9 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -75,7 +75,6 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { /** * Adds a message to the end of the sequence of transaction items. * @param item A single message that can contain a client activity/window request/callback. - * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}. */ public void addTransactionItem(@NonNull ClientTransactionItem item) { if (mTransactionItems == null) { diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index dfbccb41d045..475c6fb9a48a 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -235,7 +235,9 @@ public class TransactionExecutorHelper { * Configuration - ActivityResult - Configuration - ActivityResult * index 1 will be returned, because ActivityResult request on position 1 will be the last * request that moves activity to the RESUMED state where it will eventually end. + * @deprecated to be removed with {@link TransactionExecutor#executeCallbacks}. */ + @Deprecated static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) { final List<ClientTransactionItem> callbacks = transaction.getCallbacks(); if (callbacks == null || callbacks.isEmpty() diff --git a/core/java/android/app/time/TEST_MAPPING b/core/java/android/app/time/TEST_MAPPING index 49a4467b1f67..47a152aa6cca 100644 --- a/core/java/android/app/time/TEST_MAPPING +++ b/core/java/android/app/time/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/timedetector/TEST_MAPPING b/core/java/android/app/timedetector/TEST_MAPPING index c050a55a3e18..9517fb99b04a 100644 --- a/core/java/android/app/timedetector/TEST_MAPPING +++ b/core/java/android/app/timedetector/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING index 46656d125b70..fd41b869efaf 100644 --- a/core/java/android/app/timezonedetector/TEST_MAPPING +++ b/core/java/android/app/timezonedetector/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.app." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "FrameworksServicesTests", "options": [ diff --git a/core/java/android/app/usage/ParcelableUsageEventList.java b/core/java/android/app/usage/ParcelableUsageEventList.java index 016d97f60e26..aa3239237478 100644 --- a/core/java/android/app/usage/ParcelableUsageEventList.java +++ b/core/java/android/app/usage/ParcelableUsageEventList.java @@ -48,13 +48,16 @@ public final class ParcelableUsageEventList implements Parcelable { private List<Event> mList; - public ParcelableUsageEventList(List<Event> list) { + public ParcelableUsageEventList(@NonNull List<Event> list) { + if (list == null) { + throw new IllegalArgumentException("Empty list"); + } mList = list; } private ParcelableUsageEventList(Parcel in) { final int N = in.readInt(); - mList = new ArrayList<>(); + mList = new ArrayList<>(N); if (DEBUG) Log.d(TAG, "Retrieving " + N + " items"); if (N <= 0) { return; diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 1eb452cfd085..6c7eba06dea7 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -24,9 +24,11 @@ import android.content.res.Configuration; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -35,6 +37,7 @@ import java.util.List; * from which to read {@link android.app.usage.UsageEvents.Event} objects. */ public final class UsageEvents implements Parcelable { + private static final String TAG = "UsageEvents"; /** @hide */ public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app"; @@ -786,10 +789,20 @@ public final class UsageEvents implements Parcelable { } private void readUsageEventsFromParcelWithParceledList(Parcel in) { + mEventCount = in.readInt(); mIndex = in.readInt(); - mEventsToWrite = in.readParcelable(UsageEvents.class.getClassLoader(), - ParcelableUsageEventList.class).getList(); - mEventCount = mEventsToWrite.size(); + ParcelableUsageEventList slice = in.readParcelable(getClass().getClassLoader(), + ParcelableUsageEventList.class); + if (slice != null) { + mEventsToWrite = slice.getList(); + } else { + mEventsToWrite = new ArrayList<>(); + } + + if (mEventCount != mEventsToWrite.size()) { + Log.w(TAG, "Partial usage event list received: " + mEventCount + " != " + + mEventsToWrite.size()); + } } private void readUsageEventsFromParcelWithBlob(Parcel in) { @@ -1065,6 +1078,7 @@ public final class UsageEvents implements Parcelable { } private void writeUsageEventsToParcelWithParceledList(Parcel dest, int flags) { + dest.writeInt(mEventCount); dest.writeInt(mIndex); dest.writeParcelable(new ParcelableUsageEventList(mEventsToWrite), flags); } diff --git a/core/java/android/companion/utils/FeatureUtils.java b/core/java/android/companion/utils/FeatureUtils.java deleted file mode 100644 index a382e09ae7b2..000000000000 --- a/core/java/android/companion/utils/FeatureUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.companion.utils; - -import android.os.Binder; -import android.os.Build; -import android.provider.DeviceConfig; - -/** - * Util class for feature flags - * - * @hide - */ -public final class FeatureUtils { - - private static final String NAMESPACE_COMPANION = "companion"; - - private static final String PROPERTY_PERM_SYNC_ENABLED = "perm_sync_enabled"; - - public static boolean isPermSyncEnabled() { - // Permissions sync is always enabled in debuggable mode. - if (Build.isDebuggable()) { - return true; - } - - // Clear app identity to read the device config for feature flag. - final long identity = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean(NAMESPACE_COMPANION, - PROPERTY_PERM_SYNC_ENABLED, false); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - private FeatureUtils() { - } -} diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 59ed0453bc01..1f25fd039dd8 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -16,6 +16,7 @@ package android.content.pm; +import android.app.PendingIntent; import android.content.pm.ArchivedPackageParcel; import android.content.pm.IPackageDeleteObserver2; import android.content.pm.IPackageInstallerCallback; @@ -82,7 +83,7 @@ interface IPackageInstaller { void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") - void requestUnarchive(String packageName, String callerPackageName, in UserHandle userHandle); + void requestUnarchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)") void installPackageArchived(in ArchivedPackageParcel archivedPackageParcel, @@ -90,4 +91,6 @@ interface IPackageInstaller { in IntentSender statusReceiver, String installerPackageName, in UserHandle userHandle); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})") + void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle); } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index e9a2aaad6579..4f0bfc7437c7 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -387,6 +387,24 @@ public class PackageInstaller { "android.content.pm.extra.UNARCHIVE_ALL_USERS"; /** + * Current status of an unarchive operation. Will be one of + * {@link #UNARCHIVAL_OK}, {@link #UNARCHIVAL_ERROR_USER_ACTION_NEEDED}, + * {@link #UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE}, {@link #UNARCHIVAL_ERROR_NO_CONNECTIVITY}, + * {@link #UNARCHIVAL_GENERIC_ERROR}, {@link #UNARCHIVAL_ERROR_INSTALLER_DISABLED} or + * {@link #UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED}. + * + * <p> If the status is not {@link #UNARCHIVAL_OK}, then {@link Intent#EXTRA_INTENT} will be set + * with an intent for a corresponding follow-up action (e.g. storage clearing dialog) or a + * failure dialog. + * + * @see #requestUnarchive + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final String EXTRA_UNARCHIVE_STATUS = "android.content.pm.extra.UNARCHIVE_STATUS"; + + /** * A list of warnings that occurred during installation. * * @hide @@ -652,6 +670,102 @@ public class PackageInstaller { @Retention(RetentionPolicy.SOURCE) public @interface UserActionReason {} + /** + * The unarchival is possible and will commence. + * + * <p> Note that this does not mean that the unarchival has completed. This status should be + * sent before any longer asynchronous action (e.g. app download) is started. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_OK = 0; + + /** + * The user needs to interact with the installer to enable the installation. + * + * <p> An example use case for this could be that the user needs to login to allow the + * download for a paid app. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_ERROR_USER_ACTION_NEEDED = 1; + + /** + * Not enough storage to unarchive the application. + * + * <p> The installer can optionally provide a {@code userActionIntent} for a space-clearing + * dialog. If no action is provided, then a generic intent + * {@link android.os.storage.StorageManager#ACTION_MANAGE_STORAGE} is started instead. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE = 2; + + /** + * The device is not connected to the internet + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_ERROR_NO_CONNECTIVITY = 3; + + /** + * The installer responsible for the unarchival is disabled. + * + * <p> Should only be used by the system. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_ERROR_INSTALLER_DISABLED = 4; + + /** + * The installer responsible for the unarchival has been uninstalled + * + * <p> Should only be used by the system. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED = 5; + + /** + * Generic error: The app cannot be unarchived. + * + * @hide + */ + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public static final int UNARCHIVAL_GENERIC_ERROR = 100; + + /** + * The set of error types that can be set for + * {@link #reportUnarchivalStatus(int, int, PendingIntent)}. + * + * @hide + */ + @IntDef(value = { + UNARCHIVAL_OK, + UNARCHIVAL_ERROR_USER_ACTION_NEEDED, + UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE, + UNARCHIVAL_ERROR_NO_CONNECTIVITY, + UNARCHIVAL_ERROR_INSTALLER_DISABLED, + UNARCHIVAL_ERROR_INSTALLER_UNINSTALLED, + UNARCHIVAL_GENERIC_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UnarchivalStatus {} + + /** Default set of checksums - includes all available checksums. * @see Session#requestChecksums */ private static final int DEFAULT_CHECKSUMS = @@ -2238,8 +2352,8 @@ public class PackageInstaller { * Requests to archive a package which is currently installed. * * <p> During the archival process, the apps APKs and cache are removed from the device while - * the user data is kept. Through the {@link #requestUnarchive(String)} call, apps can be - * restored again through their responsible installer. + * the user data is kept. Through the {@link #requestUnarchive} call, apps + * can be restored again through their responsible installer. * * <p> Archived apps are returned as displayable apps through the {@link LauncherApps} APIs and * will be displayed to users with UI treatment to highlight that said apps are archived. If @@ -2278,6 +2392,10 @@ public class PackageInstaller { * <p> The installation will happen asynchronously and can be observed through * {@link android.content.Intent#ACTION_PACKAGE_ADDED}. * + * @param statusReceiver Callback used to notify whether the installer has accepted the + * unarchival request or an error has occurred. The status update will be + * sent though {@link EXTRA_UNARCHIVE_STATUS}. Only one status will be + * sent. * @throws PackageManager.NameNotFoundException If {@code packageName} isn't found or not * visible to the caller or if the package has no * installer on the device anymore to unarchive it. @@ -2290,10 +2408,10 @@ public class PackageInstaller { Manifest.permission.REQUEST_INSTALL_PACKAGES}) @SystemApi @FlaggedApi(Flags.FLAG_ARCHIVING) - public void requestUnarchive(@NonNull String packageName) + public void requestUnarchive(@NonNull String packageName, @NonNull IntentSender statusReceiver) throws IOException, PackageManager.NameNotFoundException { try { - mInstaller.requestUnarchive(packageName, mInstallerPackageName, + mInstaller.requestUnarchive(packageName, mInstallerPackageName, statusReceiver, new UserHandle(mUserId)); } catch (ParcelableException e) { e.maybeRethrow(IOException.class); @@ -2303,6 +2421,39 @@ public class PackageInstaller { } } + /** + * Reports the status of an unarchival to the system. + * + * @param unarchiveId the ID provided by the system as part of the + * intent.action.UNARCHIVE broadcast with EXTRA_UNARCHIVE_ID. + * @param status is used for the system to provide the user with necessary + * follow-up steps or errors. + * @param requiredStorageBytes If the error is UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE this field + * should be set to specify how many additional bytes of storage + * are required to unarchive the app. + * @param userActionIntent Optional intent to start a follow up action required to + * facilitate the unarchival flow (e.g. user needs to log in). + * @throws PackageManager.NameNotFoundException if no unarchival with {@code unarchiveId} exists + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.REQUEST_INSTALL_PACKAGES}) + @SystemApi + @FlaggedApi(Flags.FLAG_ARCHIVING) + public void reportUnarchivalStatus(int unarchiveId, @UnarchivalStatus int status, + long requiredStorageBytes, @Nullable PendingIntent userActionIntent) + throws PackageManager.NameNotFoundException { + try { + mInstaller.reportUnarchivalStatus(unarchiveId, status, requiredStorageBytes, + userActionIntent, new UserHandle(mUserId)); + } catch (ParcelableException e) { + e.maybeRethrow(PackageManager.NameNotFoundException.class); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + // (b/239722738) This class serves as a bridge between the PackageLite class, which // is a hidden class, and the consumers of this class. (e.g. InstallInstalling.java) // This is a part of an effort to remove dependency on hidden APIs and use SystemAPIs or @@ -2566,6 +2717,8 @@ public class PackageInstaller { public int developmentInstallFlags = 0; /** {@hide} */ public int unarchiveId = -1; + /** {@hide} */ + public IntentSender unarchiveIntentSender; private final ArrayMap<String, Integer> mPermissionStates; @@ -2618,6 +2771,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent = source.readBoolean(); developmentInstallFlags = source.readInt(); unarchiveId = source.readInt(); + unarchiveIntentSender = source.readParcelable(null, IntentSender.class); } /** {@hide} */ @@ -2652,6 +2806,7 @@ public class PackageInstaller { ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent; ret.developmentInstallFlags = developmentInstallFlags; ret.unarchiveId = unarchiveId; + ret.unarchiveIntentSender = unarchiveIntentSender; return ret; } @@ -3364,6 +3519,7 @@ public class PackageInstaller { applicationEnabledSettingPersistent); pw.printHexPair("developmentInstallFlags", developmentInstallFlags); pw.printPair("unarchiveId", unarchiveId); + pw.printPair("unarchiveIntentSender", unarchiveIntentSender); pw.println(); } @@ -3408,6 +3564,7 @@ public class PackageInstaller { dest.writeBoolean(applicationEnabledSettingPersistent); dest.writeInt(developmentInstallFlags); dest.writeInt(unarchiveId); + dest.writeParcelable(unarchiveIntentSender, flags); } public static final Parcelable.Creator<SessionParams> diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c3b3423c1a57..fe31c9dbf27d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -34,6 +34,7 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; +import android.annotation.WorkerThread; import android.annotation.XmlRes; import android.app.ActivityManager; import android.app.ActivityThread; @@ -96,6 +97,7 @@ import com.android.internal.util.DataClass; import dalvik.system.VMRuntime; import java.io.File; +import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.cert.Certificate; @@ -108,6 +110,7 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.function.Function; /** * Class for retrieving various kinds of information related to the application @@ -11426,4 +11429,60 @@ public abstract class PackageManager { throw new UnsupportedOperationException( "unregisterPackageMonitorCallback not implemented in subclass"); } + + /** + * Retrieve AndroidManifest.xml information for the given application apk path. + * + * <p>Example: + * + * <pre><code> + * Bundle result; + * try { + * result = getContext().getPackageManager().parseAndroidManifest(apkFilePath, + * xmlResourceParser -> { + * Bundle bundle = new Bundle(); + * // Search the start tag + * int type; + * while ((type = xmlResourceParser.next()) != XmlPullParser.START_TAG + * && type != XmlPullParser.END_DOCUMENT) { + * } + * if (type != XmlPullParser.START_TAG) { + * return bundle; + * } + * + * // Start to read the tags and attributes from the xmlResourceParser + * if (!xmlResourceParser.getName().equals("manifest")) { + * return bundle; + * } + * String packageName = xmlResourceParser.getAttributeValue(null, "package"); + * bundle.putString("package", packageName); + * + * // Continue to read the tags and attributes from the xmlResourceParser + * + * return bundle; + * }); + * } catch (IOException e) { + * } + * </code></pre> + * + * Note: When the parserFunction is invoked, the client can read the AndroidManifest.xml + * information by the XmlResourceParser object. After leaving the parserFunction, the + * XmlResourceParser object will be closed. + * + * @param apkFilePath The path of an application apk file. + * @param parserFunction The parserFunction will be invoked with the XmlResourceParser object + * after getting the AndroidManifest.xml of an application package. + * + * @return Returns the result of the {@link Function#apply(Object)}. + * + * @throws IOException if the AndroidManifest.xml of an application package cannot be + * read or accessed. + */ + @FlaggedApi(android.content.pm.Flags.FLAG_GET_PACKAGE_INFO) + @WorkerThread + public <T> T parseAndroidManifest(@NonNull String apkFilePath, + @NonNull Function<XmlResourceParser, T> parserFunction) throws IOException { + throw new UnsupportedOperationException( + "parseAndroidManifest not implemented in subclass"); + } } diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java index 7756b9ca7e5a..c3791888b44f 100644 --- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -22,9 +22,11 @@ import android.annotation.RequiresPermission; import android.content.Context; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.Trace; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; @@ -45,6 +47,8 @@ import java.util.concurrent.Executor; @VisibleForTesting(visibility = Visibility.PACKAGE) public final class DeviceStateManagerGlobal { private static DeviceStateManagerGlobal sInstance; + private static final String TAG = "DeviceStateManagerGlobal"; + private static final boolean DEBUG = Build.IS_DEBUGGABLE; /** * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a @@ -400,11 +404,29 @@ public final class DeviceStateManagerGlobal { } void notifyBaseStateChanged(int newBaseState) { - mExecutor.execute(() -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); + execute("notifyBaseStateChanged", + () -> mDeviceStateCallback.onBaseStateChanged(newBaseState)); } void notifyStateChanged(int newDeviceState) { - mExecutor.execute(() -> mDeviceStateCallback.onStateChanged(newDeviceState)); + execute("notifyStateChanged", + () -> mDeviceStateCallback.onStateChanged(newDeviceState)); + } + + private void execute(String traceName, Runnable r) { + mExecutor.execute(() -> { + if (DEBUG) { + Trace.beginSection( + mDeviceStateCallback.getClass().getSimpleName() + "#" + traceName); + } + try { + r.run(); + } finally { + if (DEBUG) { + Trace.endSection(); + } + } + }); } } diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java index f817fb8dcaed..1100731702a2 100644 --- a/core/java/android/os/BinderProxy.java +++ b/core/java/android/os/BinderProxy.java @@ -82,8 +82,8 @@ public final class BinderProxy implements IBinder { private static final int MAIN_INDEX_SIZE = 1 << LOG_MAIN_INDEX_SIZE; private static final int MAIN_INDEX_MASK = MAIN_INDEX_SIZE - 1; /** - * Debuggable builds will throw an BinderProxyMapSizeException if the number of - * map entries exceeds: + * We will throw a BinderProxyMapSizeException if the number of map entries + * exceeds: */ private static final int CRASH_AT_SIZE = 25_000; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 33c15d775ff1..ff6ec29bb8ac 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -18,6 +18,7 @@ package android.provider; import android.Manifest; import android.annotation.CallbackExecutor; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -36,6 +37,7 @@ import android.app.ActivityThread; import android.app.AppOpsManager; import android.app.Application; import android.app.AutomaticZenRule; +import android.app.Flags; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.SearchManager; @@ -1904,6 +1906,36 @@ public final class Settings { = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS"; /** + * Activity Action: Shows the settings page for an {@link AutomaticZenRule} mode. + * <p> + * Users can change the behavior of the mode when it's activated and access the owning app's + * additional configuration screen, where triggering criteria can be modified (see + * {@link AutomaticZenRule#setConfigurationActivity(ComponentName)}). + * <p> + * A matching Activity will only be found if + * {@link NotificationManager#areAutomaticZenRulesUserManaged()} is true. + * <p> + * Input: Intent's data URI set with an application name, using the "package" schema (like + * "package:com.my.app"). + * Input: The id of the rule, provided in {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID}. + * <p> + * Output: Nothing. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_AUTOMATIC_ZEN_RULE_SETTINGS + = "android.settings.AUTOMATIC_ZEN_RULE_SETTINGS"; + + /** + * Activity Extra: The String id of the {@link AutomaticZenRule mode} settings to display. + * <p> + * This must be passed as an extra field to the {@link #ACTION_AUTOMATIC_ZEN_RULE_SETTINGS}. + */ + @FlaggedApi(Flags.FLAG_MODES_API) + public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID + = "android.provider.extra.AUTOMATIC_ZEN_RULE_ID"; + + /** * Activity Action: Show settings for video captioning. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard diff --git a/core/java/android/service/notification/OWNERS b/core/java/android/service/notification/OWNERS index bb0e6aba436b..cb0b5fa6a029 100644 --- a/core/java/android/service/notification/OWNERS +++ b/core/java/android/service/notification/OWNERS @@ -2,6 +2,7 @@ juliacr@google.com yurilin@google.com +matiashe@google.com jeffdq@google.com dsandler@android.com dsandler@google.com diff --git a/core/java/android/service/timezone/TEST_MAPPING b/core/java/android/service/timezone/TEST_MAPPING index 21a8eab19837..e5910ea4a1fa 100644 --- a/core/java/android/service/timezone/TEST_MAPPING +++ b/core/java/android/service/timezone/TEST_MAPPING @@ -1,6 +1,5 @@ { - // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. - "postsubmit": [ + "presubmit": [ { "name": "FrameworksTimeCoreTests", "options": [ @@ -8,7 +7,10 @@ "include-filter": "android.service." } ] - }, + } + ], + // TODO(b/182461754): Change to "presubmit" when go/test-mapping-slo-guide allows. + "postsubmit": [ { "name": "CtsLocationTimeZoneManagerHostTest" } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 43bfe139c223..53aed498f110 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -23,7 +23,7 @@ import static java.util.Collections.EMPTY_LIST; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; -import android.annotation.Hide; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -752,6 +752,7 @@ public class AccessibilityNodeInfo implements Parcelable { * {@link #isGranularScrollingSupported()} to check if granular scrolling is supported. * </p> */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public static final String ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT = "android.view.accessibility.action.ARGUMENT_SCROLL_AMOUNT_FLOAT"; @@ -2608,6 +2609,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return True if all scroll actions that could support * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT} have done so, false otherwise. */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public boolean isGranularScrollingSupported() { return getBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING); } @@ -2626,6 +2628,7 @@ public class AccessibilityNodeInfo implements Parcelable { * * @throws IllegalStateException If called from an AccessibilityService. */ + @FlaggedApi(Flags.FLAG_GRANULAR_SCROLLING) public void setGranularScrollingSupported(boolean granularScrollingSupported) { setBooleanProperty(BOOLEAN_PROPERTY_SUPPORTS_GRANULAR_SCROLLING, granularScrollingSupported); @@ -6119,6 +6122,7 @@ public class AccessibilityNodeInfo implements Parcelable { * This should be used for {@code mItemCount} and * {@code mImportantForAccessibilityItemCount} when values for those fields are not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public static final int UNDEFINED = -1; private int mRowCount; @@ -6229,8 +6233,8 @@ public class AccessibilityNodeInfo implements Parcelable { * the item count is not known. * @param importantForAccessibilityItemCount The count of the collection's views considered * important for accessibility. + * @hide */ - @Hide public CollectionInfo(int rowCount, int columnCount, boolean hierarchical, int selectionMode, int itemCount, int importantForAccessibilityItemCount) { mRowCount = rowCount; @@ -6287,6 +6291,7 @@ public class AccessibilityNodeInfo implements Parcelable { * * @return The count of items, which may be {@code UNDEFINED} if the count is not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public int getItemCount() { return mItemCount; } @@ -6297,6 +6302,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return The count of items important for accessibility, which may be {@code UNDEFINED} * if the count is not known. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public int getImportantForAccessibilityItemCount() { return mImportantForAccessibilityItemCount; } @@ -6323,6 +6329,7 @@ public class AccessibilityNodeInfo implements Parcelable { * The builder for CollectionInfo. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public static final class Builder { private int mRowCount = 0; private int mColumnCount = 0; @@ -6334,6 +6341,7 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Creates a new Builder. */ + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public Builder() { } @@ -6343,6 +6351,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setRowCount(int rowCount) { mRowCount = rowCount; return this; @@ -6354,6 +6363,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setColumnCount(int columnCount) { mColumnCount = columnCount; return this; @@ -6364,6 +6374,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setHierarchical(boolean hierarchical) { mHierarchical = hierarchical; return this; @@ -6375,6 +6386,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setSelectionMode(int selectionMode) { mSelectionMode = selectionMode; return this; @@ -6389,6 +6401,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setItemCount(int itemCount) { mItemCount = itemCount; return this; @@ -6401,6 +6414,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @return This builder. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo.Builder setImportantForAccessibilityItemCount( int importantForAccessibilityItemCount) { mImportantForAccessibilityItemCount = importantForAccessibilityItemCount; @@ -6411,6 +6425,7 @@ public class AccessibilityNodeInfo implements Parcelable { * Creates a new {@link CollectionInfo} instance. */ @NonNull + @FlaggedApi(Flags.FLAG_COLLECTION_INFO_ITEM_COUNTS) public CollectionInfo build() { CollectionInfo collectionInfo = new CollectionInfo(mRowCount, mColumnCount, mHierarchical); diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig index 950fa4b1d711..c337cb45d3a6 100644 --- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig +++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig @@ -17,6 +17,13 @@ flag { } flag { + namespace: "accessibility" + name: "collection_info_item_counts" + description: "Fields for total items and the number of important for accessibility items in a collection" + bug: "302376158" +} + +flag { name: "deduplicate_accessibility_warning_dialog" namespace: "accessibility" description: "Removes duplicate definition of the accessibility warning dialog." @@ -39,6 +46,13 @@ flag { flag { namespace: "accessibility" + name: "granular_scrolling" + description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen" + bug: "302376158" +} + +flag { + namespace: "accessibility" name: "update_always_on_a11y_service" description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut." bug: "298869916" diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index 11bd22f7d6ff..0da03fb5aaeb 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -39,4 +39,12 @@ flag { description: "Remove uses of ScreenCapture#captureDisplay" is_fixed_read_only: true bug: "293445881" -}
\ No newline at end of file +} + +flag { + namespace: "window_surfaces" + name: "allow_disable_activity_record_input_sink" + description: "Whether to allow system activity to disable ActivityRecordInputSink" + is_fixed_read_only: true + bug: "262477923" +} diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index b600b22751ff..933cc49f8602 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -9,6 +9,16 @@ flag { bug: "260873529" } +# Using a fixed read only flag because there are ClientTransaction scheduling before +# WindowManagerService creation. +flag { + namespace: "windowing_sdk" + name: "bundle_client_transaction_flag" + description: "To bundle multiple ClientTransactionItems into one ClientTransaction" + bug: "260873529" + is_fixed_read_only: true +} + flag { namespace: "windowing_sdk" name: "activity_embedding_overlay_presentation_flag" diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index e3bb1fe8b736..7be27be2e798 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -54,18 +54,6 @@ public class SystemUiSystemPropertiesFlags { */ public static final class NotificationFlags { - /** - * FOR DEVELOPMENT / TESTING ONLY!!! - * Forcibly demote *ALL* FSI notifications as if no apps have the app op permission. - * NOTE: enabling this implies SHOW_STICKY_HUN_FOR_DENIED_FSI in SystemUI - */ - public static final Flag FSI_FORCE_DEMOTE = - devFlag("persist.sysui.notification.fsi_force_demote"); - - /** Gating the feature which shows FSI-denied notifications as Sticky HUNs */ - public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI = - releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi"); - /** Gating the logging of DND state change events. */ public static final Flag LOG_DND_STATE_EVENTS = releasedFlag("persist.sysui.notification.log_dnd_state_events"); diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 5c1d91ff540e..5b68e8ed1ad8 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -184,11 +184,11 @@ status_t NativeInputEventReceiver::reportTimeline(int32_t inputEventId, nsecs_t void NativeInputEventReceiver::setFdEvents(int events) { if (mFdEvents != events) { mFdEvents = events; - int fd = mInputConsumer.getChannel()->getFd(); + auto&& fd = mInputConsumer.getChannel()->getFd(); if (events) { - mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr); + mMessageQueue->getLooper()->addFd(fd.get(), 0, events, this, nullptr); } else { - mMessageQueue->getLooper()->removeFd(fd); + mMessageQueue->getLooper()->removeFd(fd.get()); } } } diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 833952def02b..6bdf8214a1bf 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -102,8 +102,8 @@ NativeInputEventSender::~NativeInputEventSender() { } status_t NativeInputEventSender::initialize() { - int receiveFd = mInputPublisher.getChannel()->getFd(); - mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL); + auto&& receiveFd = mInputPublisher.getChannel()->getFd(); + mMessageQueue->getLooper()->addFd(receiveFd.get(), 0, ALOOPER_EVENT_INPUT, this, NULL); return OK; } @@ -112,7 +112,7 @@ void NativeInputEventSender::dispose() { LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Disposing input event sender."; } - mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd()); + mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd().get()); } status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) { diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index 4b02257978d2..2327b20bada6 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -42,6 +42,7 @@ import android.app.IApplicationThread; import android.app.PictureInPictureParams; import android.app.ResourcesManager; import android.app.servertransaction.ActivityConfigurationChangeItem; +import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityRelaunchItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.ClientTransactionItem; @@ -73,6 +74,7 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.internal.content.ReferrerIntent; +import com.android.window.flags.Flags; import org.junit.After; import org.junit.Before; @@ -227,7 +229,8 @@ public class ActivityThreadTest { try { // Send process level config change. ClientTransaction transaction = newTransaction(activityThread); - transaction.addCallback(ConfigurationChangeItem.obtain(newConfig, DEVICE_ID_INVALID)); + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( + newConfig, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -243,7 +246,7 @@ public class ActivityThreadTest { newConfig.seq++; newConfig.smallestScreenWidthDp++; transaction = newTransaction(activityThread); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), newConfig)); appThread.scheduleTransaction(transaction); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -444,16 +447,16 @@ public class ActivityThreadTest { activity.mTestLatch = new CountDownLatch(1); ClientTransaction transaction = newTransaction(activityThread); - transaction.addCallback(ConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( processConfigLandscape, DEVICE_ID_INVALID)); appThread.scheduleTransaction(transaction); transaction = newTransaction(activityThread); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), activityConfigLandscape)); - transaction.addCallback(ConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ConfigurationChangeItem.obtain( processConfigPortrait, DEVICE_ID_INVALID)); - transaction.addCallback(ActivityConfigurationChangeItem.obtain( + addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain( activity.getActivityToken(), activityConfigPortrait)); appThread.scheduleTransaction(transaction); @@ -840,8 +843,8 @@ public class ActivityThreadTest { false /* shouldSendCompatFakeFocus*/); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(callbackItem); - transaction.setLifecycleStateRequest(resumeStateRequest); + addClientTransactionItem(transaction, callbackItem); + addClientTransactionItem(transaction, resumeStateRequest); return transaction; } @@ -853,7 +856,7 @@ public class ActivityThreadTest { false /* shouldSendCompatFakeFocus */); final ClientTransaction transaction = newTransaction(activity); - transaction.setLifecycleStateRequest(resumeStateRequest); + addClientTransactionItem(transaction, resumeStateRequest); return transaction; } @@ -864,7 +867,7 @@ public class ActivityThreadTest { activity.getActivityToken(), 0 /* configChanges */); final ClientTransaction transaction = newTransaction(activity); - transaction.setLifecycleStateRequest(stopStateRequest); + addClientTransactionItem(transaction, stopStateRequest); return transaction; } @@ -876,7 +879,7 @@ public class ActivityThreadTest { activity.getActivityToken(), config); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(item); + addClientTransactionItem(transaction, item); return transaction; } @@ -888,7 +891,7 @@ public class ActivityThreadTest { resume); final ClientTransaction transaction = newTransaction(activity); - transaction.addCallback(item); + addClientTransactionItem(transaction, item); return transaction; } @@ -903,6 +906,17 @@ public class ActivityThreadTest { return ClientTransaction.obtain(activityThread.getApplicationThread()); } + private static void addClientTransactionItem(@NonNull ClientTransaction transaction, + @NonNull ClientTransactionItem item) { + if (Flags.bundleClientTransactionFlag()) { + transaction.addTransactionItem(item); + } else if (item.isActivityLifecycleItem()) { + transaction.setLifecycleStateRequest((ActivityLifecycleItem) item); + } else { + transaction.addCallback(item); + } + } + // Test activity public static class TestActivity extends Activity { static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter"; diff --git a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java index 2ec58d43477d..5a202c5e8834 100644 --- a/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java +++ b/core/tests/coretests/src/android/app/usage/ParcelableUsageEventListTest.java @@ -20,6 +20,7 @@ import static android.view.Surface.ROTATION_90; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -43,11 +44,29 @@ import java.util.Random; @LargeTest public class ParcelableUsageEventListTest { private static final int SMALL_TEST_EVENT_COUNT = 100; - private static final int LARGE_TEST_EVENT_COUNT = 10000; + private static final int LARGE_TEST_EVENT_COUNT = 30000; private Random mRandom = new Random(); @Test + public void testNullList() throws Exception { + Parcel parcel = Parcel.obtain(); + try { + parcel.writeParcelable(new ParcelableUsageEventList(null), 0); + fail("Expected IllegalArgumentException with null list."); + } catch (IllegalArgumentException expected) { + // Expected. + } finally { + parcel.recycle(); + } + } + + @Test + public void testEmptyList() throws Exception { + testParcelableUsageEventList(0); + } + + @Test public void testSmallList() throws Exception { testParcelableUsageEventList(SMALL_TEST_EVENT_COUNT); } @@ -58,15 +77,15 @@ public class ParcelableUsageEventListTest { } private void testParcelableUsageEventList(int eventCount) throws Exception { - List<Event> smallList = new ArrayList<>(); + List<Event> eventList = new ArrayList<>(); for (int i = 0; i < eventCount; i++) { - smallList.add(generateUsageEvent()); + eventList.add(generateUsageEvent()); } ParcelableUsageEventList slice; Parcel parcel = Parcel.obtain(); try { - parcel.writeParcelable(new ParcelableUsageEventList(smallList), 0); + parcel.writeParcelable(new ParcelableUsageEventList(eventList), 0); parcel.setDataPosition(0); slice = parcel.readParcelable(getClass().getClassLoader(), ParcelableUsageEventList.class); @@ -79,7 +98,7 @@ public class ParcelableUsageEventListTest { assertEquals(eventCount, slice.getList().size()); for (int i = 0; i < eventCount; i++) { - compareUsageEvent(smallList.get(i), slice.getList().get(i)); + compareUsageEvent(eventList.get(i), slice.getList().get(i)); } } diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java index 07f1d4a09006..8dc9579e6b52 100644 --- a/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/HideInCommentsChecker.java @@ -63,7 +63,7 @@ public class HideInCommentsChecker extends BugChecker implements @Override public Description matchCompilationUnit(CompilationUnitTree tree, VisitorState state) { - final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree); + final Map<Integer, Tree> javadocableTrees = findJavadocableTrees(tree, state); final String sourceCode = state.getSourceCode().toString(); for (ErrorProneToken token : ErrorProneTokens.getTokens(sourceCode, state.context)) { for (Tokens.Comment comment : token.comments()) { @@ -112,9 +112,9 @@ public class HideInCommentsChecker extends BugChecker implements } - private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree) { + private Map<Integer, Tree> findJavadocableTrees(CompilationUnitTree tree, VisitorState state) { Map<Integer, Tree> javadoccableTrees = new HashMap<>(); - new SuppressibleTreePathScanner<Void, Void>() { + new SuppressibleTreePathScanner<Void, Void>(state) { @Override public Void visitClass(ClassTree classTree, Void unused) { javadoccableTrees.put(getStartPosition(classTree), classTree); diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index c366ccd235db..4d2d960822d1 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -42,3 +42,11 @@ flag { description: "Enables PiP UI state callback on entering" bug: "303718131" } + +flag { + name: "enable_pip2_implementation" + namespace: "multitasking" + description: "Enables the new implementation of PiP (PiP2)" + bug: "290220798" + is_fixed_read_only: true +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index c7ab6aa3934e..f5b877a70b84 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -963,7 +963,11 @@ public class BubbleExpandedView extends LinearLayout { && mTaskView.isAttachedToWindow()) { // post this to the looper, because if the device orientation just changed, we need to // let the current shell transition complete before updating the task view bounds. - post(() -> mTaskView.onLocationChanged()); + post(() -> { + if (mTaskView != null) { + mTaskView.onLocationChanged(); + } + }); } if (mIsOverflow) { // post this to the looper so that the view has a chance to be laid out before it can diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt index 108aa8275009..1e30d8feb132 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt @@ -21,13 +21,13 @@ import android.app.WindowConfiguration import android.content.ComponentName import android.content.Context import android.os.RemoteException -import android.os.SystemProperties import android.util.DisplayMetrics import android.util.Log import android.util.Pair import android.util.TypedValue import android.window.TaskSnapshot import com.android.internal.protolog.common.ProtoLog +import com.android.wm.shell.Flags import com.android.wm.shell.protolog.ShellProtoLogGroup import kotlin.math.abs @@ -37,7 +37,6 @@ object PipUtils { // Minimum difference between two floats (e.g. aspect ratios) to consider them not equal. private const val EPSILON = 1e-7 - private const val ENABLE_PIP2_IMPLEMENTATION = "persist.wm.debug.enable_pip2_implementation" /** * @return the ComponentName and user id of the top non-SystemUI activity in the pinned stack. @@ -138,5 +137,5 @@ object PipUtils { @JvmStatic val isPip2ExperimentEnabled: Boolean - get() = SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false) + get() = Flags.enablePip2Implementation() }
\ No newline at end of file diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index b63ee1bd3d98..a9d1a2aed8cc 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -25,8 +25,9 @@ #include "MinikinSkia.h" #include "SkPaint.h" -#include "SkStream.h" // Fot tests. +#include "SkStream.h" // For tests. #include "SkTypeface.h" +#include "utils/TypefaceUtils.h" #include <minikin/FontCollection.h> #include <minikin/FontFamily.h> @@ -186,7 +187,9 @@ void Typeface::setRobotoTypefaceForTest() { LOG_ALWAYS_FATAL_IF(fstat(fd, &st) == -1, "Failed to stat file %s", kRobotoFont); void* data = mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd, 0); std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data, st.st_size)); - sk_sp<SkTypeface> typeface = SkTypeface::MakeFromStream(std::move(fontData)); + sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr(); + LOG_ALWAYS_FATAL_IF(fm == nullptr, "Could not load FreeType SkFontMgr"); + sk_sp<SkTypeface> typeface = fm->makeFromStream(std::move(fontData)); LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", kRobotoFont); std::shared_ptr<minikin::MinikinFont> font = diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt index 4533db674da2..3abdb6f9c9f2 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/IntentKtx.kt @@ -37,17 +37,17 @@ val Intent.requestInfo: RequestInfo? RequestInfo::class.java ) -val Intent.getCredentialProviderDataList: List<ProviderData> +val Intent.getCredentialProviderDataList: List<GetCredentialProviderData> get() = this.extras?.getParcelableArrayList( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, GetCredentialProviderData::class.java - ) ?: emptyList() + ) ?.filterIsInstance<GetCredentialProviderData>() ?: emptyList() -val Intent.createCredentialProviderDataList: List<ProviderData> +val Intent.createCredentialProviderDataList: List<CreateCredentialProviderData> get() = this.extras?.getParcelableArrayList( ProviderData.EXTRA_ENABLED_PROVIDER_DATA_LIST, CreateCredentialProviderData::class.java - ) ?: emptyList() + ) ?.filterIsInstance<CreateCredentialProviderData>() ?: emptyList() val Intent.resultReceiver: ResultReceiver? get() = this.getParcelableExtra( diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt index ee45fbb00ba6..d4bca2add6cb 100644 --- a/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt +++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/mapper/RequestGetMapper.kt @@ -18,7 +18,6 @@ package com.android.credentialmanager.mapper import android.content.Intent import android.credentials.ui.Entry -import android.credentials.ui.GetCredentialProviderData import androidx.credentials.provider.PasswordCredentialEntry import com.android.credentialmanager.factory.fromSlice import com.android.credentialmanager.ktx.getCredentialProviderDataList @@ -32,12 +31,10 @@ import com.google.common.collect.ImmutableMap fun Intent.toGet(): Request.Get { val credentialEntries = mutableListOf<Pair<String, Entry>>() for (providerData in getCredentialProviderDataList) { - if (providerData is GetCredentialProviderData) { - for (credentialEntry in providerData.credentialEntries) { - credentialEntries.add( - Pair(providerData.providerFlattenedComponentName, credentialEntry) - ) - } + for (credentialEntry in providerData.credentialEntries) { + credentialEntries.add( + Pair(providerData.providerFlattenedComponentName, credentialEntry) + ) } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 2c15fc6a9cfd..8412cbaaea36 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -249,6 +249,8 @@ public class SecureSettings { Settings.Secure.HUB_MODE_TUTORIAL_STATE, Settings.Secure.STYLUS_BUTTONS_ENABLED, Settings.Secure.STYLUS_HANDWRITING_ENABLED, - Settings.Secure.DEFAULT_NOTE_TASK_PROFILE + Settings.Secure.DEFAULT_NOTE_TASK_PROFILE, + Settings.Secure.CREDENTIAL_SERVICE, + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 71c2ddc6de1f..9197554662d3 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -19,11 +19,13 @@ package android.provider.settings.validators; import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COMMA_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.NONE_NEGATIVE_LONG_VALIDATOR; @@ -62,7 +64,6 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ADAPTIVE_CHARGING_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.CAMERA_AUTOROTATE, BOOLEAN_VALIDATOR); - VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR); VALIDATORS.put( Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE)); @@ -398,5 +399,8 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.STYLUS_HANDWRITING_ENABLED, new DiscreteValueValidator(new String[] {"-1", "0", "1"})); VALIDATORS.put(Secure.DEFAULT_NOTE_TASK_PROFILE, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.CREDENTIAL_SERVICE, CREDENTIAL_SERVICE_VALIDATOR); + VALIDATORS.put(Secure.CREDENTIAL_SERVICE_PRIMARY, NULLABLE_COMPONENT_NAME_VALIDATOR); + VALIDATORS.put(Secure.AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java index 49012b0159c2..a8a659ee1e5c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java @@ -235,4 +235,30 @@ public class SettingsValidators { } } }; + + static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.equals("")) { + return true; + } + + return COLON_SEPARATED_COMPONENT_LIST_VALIDATOR.validate(value); + } + }; + + static final Validator AUTOFILL_SERVICE_VALIDATOR = new Validator() { + @Override + public boolean validate(String value) { + if (value == null || value.equals("")) { + return true; + } + + if (value.equals("credential-provider")) { + return true; + } + + return NULLABLE_COMPONENT_NAME_VALIDATOR.validate(value); + } + }; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index f1b53edbb1cd..efed8c3c1ef4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -851,8 +851,6 @@ public class SettingsBackupTest { Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT, Settings.Secure.UI_TRANSLATION_ENABLED, - Settings.Secure.CREDENTIAL_SERVICE, - Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_EDGE_HAPTIC_ENABLED, Settings.Secure.DND_CONFIGS_MIGRATED, Settings.Secure.NAVIGATION_MODE_RESTORE); diff --git a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java index 865f431183c6..3b3bf8ca15f7 100644 --- a/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java +++ b/packages/SettingsProvider/test/src/android/provider/settings/validators/SettingsValidatorsTest.java @@ -340,6 +340,60 @@ public class SettingsValidatorsTest { failIfOffendersPresent(offenders, "Settings.Secure"); } + @Test + public void testCredentialServiceValidator_returnsTrueIfNull() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate(null)); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfEmpty() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate("")); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfSingleComponentName() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test")); + } + + @Test + public void testCredentialServiceValidator_returnsTrueIfMultipleComponentName() { + assertTrue(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test" + + ":android.credentials/.Test2")); + } + + @Test + public void testCredentialServiceValidator_returnsFalseIfInvalidComponentName() { + assertFalse(SettingsValidators.CREDENTIAL_SERVICE_VALIDATOR.validate("test")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfNull() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate(null)); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfEmpty() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfPlaceholder() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("credential-provider")); + } + + @Test + public void testAutofillServiceValidator_returnsTrueIfSingleComponentName() { + assertTrue(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate( + "android.credentials/android.credentials.Test")); + } + + @Test + public void testAutofillServiceValidator_returnsFalseIfInvalidComponentName() { + assertFalse(SettingsValidators.AUTOFILL_SERVICE_VALIDATOR.validate("test")); + } + private void failIfOffendersPresent(String offenders, String settingsType) { if (offenders.length() > 0) { fail("All " + settingsType + " settings that are backed up have to have a non-null" diff --git a/packages/SystemUI/communal/layout/Android.bp b/packages/SystemUI/communal/layout/Android.bp deleted file mode 100644 index 88dad6623d03..000000000000 --- a/packages/SystemUI/communal/layout/Android.bp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2023 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_library { - name: "CommunalLayoutLib", - srcs: [ - "src/**/*.kt", - ], - static_libs: [ - "androidx.arch.core_core-runtime", - "androidx.compose.animation_animation-graphics", - "androidx.compose.runtime_runtime", - "androidx.compose.material3_material3", - "jsr330", - "kotlinx-coroutines-android", - "kotlinx-coroutines-core", - ], - manifest: "AndroidManifest.xml", - kotlincflags: ["-Xjvm-default=all"], -} diff --git a/packages/SystemUI/communal/layout/AndroidManifest.xml b/packages/SystemUI/communal/layout/AndroidManifest.xml deleted file mode 100644 index 141be0762ae9..000000000000 --- a/packages/SystemUI/communal/layout/AndroidManifest.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<!-- Copyright (C) 2023 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest package="com.android.systemui.communal.layout" /> diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt deleted file mode 100644 index 91fe33cd2f4b..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/CommunalLayoutEngine.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.communal.layout - -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard - -/** Computes the arrangement of cards. */ -class CommunalLayoutEngine { - companion object { - /** - * Determines the size that each card should be rendered in, and distributes the cards into - * columns. - * - * Returns a nested list where the outer list contains columns, and the inner list contains - * cards in each column. - * - * Currently treats the first supported size as the size to be rendered in, ignoring other - * supported sizes. - * - * Cards are ordered by priority, from highest to lowest. - */ - fun distributeCardsIntoColumns( - cards: List<CommunalGridLayoutCard>, - ): List<List<CommunalGridLayoutCardInfo>> { - val result = ArrayList<ArrayList<CommunalGridLayoutCardInfo>>() - - var capacityOfLastColumn = 0 - val sorted = cards.sortedByDescending { it.priority } - for (card in sorted) { - val cardSize = card.supportedSizes.first() - if (capacityOfLastColumn >= cardSize.value) { - // Card fits in last column - capacityOfLastColumn -= cardSize.value - } else { - // Create a new column - result.add(arrayListOf()) - capacityOfLastColumn = CommunalGridLayoutCard.Size.FULL.value - cardSize.value - } - - result.last().add(CommunalGridLayoutCardInfo(card, cardSize)) - } - - return result - } - } - - /** - * A data class that wraps around a [CommunalGridLayoutCard] and also contains the size that the - * card should be rendered in. - */ - data class CommunalGridLayoutCardInfo( - val card: CommunalGridLayoutCard, - val size: CommunalGridLayoutCard.Size, - ) -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt deleted file mode 100644 index 33024f764710..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/CommunalGridLayout.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.communal.layout.ui.compose - -import android.util.SizeF -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.communal.layout.CommunalLayoutEngine -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutConfig - -/** - * An arrangement of cards with a horizontal scroll, where each card is displayed in the right size - * and follows a specific order based on its priority, ensuring a seamless layout without any gaps. - */ -@Composable -fun CommunalGridLayout( - modifier: Modifier, - layoutConfig: CommunalGridLayoutConfig, - communalCards: List<CommunalGridLayoutCard>, -) { - val columns = CommunalLayoutEngine.distributeCardsIntoColumns(communalCards) - LazyRow( - modifier = modifier.height(layoutConfig.gridHeight), - horizontalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter), - ) { - for (column in columns) { - item { - Column( - modifier = Modifier.width(layoutConfig.cardWidth), - verticalArrangement = Arrangement.spacedBy(layoutConfig.gridGutter), - ) { - for (cardInfo in column) { - Row( - modifier = Modifier.height(layoutConfig.cardHeight(cardInfo.size)), - ) { - cardInfo.card.Content( - modifier = Modifier.fillMaxSize(), - size = - SizeF( - layoutConfig.cardWidth.value, - layoutConfig.cardHeight(cardInfo.size).value, - ), - ) - } - } - } - } - } - } -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt deleted file mode 100644 index 4b2a156c1dbd..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutCard.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.communal.layout.ui.compose.config - -import android.util.SizeF -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier - -/** A card that hosts content to be rendered in the communal grid layout. */ -abstract class CommunalGridLayoutCard { - /** - * Content to be hosted by the card. - * - * To host non-Compose views, see - * https://developer.android.com/jetpack/compose/migrate/interoperability-apis/views-in-compose. - * - * @param size The size given to the card. Content of the card should fill all this space, given - * that margins and paddings have been taken care of by the layout. - */ - @Composable abstract fun Content(modifier: Modifier, size: SizeF) - - /** - * Sizes supported by the card. - * - * If multiple sizes are available, they should be ranked in order of preference, from most to - * least preferred. - */ - abstract val supportedSizes: List<Size> - - /** - * Priority of the content hosted by the card. - * - * The value of priority is relative to other cards. Cards with a higher priority are generally - * ordered first. - */ - open val priority: Int = 0 - - /** - * Size of the card. - * - * @param value A numeric value that represents the size. Must be less than or equal to - * [Size.FULL]. - */ - enum class Size(val value: Int) { - /** The card takes up full height of the grid layout. */ - FULL(value = 6), - - /** The card takes up half of the vertical space of the grid layout. */ - HALF(value = 3), - - /** The card takes up a third of the vertical space of the grid layout. */ - THIRD(value = 2), - } -} diff --git a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt b/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt deleted file mode 100644 index 143df838169b..000000000000 --- a/packages/SystemUI/communal/layout/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfig.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.communal.layout.ui.compose.config - -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.times - -/** - * Configurations of the communal grid layout. - * - * The communal grid layout follows Material Design's responsive layout grid (see - * https://m2.material.io/design/layout/responsive-layout-grid.html), in which the layout is divided - * up by columns and gutters, and each card occupies one or multiple columns. - */ -data class CommunalGridLayoutConfig( - /** - * Size in dp of each grid column. - * - * Every card occupies one or more grid columns, which means that the width of each card is - * influenced by the size of the grid columns. - */ - val gridColumnSize: Dp, - - /** - * Size in dp of each grid gutter. - * - * A gutter is the space between columns that helps separate content. This is, therefore, also - * the size of the gaps between cards, both horizontally and vertically. - */ - val gridGutter: Dp, - - /** - * Height in dp of the grid layout. - * - * Cards with a full size take up the entire height of the grid layout. - */ - val gridHeight: Dp, - - /** - * Number of grid columns that each card occupies. - * - * It's important to note that all the cards take up the same number of grid columns, or in - * simpler terms, they all have the same width. - */ - val gridColumnsPerCard: Int, -) { - /** - * Width in dp of each card. - * - * It's important to note that all the cards take up the same number of grid columns, or in - * simpler terms, they all have the same width. - */ - val cardWidth = gridColumnSize * gridColumnsPerCard + gridGutter * (gridColumnsPerCard - 1) - - /** Returns the height of a card in dp, based on its size. */ - fun cardHeight(cardSize: CommunalGridLayoutCard.Size): Dp { - return when (cardSize) { - CommunalGridLayoutCard.Size.FULL -> cardHeightBy(denominator = 1) - CommunalGridLayoutCard.Size.HALF -> cardHeightBy(denominator = 2) - CommunalGridLayoutCard.Size.THIRD -> cardHeightBy(denominator = 3) - } - } - - /** Returns the height of a card in dp when the layout is evenly divided by [denominator]. */ - private fun cardHeightBy(denominator: Int): Dp { - return (gridHeight - (denominator - 1) * gridGutter) / denominator - } -} diff --git a/packages/SystemUI/communal/layout/tests/Android.bp b/packages/SystemUI/communal/layout/tests/Android.bp deleted file mode 100644 index 9a05504cad8b..000000000000 --- a/packages/SystemUI/communal/layout/tests/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2023 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_test { - name: "CommunalLayoutLibTests", - srcs: [ - "**/*.kt", - ], - static_libs: [ - "CommunalLayoutLib", - "androidx.test.runner", - "androidx.test.rules", - "androidx.test.ext.junit", - "frameworks-base-testutils", - "junit", - "kotlinx_coroutines_test", - "mockito-target-extended-minus-junit4", - "platform-test-annotations", - "testables", - "truth", - ], - libs: [ - "android.test.mock", - "android.test.base", - "android.test.runner", - ], - jni_libs: [ - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - ], - manifest: "AndroidManifest.xml", -} diff --git a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml b/packages/SystemUI/communal/layout/tests/AndroidManifest.xml deleted file mode 100644 index b19007c1ff1b..000000000000 --- a/packages/SystemUI/communal/layout/tests/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest - xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.communal.layout.tests"> - - <application android:debuggable="true" android:largeHeap="true"> - <uses-library android:name="android.test.mock" /> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="android.testing.TestableInstrumentation" - android:targetPackage="com.android.systemui.communal.layout.tests" - android:label="Tests for CommunalLayoutLib"> - </instrumentation> - -</manifest> diff --git a/packages/SystemUI/communal/layout/tests/AndroidTest.xml b/packages/SystemUI/communal/layout/tests/AndroidTest.xml deleted file mode 100644 index 1352b238f6fe..000000000000 --- a/packages/SystemUI/communal/layout/tests/AndroidTest.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<configuration description="Runs tests for CommunalLayoutLib"> - - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="install-arg" value="-t" /> - <option name="test-file-name" value="CommunalLayoutLibTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="framework-base-presubmit" /> - <option name="test-tag" value="CommunalLayoutLibTests" /> - - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.systemui.communal.layout.tests" /> - <option name="runner" value="android.testing.TestableInstrumentation" /> - <option name="hidden-api-checks" value="false"/> - </test> - -</configuration> diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt deleted file mode 100644 index 50b7c5f02068..000000000000 --- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/CommunalLayoutEngineTest.kt +++ /dev/null @@ -1,145 +0,0 @@ -package com.android.systemui.communal.layout - -import android.util.SizeF -import androidx.compose.material3.Card -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.communal.layout.ui.compose.config.CommunalGridLayoutCard -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class CommunalLayoutEngineTest { - @Test - fun distribution_fullLayout() { - val cards = - listOf( - generateCard(CommunalGridLayoutCard.Size.FULL), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.THIRD), - ) - val expected = - listOf( - listOf( - CommunalGridLayoutCard.Size.FULL, - ), - listOf( - CommunalGridLayoutCard.Size.HALF, - CommunalGridLayoutCard.Size.HALF, - ), - listOf( - CommunalGridLayoutCard.Size.THIRD, - CommunalGridLayoutCard.Size.THIRD, - CommunalGridLayoutCard.Size.THIRD, - ), - ) - - assertDistributionBySize(cards, expected) - } - - @Test - fun distribution_layoutWithGaps() { - val cards = - listOf( - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.THIRD), - generateCard(CommunalGridLayoutCard.Size.HALF), - generateCard(CommunalGridLayoutCard.Size.FULL), - generateCard(CommunalGridLayoutCard.Size.THIRD), - ) - val expected = - listOf( - listOf( - CommunalGridLayoutCard.Size.HALF, - CommunalGridLayoutCard.Size.THIRD, - ), - listOf( - CommunalGridLayoutCard.Size.HALF, - ), - listOf( - CommunalGridLayoutCard.Size.FULL, - ), - listOf( - CommunalGridLayoutCard.Size.THIRD, - ), - ) - - assertDistributionBySize(cards, expected) - } - - @Test - fun distribution_sortByPriority() { - val cards = - listOf( - generateCard(priority = 2), - generateCard(priority = 7), - generateCard(priority = 10), - generateCard(priority = 1), - generateCard(priority = 5), - ) - val expected = - listOf( - listOf(10, 7), - listOf(5, 2), - listOf(1), - ) - - assertDistributionByPriority(cards, expected) - } - - private fun assertDistributionBySize( - cards: List<CommunalGridLayoutCard>, - expected: List<List<CommunalGridLayoutCard.Size>>, - ) { - val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards) - - for (c in expected.indices) { - for (r in expected[c].indices) { - assertThat(result[c][r].size).isEqualTo(expected[c][r]) - } - } - } - - private fun assertDistributionByPriority( - cards: List<CommunalGridLayoutCard>, - expected: List<List<Int>>, - ) { - val result = CommunalLayoutEngine.distributeCardsIntoColumns(cards) - - for (c in expected.indices) { - for (r in expected[c].indices) { - assertThat(result[c][r].card.priority).isEqualTo(expected[c][r]) - } - } - } - - private fun generateCard(size: CommunalGridLayoutCard.Size): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(size) - - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - Card(modifier = modifier, content = {}) - } - } - } - - private fun generateCard(priority: Int): CommunalGridLayoutCard { - return object : CommunalGridLayoutCard() { - override val supportedSizes = listOf(Size.HALF) - override val priority = priority - - @Composable - override fun Content(modifier: Modifier, size: SizeF) { - Card(modifier = modifier, content = {}) - } - } - } -} diff --git a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt b/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt deleted file mode 100644 index 946eeecbec5d..000000000000 --- a/packages/SystemUI/communal/layout/tests/src/com/android/systemui/communal/layout/ui/compose/config/CommunalGridLayoutConfigTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.android.systemui.communal.layout.ui.compose.config - -import androidx.compose.ui.unit.dp -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.google.common.truth.Truth -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class CommunalGridLayoutConfigTest { - @Test - fun cardWidth() { - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 1, - ) - .cardWidth - ) - .isEqualTo(5.dp) - - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 2, - ) - .cardWidth - ) - .isEqualTo(13.dp) - - Truth.assertThat( - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 3.dp, - gridHeight = 17.dp, - gridColumnsPerCard = 3, - ) - .cardWidth - ) - .isEqualTo(21.dp) - } - - @Test - fun cardHeight() { - val config = - CommunalGridLayoutConfig( - gridColumnSize = 5.dp, - gridGutter = 2.dp, - gridHeight = 10.dp, - gridColumnsPerCard = 3, - ) - - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.FULL)).isEqualTo(10.dp) - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.HALF)).isEqualTo(4.dp) - Truth.assertThat(config.cardHeight(CommunalGridLayoutCard.Size.THIRD)).isEqualTo(2.dp) - } -} diff --git a/packages/SystemUI/compose/features/Android.bp b/packages/SystemUI/compose/features/Android.bp index 16c24375d14f..796abf4b52d6 100644 --- a/packages/SystemUI/compose/features/Android.bp +++ b/packages/SystemUI/compose/features/Android.bp @@ -31,7 +31,6 @@ android_library { ], static_libs: [ - "CommunalLayoutLib", "SystemUI-core", "PlatformComposeCore", "PlatformComposeSceneTransitionLayout", diff --git a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml index 952f056b3023..cc99f5e125f3 100644 --- a/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml +++ b/packages/SystemUI/res-keyguard/layout/shade_carrier_new.xml @@ -22,13 +22,15 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:orientation="horizontal" > + android:orientation="horizontal" + android:theme="@style/Theme.SystemUI.QuickSettings.Header" > <com.android.systemui.util.AutoMarqueeTextView android:id="@+id/mobile_carrier_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" + android:textAppearance="@style/TextAppearance.QS.Status.Carriers" android:layout_marginEnd="@dimen/qs_carrier_margin_width" android:visibility="gone" android:textDirection="locale" diff --git a/packages/SystemUI/res/drawable/notification_material_bg.xml b/packages/SystemUI/res/drawable/notification_material_bg.xml index 355e75d0716b..9c08f5ef4cfe 100644 --- a/packages/SystemUI/res/drawable/notification_material_bg.xml +++ b/packages/SystemUI/res/drawable/notification_material_bg.xml @@ -18,7 +18,7 @@ <layer-list xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:color="?android:attr/colorControlHighlight"> - <item> + <item android:id="@+id/notification_background_color_layer"> <shape> <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh" /> </shape> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 1fa55f5d839b..54cb501db002 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -384,6 +384,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + @Nullable + View getAodNotifIconContainer() { + return mAodIconContainer; + } + @Override protected void onViewDetached() { mClockRegistry.unregisterClockChangeListener(mClockChangedListener); @@ -639,6 +644,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } else { mNotificationIconAreaController.setupAodIcons(nic); + mAodIconContainer = nic; } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index 87d937bc45fb..4fbf077a8852 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -84,6 +84,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV Dumpable { private static final boolean DEBUG = KeyguardConstants.DEBUG; @VisibleForTesting static final String TAG = "KeyguardStatusViewController"; + private static final long STATUS_AREA_HEIGHT_ANIMATION_MILLIS = 133; /** * Duration to use for the animator when the keyguard status view alignment changes, and a @@ -104,6 +105,10 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final KeyguardInteractor mKeyguardInteractor; private final PowerInteractor mPowerInteractor; private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final DozeParameters mDozeParameters; + + private View mStatusArea = null; + private ValueAnimator mStatusAreaHeightAnimator = null; private Boolean mSplitShadeEnabled = false; private Boolean mStatusViewCentered = true; @@ -123,6 +128,46 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV } }; + private final View.OnLayoutChangeListener mStatusAreaLayoutChangeListener = + new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, + int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom + ) { + if (!mDozeParameters.getAlwaysOn()) { + return; + } + + int oldHeight = oldBottom - oldTop; + int diff = v.getHeight() - oldHeight; + if (diff == 0) { + return; + } + + int startValue = -1 * diff; + long duration = STATUS_AREA_HEIGHT_ANIMATION_MILLIS; + if (mStatusAreaHeightAnimator != null + && mStatusAreaHeightAnimator.isRunning()) { + duration += mStatusAreaHeightAnimator.getDuration() + - mStatusAreaHeightAnimator.getCurrentPlayTime(); + startValue += (int) mStatusAreaHeightAnimator.getAnimatedValue(); + mStatusAreaHeightAnimator.cancel(); + mStatusAreaHeightAnimator = null; + } + + mStatusAreaHeightAnimator = ValueAnimator.ofInt(startValue, 0); + mStatusAreaHeightAnimator.setDuration(duration); + final View nic = mKeyguardClockSwitchController.getAodNotifIconContainer(); + if (nic != null) { + mStatusAreaHeightAnimator.addUpdateListener(anim -> { + nic.setTranslationY((int) anim.getAnimatedValue()); + }); + } + mStatusAreaHeightAnimator.start(); + } + }; + @Inject public KeyguardStatusViewController( KeyguardStatusView keyguardStatusView, @@ -144,6 +189,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mKeyguardClockSwitchController = keyguardClockSwitchController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mConfigurationController = configurationController; + mDozeParameters = dozeParameters; mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters, screenOffAnimationController, /* animateYPos= */ true, logger.getBuffer()); @@ -218,12 +264,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV @Override protected void onViewAttached() { + mStatusArea = mView.findViewById(R.id.keyguard_status_area); + mStatusArea.addOnLayoutChangeListener(mStatusAreaLayoutChangeListener); mKeyguardUpdateMonitor.registerCallback(mInfoCallback); mConfigurationController.addCallback(mConfigurationListener); } @Override protected void onViewDetached() { + mStatusArea.removeOnLayoutChangeListener(mStatusAreaLayoutChangeListener); mKeyguardUpdateMonitor.removeCallback(mInfoCallback); mConfigurationController.removeCallback(mConfigurationListener); } @@ -293,9 +342,15 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV /** * Get the height of the keyguard status view without the notification icon area, as that's * only visible on AOD. + * + * We internally animate height changes to the status area to prevent discontinuities in the + * doze animation introduced by the height suddenly changing due to smartpace. */ public int getLockscreenHeight() { - return mView.getHeight() - mKeyguardClockSwitchController.getNotificationIconAreaHeight(); + int heightAnimValue = mStatusAreaHeightAnimator == null ? 0 : + (int) mStatusAreaHeightAnimator.getAnimatedValue(); + return mView.getHeight() + heightAnimValue + - mKeyguardClockSwitchController.getNotificationIconAreaHeight(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 57e252dd9929..8fe42b536b1e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -100,7 +100,6 @@ import javax.inject.Inject; import javax.inject.Provider; import kotlin.Unit; - import kotlinx.coroutines.CoroutineScope; /** @@ -1099,6 +1098,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, // TODO(b/141025588): Create separate methods for handling hard and soft errors. final boolean isSoftError = (error == BiometricConstants.BIOMETRIC_PAUSED_REJECTED || error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT + || error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL || isCameraPrivacyEnabled); if (mCurrentDialog != null) { if (mCurrentDialog.isAllowDeviceCredentials() && isLockout) { diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 7d541070f146..98fda3eb01c5 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -464,12 +464,6 @@ object Flags { val WALLPAPER_MULTI_CROP = sysPropBooleanFlag("persist.wm.debug.wallpaper_multi_crop", default = false) - // TODO(b/290220798): Tracking Bug - @Keep - @JvmField - val ENABLE_PIP2_IMPLEMENTATION = - sysPropBooleanFlag("persist.wm.debug.enable_pip2_implementation", default = false) - // 1200 - predictive back @Keep @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt index 99529a100b07..9a50d8370525 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryForegroundViewModel.kt @@ -31,6 +31,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart @@ -49,12 +50,22 @@ constructor( transitionInteractor.startedKeyguardState.map { keyguardState -> keyguardState == KeyguardState.AOD } + + private fun getColor(usingBackgroundProtection: Boolean): Int { + return if (usingBackgroundProtection) { + Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) + } else { + Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent) + } + } + private val color: Flow<Int> = - configurationRepository.onAnyConfigurationChange - .map { Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) } - .onStart { - emit(Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)) - } + deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection -> + configurationRepository.onAnyConfigurationChange + .map { getColor(useBgProtection) } + .onStart { emit(getColor(useBgProtection)) } + } + private val useAodIconVariant: Flow<Boolean> = combine(isShowingAod, deviceEntryUdfpsInteractor.isUdfpsSupported) { isTransitionToAod, diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 58c4f0d029c7..ef7296743f20 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -131,6 +131,9 @@ class EmojiHelper { + "\\p{Emoji}(\\p{EMod}|\\x{FE0F}\\x{20E3}?|[\\x{E0020}-\\x{E007E}]+\\x{E007F})" + "?)*"; + // Not all JDKs support emoji patterns, including the one errorprone runs under, which + // makes it think that this is an invalid pattern. + @SuppressWarnings("InvalidPatternSyntax") static final Pattern EMOJI_PATTERN = Pattern.compile(UNICODE_EMOJI_REGEX); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 8eda96f62257..64f61d9ac2da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -27,6 +27,7 @@ import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -42,9 +43,11 @@ import java.util.Arrays; * A view that can be used for both the dimmed and normal background of an notification. */ public class NotificationBackgroundView extends View implements Dumpable { + private static final String TAG = "NotificationBackgroundView"; private final boolean mDontModifyCorners; private Drawable mBackground; + private Drawable mBackgroundDrawableToTint; private int mClipTopAmount; private int mClipBottomAmount; private int mTintColor; @@ -131,6 +134,7 @@ public class NotificationBackgroundView extends View implements Dumpable { unscheduleDrawable(mBackground); } mBackground = background; + mBackgroundDrawableToTint = findBackgroundDrawableToTint(mBackground); mRippleColor = null; mBackground.mutate(); if (mBackground != null) { @@ -144,25 +148,46 @@ public class NotificationBackgroundView extends View implements Dumpable { invalidate(); } + // setCustomBackground should be called from ActivatableNotificationView.initBackground + // with R.drawable.notification_material_bg, which is a layer-list with a lower layer + // for the background color (annotated with an ID so we can find it) and an upper layer + // to blend in the stateful @color/notification_overlay_color. + // + // If the notification is tinted, we want to set a tint list on *just that lower layer* that + // will replace the default materialColorSurfaceContainerHigh *without* wiping out the stateful + // tints in the upper layer that make the hovered and pressed states visible. + // + // This function fishes that lower layer out, or makes a fuss in logcat if it can't find it. + private @Nullable Drawable findBackgroundDrawableToTint(@Nullable Drawable background) { + if (background == null) { + return null; + } + + if (!(background instanceof LayerDrawable)) { + Log.wtf(TAG, "background is not a LayerDrawable: " + background); + return background; + } + + final Drawable backgroundColorLayer = ((LayerDrawable) background).findDrawableByLayerId( + R.id.notification_background_color_layer); + + if (backgroundColorLayer == null) { + Log.wtf(TAG, "background is missing background color layer: " + background); + return background; + } + + return backgroundColorLayer; + } + public void setCustomBackground(int drawableResId) { final Drawable d = mContext.getDrawable(drawableResId); setCustomBackground(d); } public void setTint(int tintColor) { - if (tintColor != 0) { - ColorStateList stateList = new ColorStateList(new int[][]{ - new int[]{com.android.internal.R.attr.state_pressed}, - new int[]{com.android.internal.R.attr.state_hovered}, - new int[]{}}, - - new int[]{tintColor, tintColor, tintColor} - ); - mBackground.setTintMode(PorterDuff.Mode.SRC_ATOP); - mBackground.setTintList(stateList); - } else { - mBackground.setTintList(null); - } + mBackgroundDrawableToTint.setTint(tintColor); + mBackgroundDrawableToTint.setTintMode(PorterDuff.Mode.SRC_ATOP); + mTintColor = tintColor; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 283a5930f930..6944453506a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -565,6 +565,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationStackScrollLayoutController.TouchHandler mTouchHandler; private final ScreenOffAnimationController mScreenOffAnimationController; private boolean mShouldUseSplitNotificationShade; + private boolean mShouldSkipTopPaddingAnimationAfterFold = false; private boolean mHasFilteredOutSeenNotifications; @Nullable private SplitShadeStateController mSplitShadeStateController = null; private boolean mIsSmallLandscapeLockscreenEnabled = false; @@ -1364,7 +1365,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mTopPadding = topPadding; updateAlgorithmHeightAndPadding(); updateContentHeight(); - if (shouldAnimate && mAnimationsEnabled && mIsExpanded) { + if (mAmbientState.isOnKeyguard() + && !mShouldUseSplitNotificationShade + && mShouldSkipTopPaddingAnimationAfterFold) { + mShouldSkipTopPaddingAnimationAfterFold = false; + } else if (shouldAnimate && mAnimationsEnabled && mIsExpanded) { mTopPaddingNeedsAnimation = true; mNeedsAnimation = true; } @@ -3751,6 +3756,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } protected boolean isInsideQsHeader(MotionEvent ev) { + if (mQsHeader == null) { + Log.wtf(TAG, "qsHeader is null while NSSL is handling a touch"); + return false; + } + mQsHeader.getBoundsOnScreen(mQsHeaderBound); /** * One-handed mode defines a feature FEATURE_ONE_HANDED of DisplayArea {@link DisplayArea} @@ -5736,6 +5746,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable boolean split = mSplitShadeStateController.shouldUseSplitNotificationShade(getResources()); if (split != mShouldUseSplitNotificationShade) { mShouldUseSplitNotificationShade = split; + mShouldSkipTopPaddingAnimationAfterFold = true; mAmbientState.setUseSplitShade(split); updateDismissBehavior(); updateUseRoundedRectClipping(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java index 146715d26b7d..13fb42ce8c3e 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java @@ -35,6 +35,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.power.data.repository.FakePowerRepository; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; +import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -70,6 +71,7 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { @Mock protected KeyguardClockSwitch mKeyguardClockSwitch; @Mock protected FrameLayout mMediaHostContainer; + @Mock protected KeyguardStatusAreaView mKeyguardStatusAreaView; @Before public void setup() { @@ -109,6 +111,8 @@ public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase { when(mKeyguardStatusView.getViewTreeObserver()).thenReturn(mViewTreeObserver); when(mKeyguardClockSwitchController.getView()).thenReturn(mKeyguardClockSwitch); when(mKeyguardTransitionInteractor.getGoneToAodTransition()).thenReturn(emptyFlow()); + when(mKeyguardStatusView.findViewById(R.id.keyguard_status_area)) + .thenReturn(mKeyguardStatusAreaView); } protected void givenViewAttached() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 948942fbce3a..9c3288b9f93d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static junit.framework.Assert.assertEquals; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.animation.AnimatorTestRule; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,6 +43,7 @@ import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -51,6 +55,9 @@ import java.lang.reflect.Field; @RunWith(AndroidTestingRunner.class) public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControllerBaseTest { + @Rule + public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(); + @Test public void dozeTimeTick_updatesSlice() { mController.dozeTimeTick(); @@ -230,4 +237,34 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll throw new RuntimeException(e); } } + + @Test + public void statusAreaHeightChange_animatesHeightOutputChange() { + // Init & Capture Layout Listener + mController.onInit(); + mController.onViewAttached(); + + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + ArgumentCaptor<View.OnLayoutChangeListener> captor = + ArgumentCaptor.forClass(View.OnLayoutChangeListener.class); + verify(mKeyguardStatusAreaView).addOnLayoutChangeListener(captor.capture()); + View.OnLayoutChangeListener listener = captor.getValue(); + + // Setup and validate initial height + when(mKeyguardStatusView.getHeight()).thenReturn(200); + when(mKeyguardClockSwitchController.getNotificationIconAreaHeight()).thenReturn(10); + assertEquals(190, mController.getLockscreenHeight()); + + // Trigger Change and validate value unchanged immediately + when(mKeyguardStatusAreaView.getHeight()).thenReturn(100); + when(mKeyguardStatusView.getHeight()).thenReturn(300); // Include child height + listener.onLayoutChange(mKeyguardStatusAreaView, + /* new layout */ 100, 300, 200, 400, + /* old layout */ 100, 300, 200, 300); + assertEquals(190, mController.getLockscreenHeight()); + + // Complete animation, validate height increased + mAnimatorTestRule.advanceTimeBy(200); + assertEquals(290, mController.getLockscreenHeight()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt index a7e7dd074a33..2b51ac5e3187 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/AnimatorTestRuleOrderTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.animation import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.core.animation.doOnEnd +import androidx.test.filters.FlakyTest import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.doOnEnd @@ -30,6 +31,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) @SmallTest @RunWithLooper +@FlakyTest(bugId = 302149604) class AnimatorTestRuleOrderTest : SysuiTestCase() { @get:Rule val animatorTestRule = AnimatorTestRule() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 11c5d3bb27b3..602f3dc29491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -475,6 +475,22 @@ public class AuthControllerTest extends SysuiTestCase { } @Test + public void testOnAuthenticationFailedInvoked_whenBiometricReEnrollRequired() { + showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final int modality = BiometricAuthenticator.TYPE_FACE; + mAuthController.onBiometricError(modality, + BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, + 0 /* vendorCode */); + + verify(mDialog1).onAuthenticationFailed(mModalityCaptor.capture(), + mMessageCaptor.capture()); + + assertThat(mModalityCaptor.getValue()).isEqualTo(modality); + assertThat(mMessageCaptor.getValue()).isEqualTo(mContext.getString( + R.string.face_recalibrate_notification_content)); + } + + @Test public void testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected_withPaused() { testOnAuthenticationFailedInvoked_coex_whenFaceAuthRejected( BiometricConstants.BIOMETRIC_PAUSED_REJECTED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt index 1c7fd565c289..7361f6baa7b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision.FSI_DEVICE_NOT_INTERACTIVE @@ -37,7 +36,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() { init { - setFlagsRule.disableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR) + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME) } override val provider by lazy { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt index df6f0d716577..d2c046c67fb0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.interruption import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.Flags import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PEEK @@ -30,7 +29,7 @@ import org.junit.runner.RunWith @RunWith(AndroidTestingRunner::class) class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() { init { - setFlagsRule.enableFlags(Flags.FLAG_VISUAL_INTERRUPTIONS_REFACTOR) + mSetFlagsRule.enableFlags(VisualInterruptionRefactor.FLAG_NAME) } override val provider by lazy { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt index 7babff5e0b2e..2ac0cb7499d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt @@ -44,7 +44,6 @@ import android.graphics.drawable.Icon import android.hardware.display.FakeAmbientDisplayConfiguration import android.os.Looper import android.os.PowerManager -import android.platform.test.flag.junit.SetFlagsRule import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED import android.provider.Settings.Global.HEADS_UP_OFF import android.provider.Settings.Global.HEADS_UP_ON @@ -84,15 +83,10 @@ import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Assert.assertEquals import org.junit.Before -import org.junit.Rule import org.junit.Test import org.mockito.Mockito.`when` as whenever abstract class VisualInterruptionDecisionProviderTestBase : SysuiTestCase() { - @JvmField - @Rule - val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT) - private val fakeLogBuffer = LogBuffer( name = "FakeLog", diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 4b1c7e8faa38..4422764c4bac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -157,6 +157,7 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper; import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProvider; +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -347,6 +348,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true); when(mDozeParameters.getAlwaysOn()).thenReturn(true); mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR); + // TODO: b/312476335 - Update to check flag and instantiate old or new implementation. + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, 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 df7609c544a4..200cfd3605c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -146,6 +146,7 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderWrapper; +import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor; @@ -366,6 +367,9 @@ public class BubblesTest extends SysuiTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + // TODO: b/312476335 - Update to check flag and instantiate old or new implementation. + mSetFlagsRule.disableFlags(VisualInterruptionRefactor.FLAG_NAME); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { doReturn(true).when(mTransitions).isRegistered(); } diff --git a/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt new file mode 100644 index 000000000000..f9c920a89e16 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/app/ActivityManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.activityManager by Kosmos.Fixture { mock<ActivityManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt new file mode 100644 index 000000000000..b284ac0ec7e9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/app/admin/DevicePolicyManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.admin + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.devicePolicyManager by Kosmos.Fixture { mock<DevicePolicyManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt new file mode 100644 index 000000000000..f96c5080bb95 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/content/ContextKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content + +import com.android.systemui.SysuiTestableContext +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testCase + +val Kosmos.testableContext: SysuiTestableContext by Kosmos.Fixture { testCase.context } +var Kosmos.applicationContext: Context by Kosmos.Fixture { testableContext } diff --git a/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt new file mode 100644 index 000000000000..56867640d03d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/content/res/ResourcesKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.content.res + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +var Kosmos.mainResources: Resources by Kosmos.Fixture { applicationContext.resources } diff --git a/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt new file mode 100644 index 000000000000..c936b914f44e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/os/UserManagerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.userManager by Kosmos.Fixture { mock<UserManager>() } diff --git a/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt new file mode 100644 index 000000000000..34c0a7915523 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/android/view/LayoutInflaterKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view + +import android.content.applicationContext +import com.android.systemui.kosmos.Kosmos + +val Kosmos.layoutInflater: LayoutInflater by + Kosmos.Fixture { LayoutInflater.from(applicationContext) } diff --git a/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt new file mode 100644 index 000000000000..9059da2259b1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/internal/logging/UiEventLoggerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.logging + +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.kosmos.Kosmos + +var Kosmos.uiEventLogger: UiEventLogger by Kosmos.Fixture { uiEventLoggerFake } +val Kosmos.uiEventLoggerFake by Kosmos.Fixture { UiEventLoggerFake() } diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt new file mode 100644 index 000000000000..fadcecc07190 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardSecurityModelKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.keyguardSecurityModel by Kosmos.Fixture { mock<KeyguardSecurityModel>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt new file mode 100644 index 000000000000..b32cbe6da0ec --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/keyguard/KeyguardUpdateMonitorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.keyguardUpdateMonitor by Kosmos.Fixture { mock<KeyguardUpdateMonitor>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt new file mode 100644 index 000000000000..4c4cfd541a1f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResetOrExitSessionReceiverKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.guestResetOrExitSessionReceiver by + Kosmos.Fixture { mock<GuestResetOrExitSessionReceiver>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt new file mode 100644 index 000000000000..a9855ffda8b8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/GuestResumeSessionReceiverKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.guestResumeSessionReceiver by Kosmos.Fixture { mock<GuestResumeSessionReceiver>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt new file mode 100644 index 000000000000..ea93e949c83c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/AuthenticationRepositoryKosmos.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.authentication.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import kotlinx.coroutines.test.currentTime + +var Kosmos.authenticationRepository: AuthenticationRepository by + Kosmos.Fixture { fakeAuthenticationRepository } +val Kosmos.fakeAuthenticationRepository by + Kosmos.Fixture { FakeAuthenticationRepository { testScope.currentTime } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt new file mode 100644 index 000000000000..060ca4c7e912 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.authentication.domain.interactor + +import com.android.systemui.authentication.data.repository.authenticationRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.user.data.repository.userRepository +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.authenticationInteractor by + Kosmos.Fixture { + AuthenticationInteractor( + applicationScope = applicationCoroutineScope, + repository = authenticationRepository, + backgroundDispatcher = testDispatcher, + userRepository = userRepository, + clock = fakeSystemClock, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt new file mode 100644 index 000000000000..2a870749644a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bouncer.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardBouncerRepository: KeyguardBouncerRepository by + Kosmos.Fixture { fakeKeyguardBouncerRepository } +val Kosmos.fakeKeyguardBouncerRepository by Kosmos.Fixture { FakeKeyguardBouncerRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt new file mode 100644 index 000000000000..3a72d11cdc18 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/classifier/FalsingCollectorKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.classifier + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.falsingCollector by Kosmos.Fixture { FalsingCollectorFake() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt new file mode 100644 index 000000000000..86a8ae5f9cf4 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/ConfigurationStateKosmos.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.common.ui + +import android.content.applicationContext +import android.view.layoutInflater +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.configurationController + +val Kosmos.configurationState: ConfigurationState by + Kosmos.Fixture { + ConfigurationState(configurationController, applicationContext, layoutInflater) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt new file mode 100644 index 000000000000..77b8bd4ff2de --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/ConfigurationRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.common.ui.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.configurationRepository: ConfigurationRepository by + Kosmos.Fixture { fakeConfigurationRepository } +val Kosmos.fakeConfigurationRepository by Kosmos.Fixture { FakeConfigurationRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt new file mode 100644 index 000000000000..3da0681dc45f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/data/repository/DeviceEntryRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.deviceentry.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceEntryRepository: DeviceEntryRepository by + Kosmos.Fixture { fakeDeviceEntryRepository } +val Kosmos.fakeDeviceEntryRepository by Kosmos.Fixture { FakeDeviceEntryRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt new file mode 100644 index 000000000000..b600b50b8d2d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.deviceentry.domain.interactor + +import com.android.systemui.authentication.domain.interactor.authenticationInteractor +import com.android.systemui.deviceentry.data.repository.deviceEntryRepository +import com.android.systemui.keyguard.data.repository.deviceEntryFaceAuthRepository +import com.android.systemui.keyguard.data.repository.trustRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.deviceEntryInteractor by + Kosmos.Fixture { + DeviceEntryInteractor( + applicationScope = applicationCoroutineScope, + repository = deviceEntryRepository, + authenticationInteractor = authenticationInteractor, + sceneInteractor = sceneInteractor, + deviceEntryFaceAuthRepository = deviceEntryFaceAuthRepository, + trustRepository = trustRepository, + flags = sceneContainerFlags, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt index a78076338c79..e6b7f62c7d5f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FeatureFlagsClassicKosmos.kt @@ -18,4 +18,4 @@ package com.android.systemui.flags import com.android.systemui.kosmos.Kosmos -val Kosmos.featureFlags by Kosmos.Fixture { FakeFeatureFlagsClassic() } +val Kosmos.featureFlagsClassic by Kosmos.Fixture { FakeFeatureFlagsClassic() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt new file mode 100644 index 000000000000..3d729675f7ab --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository by + Kosmos.Fixture { fakeDeviceEntryFaceAuthRepository } +val Kosmos.fakeDeviceEntryFaceAuthRepository by + Kosmos.Fixture { FakeDeviceEntryFaceAuthRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt new file mode 100644 index 000000000000..b0e4ba06d7b2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.inWindowLauncherUnlockAnimationRepository by + Kosmos.Fixture { InWindowLauncherUnlockAnimationRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt new file mode 100644 index 000000000000..453fef5ddca9 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.keyguardRepository: KeyguardRepository by Kosmos.Fixture { fakeKeyguardRepository } +val Kosmos.fakeKeyguardRepository by Kosmos.Fixture { FakeKeyguardRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt new file mode 100644 index 000000000000..c900ac9771a7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardSurfaceBehindRepository: KeyguardSurfaceBehindRepository by + Kosmos.Fixture { fakeKeyguardSurfaceBehindRepository } +val Kosmos.fakeKeyguardSurfaceBehindRepository by + Kosmos.Fixture { FakeKeyguardSurfaceBehindRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt new file mode 100644 index 000000000000..008f79a377e0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by + Kosmos.Fixture { fakeKeyguardTransitionRepository } +val Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt new file mode 100644 index 000000000000..ca87acf88d01 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/TrustRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.trustRepository: TrustRepository by Kosmos.Fixture { fakeTrustRepository } +val Kosmos.fakeTrustRepository by Kosmos.Fixture { FakeTrustRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..b03d0b822161 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.shade.data.repository.shadeRepository +import dagger.Lazy + +val Kosmos.fromLockscreenTransitionInteractor by + Kosmos.Fixture { + FromLockscreenTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + keyguardInteractor = keyguardInteractor, + flags = featureFlagsClassic, + shadeRepository = shadeRepository, + powerInteractor = powerInteractor, + inWindowLauncherUnlockAnimationInteractor = + Lazy { inWindowLauncherUnlockAnimationInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..ade3e1a82297 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.keyguard.keyguardSecurityModel +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.user.domain.interactor.selectedUserInteractor + +val Kosmos.fromPrimaryBouncerTransitionInteractor by + Kosmos.Fixture { + FromPrimaryBouncerTransitionInteractor( + transitionRepository = keyguardTransitionRepository, + transitionInteractor = keyguardTransitionInteractor, + scope = applicationCoroutineScope, + keyguardInteractor = keyguardInteractor, + flags = featureFlagsClassic, + keyguardSecurityModel = keyguardSecurityModel, + selectedUserInteractor = selectedUserInteractor, + powerInteractor = powerInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt new file mode 100644 index 000000000000..dbbb203b0570 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.inWindowLauncherUnlockAnimationRepository +import com.android.systemui.keyguard.data.repository.keyguardSurfaceBehindRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.shared.system.activityManagerWrapper +import dagger.Lazy + +val Kosmos.inWindowLauncherUnlockAnimationInteractor by + Kosmos.Fixture { + InWindowLauncherUnlockAnimationInteractor( + repository = inWindowLauncherUnlockAnimationRepository, + scope = applicationCoroutineScope, + transitionInteractor = keyguardTransitionInteractor, + surfaceBehindRepository = Lazy { keyguardSurfaceBehindRepository }, + activityManager = activityManagerWrapper, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt new file mode 100644 index 000000000000..4843ae7ceb1d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.commandQueue + +val Kosmos.keyguardInteractor by + Kosmos.Fixture { + KeyguardInteractor( + repository = keyguardRepository, + commandQueue = commandQueue, + powerInteractor = powerInteractor, + featureFlags = featureFlagsClassic, + sceneContainerFlags = sceneContainerFlags, + bouncerRepository = keyguardBouncerRepository, + configurationRepository = configurationRepository, + shadeRepository = shadeRepository, + sceneInteractorProvider = { sceneInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt new file mode 100644 index 000000000000..e4d115e16b6a --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import dagger.Lazy + +val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by + Kosmos.Fixture { + KeyguardTransitionInteractor( + scope = applicationCoroutineScope, + repository = keyguardTransitionRepository, + keyguardInteractor = Lazy { keyguardInteractor }, + fromLockscreenTransitionInteractor = Lazy { fromLockscreenTransitionInteractor }, + fromPrimaryBouncerTransitionInteractor = + Lazy { fromPrimaryBouncerTransitionInteractor }, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt index b05915c4f678..0b1385865d63 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt @@ -1,16 +1,11 @@ package com.android.systemui.kosmos -import android.content.Context -import android.os.UserManager +import com.android.systemui.SysuiTestCase import com.android.systemui.kosmos.Kosmos.Fixture -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import org.mockito.Mockito var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() } var Kosmos.testScope by Fixture { TestScope(testDispatcher) } -var Kosmos.context by Fixture<Context>() -var Kosmos.lifecycleScope by Fixture<CoroutineScope>() - -val Kosmos.userManager by Fixture { Mockito.mock(UserManager::class.java) } +var Kosmos.applicationCoroutineScope by Fixture { testScope.backgroundScope } +var Kosmos.testCase: SysuiTestCase by Fixture() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt new file mode 100644 index 000000000000..0ec8d49ec29b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/ActivityStarterKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.activityStarter by Kosmos.Fixture { mock<ActivityStarter>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt new file mode 100644 index 000000000000..cac2646a58f2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/statusbar/StatusBarStateControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.plugins.statusbar + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.statusBarStateController by Kosmos.Fixture { mock<StatusBarStateController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt new file mode 100644 index 000000000000..c924579d43e5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/PowerRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.power.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.powerRepository: PowerRepository by Kosmos.Fixture { fakePowerRepository } +val Kosmos.fakePowerRepository by Kosmos.Fixture { FakePowerRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt new file mode 100644 index 000000000000..8486691a7b95 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/domain/interactor/PowerInteractorKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.power.domain.interactor + +import com.android.systemui.classifier.falsingCollector +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.plugins.statusbar.statusBarStateController +import com.android.systemui.power.data.repository.powerRepository +import com.android.systemui.statusbar.phone.screenOffAnimationController + +val Kosmos.powerInteractor by + Kosmos.Fixture { + PowerInteractor( + repository = powerRepository, + falsingCollector = falsingCollector, + screenOffAnimationController = screenOffAnimationController, + statusBarStateController = statusBarStateController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt new file mode 100644 index 000000000000..7c4e160f6d05 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.data.repository + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.scene.shared.model.sceneContainerConfig + +val Kosmos.sceneContainerRepository by + Kosmos.Fixture { SceneContainerRepository(applicationCoroutineScope, sceneContainerConfig) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt new file mode 100644 index 000000000000..998987602234 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneInteractorKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.data.repository.sceneContainerRepository +import com.android.systemui.scene.shared.logger.sceneLogger + +val Kosmos.sceneInteractor by + Kosmos.Fixture { + SceneInteractor( + applicationScope = applicationCoroutineScope, + repository = sceneContainerRepository, + powerInteractor = powerInteractor, + logger = sceneLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt new file mode 100644 index 000000000000..a3ceef021c59 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.shared.flag + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.sceneContainerFlags by Kosmos.Fixture { FakeSceneContainerFlags() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt new file mode 100644 index 000000000000..c5f24f4f049d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/logger/SceneLoggerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.shared.logger + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.sceneLogger by Kosmos.Fixture { mock<SceneLogger>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt new file mode 100644 index 000000000000..f9cdc1bea309 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/shared/model/SceneContainerConfigKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.scene.shared.model + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.sceneContainerConfig by + Kosmos.Fixture { FakeSceneContainerConfigModule().sceneContainerConfig } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt new file mode 100644 index 000000000000..38cedbca3886 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.shadeRepository: ShadeRepository by Kosmos.Fixture { fakeShadeRepository } +val Kosmos.fakeShadeRepository by Kosmos.Fixture { FakeShadeRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt new file mode 100644 index 000000000000..7da57f024ec7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade.domain.interactor + +import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.keyguardRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.flag.sceneContainerFlags +import com.android.systemui.shade.ShadeModule +import com.android.systemui.shade.data.repository.shadeRepository +import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository +import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor +import com.android.systemui.statusbar.phone.dozeParameters +import com.android.systemui.statusbar.pipeline.mobile.data.repository.userSetupRepository +import com.android.systemui.statusbar.policy.data.repository.deviceProvisioningRepository +import com.android.systemui.user.domain.interactor.userSwitcherInteractor + +var Kosmos.baseShadeInteractor: BaseShadeInteractor by + Kosmos.Fixture { + ShadeModule.provideBaseShadeInteractor( + sceneContainerFlags = sceneContainerFlags, + sceneContainerOn = { shadeInteractorSceneContainerImpl }, + sceneContainerOff = { shadeInteractorLegacyImpl }, + ) + } +val Kosmos.shadeInteractorSceneContainerImpl by + Kosmos.Fixture { + ShadeInteractorSceneContainerImpl( + scope = applicationCoroutineScope, + sceneInteractor = sceneInteractor, + sharedNotificationContainerInteractor = sharedNotificationContainerInteractor, + ) + } +val Kosmos.shadeInteractorLegacyImpl by + Kosmos.Fixture { + ShadeInteractorLegacyImpl( + scope = applicationCoroutineScope, + keyguardRepository = keyguardRepository, + sharedNotificationContainerInteractor = sharedNotificationContainerInteractor, + repository = shadeRepository + ) + } +var Kosmos.shadeInteractor: ShadeInteractor by Kosmos.Fixture { shadeInteractorImpl } +val Kosmos.shadeInteractorImpl by + Kosmos.Fixture { + ShadeInteractorImpl( + scope = applicationCoroutineScope, + deviceProvisioningRepository = deviceProvisioningRepository, + disableFlagsRepository = disableFlagsRepository, + dozeParams = dozeParameters, + keyguardRepository = fakeKeyguardRepository, + keyguardTransitionInteractor = keyguardTransitionInteractor, + powerInteractor = powerInteractor, + userSetupRepository = userSetupRepository, + userSwitcherInteractor = userSwitcherInteractor, + baseShadeInteractor = baseShadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt new file mode 100644 index 000000000000..e75359386b2f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shared/system/ActivityManagerWrapperKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.activityManagerWrapper by Kosmos.Fixture { mock<ActivityManagerWrapper>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt new file mode 100644 index 000000000000..27f7f6823cc7 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/CommandQueueKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.commandQueue by Kosmos.Fixture { mock<CommandQueue>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt new file mode 100644 index 000000000000..10151ac92c35 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/NotificationListenerSettingsRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.notificationListenerSettingsRepository by + Kosmos.Fixture { NotificationListenerSettingsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt new file mode 100644 index 000000000000..a373a8e1844d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.disableflags.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.disableFlagsRepository: DisableFlagsRepository by + Kosmos.Fixture { fakeDisableFlagsRepository } +val Kosmos.fakeDisableFlagsRepository by Kosmos.Fixture { FakeDisableFlagsRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt new file mode 100644 index 000000000000..5507d6c2c90d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepositoryKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.activeNotificationListRepository by Kosmos.Fixture { ActiveNotificationListRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt new file mode 100644 index 000000000000..ed62fda6fc99 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/HeadsUpNotificationIconViewStateRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.data.repository + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.headsUpNotificationIconViewStateRepository by + Kosmos.Fixture { HeadsUpNotificationIconViewStateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt new file mode 100644 index 000000000000..f2b9da413c22 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/repository/NotificationsKeyguardViewStateRepositoryKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.notificationsKeyguardViewStateRepository: NotificationsKeyguardViewStateRepository by + Kosmos.Fixture { fakeNotificationsKeyguardViewStateRepository } +val Kosmos.fakeNotificationsKeyguardViewStateRepository by + Kosmos.Fixture { FakeNotificationsKeyguardViewStateRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt new file mode 100644 index 000000000000..3d7fb6d91393 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository + +val Kosmos.activeNotificationsInteractor by + Kosmos.Fixture { ActiveNotificationsInteractor(activeNotificationListRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt new file mode 100644 index 000000000000..d14c8548f3cf --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/domain/interactor/HeadsUpNotificationIconInteractorKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.notification.data.repository.headsUpNotificationIconViewStateRepository + +val Kosmos.headsUpNotificationIconInteractor by + Kosmos.Fixture { HeadsUpNotificationIconInteractor(headsUpNotificationIconViewStateRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt new file mode 100644 index 000000000000..e7bd5ea2b174 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorKosmos.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.systemui.statusbar.notification.icon.domain.interactor + +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository +import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.wm.shell.bubbles.bubblesOptional +import kotlinx.coroutines.ExperimentalCoroutinesApi + +val Kosmos.alwaysOnDisplayNotificationIconsInteractor by + Kosmos.Fixture { + AlwaysOnDisplayNotificationIconsInteractor( + deviceEntryInteractor = deviceEntryInteractor, + iconsInteractor = notificationIconsInteractor, + ) + } +val Kosmos.statusBarNotificationIconsInteractor by + Kosmos.Fixture { + StatusBarNotificationIconsInteractor( + iconsInteractor = notificationIconsInteractor, + settingsRepository = notificationListenerSettingsRepository, + ) + } +val Kosmos.notificationIconsInteractor by + Kosmos.Fixture { + NotificationIconsInteractor( + activeNotificationsInteractor = activeNotificationsInteractor, + bubbles = bubblesOptional, + keyguardViewStateRepository = notificationsKeyguardViewStateRepository, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt new file mode 100644 index 000000000000..6295b83a2bd0 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import android.content.res.mainResources +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor + +val Kosmos.notificationIconContainerAlwaysOnDisplayViewModel by + Kosmos.Fixture { + NotificationIconContainerAlwaysOnDisplayViewModel( + iconsInteractor = alwaysOnDisplayNotificationIconsInteractor, + keyguardInteractor = keyguardInteractor, + keyguardTransitionInteractor = keyguardTransitionInteractor, + resources = mainResources, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt new file mode 100644 index 000000000000..04bb52d101db --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelKosmos.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.icon.ui.viewmodel + +import android.content.res.mainResources +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor +import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor +import com.android.systemui.statusbar.notification.icon.domain.interactor.statusBarNotificationIconsInteractor +import com.android.systemui.statusbar.phone.domain.interactor.darkIconInteractor + +val Kosmos.notificationIconContainerStatusBarViewModel by + Kosmos.Fixture { + NotificationIconContainerStatusBarViewModel( + darkIconInteractor = darkIconInteractor, + iconsInteractor = statusBarNotificationIconsInteractor, + headsUpIconInteractor = headsUpNotificationIconInteractor, + keyguardInteractor = keyguardInteractor, + notificationsInteractor = activeNotificationsInteractor, + resources = mainResources, + shadeInteractor = shadeInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt new file mode 100644 index 000000000000..3403227f6d27 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.stack.domain.interactor + +import android.content.applicationContext +import com.android.systemui.common.ui.data.repository.configurationRepository +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.policy.splitShadeStateController + +val Kosmos.sharedNotificationContainerInteractor by + Kosmos.Fixture { + SharedNotificationContainerInteractor( + configurationRepository = configurationRepository, + context = applicationContext, + splitShadeStateController = splitShadeStateController, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt new file mode 100644 index 000000000000..9f6b181f8348 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeParametersKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.dozeParameters by Kosmos.Fixture { mock<DozeParameters>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt new file mode 100644 index 000000000000..d4c21f66d394 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ScreenOffAnimationControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +var Kosmos.screenOffAnimationController by Kosmos.Fixture { mock<ScreenOffAnimationController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt new file mode 100644 index 000000000000..977dcb738ba1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.darkIconRepository: DarkIconRepository by Kosmos.Fixture { fakeDarkIconRepository } +val Kosmos.fakeDarkIconRepository by Kosmos.Fixture { FakeDarkIconRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt new file mode 100644 index 000000000000..db678d487b3d --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/domain/interactor/DarkIconInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.data.repository.darkIconRepository + +val Kosmos.darkIconInteractor by Kosmos.Fixture { DarkIconInteractor(darkIconRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt new file mode 100644 index 000000000000..7b9634a7abb5 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/UserSetupRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.userSetupRepository: UserSetupRepository by Kosmos.Fixture { fakeUserSetupRepository } +val Kosmos.fakeUserSetupRepository by Kosmos.Fixture { FakeUserSetupRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt new file mode 100644 index 000000000000..18a2f9482df8 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/ConfigurationControllerKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock + +val Kosmos.configurationController by Kosmos.Fixture { mock<ConfigurationController>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt new file mode 100644 index 000000000000..6a77c882eaa1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceProvisionedController: DeviceProvisionedController by + Kosmos.Fixture { fakeDeviceProvisionedController } +val Kosmos.fakeDeviceProvisionedController by Kosmos.Fixture { FakeDeviceProvisionedController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt new file mode 100644 index 000000000000..5e430381e293 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/SplitShadeStateControllerKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.splitShadeStateController: SplitShadeStateController by + Kosmos.Fixture { resourcesSplitShadeStateController } +val Kosmos.resourcesSplitShadeStateController by + Kosmos.Fixture { ResourcesSplitShadeStateController() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt new file mode 100644 index 000000000000..56a0e02e41dc --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/data/repository/DeviceProvisioningRepositoryKosmos.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.deviceProvisioningRepository: DeviceProvisioningRepository by + Kosmos.Fixture { fakeDeviceProvisioningRepository } +val Kosmos.fakeDeviceProvisioningRepository by Kosmos.Fixture { FakeDeviceProvisioningRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt new file mode 100644 index 000000000000..6bb5ec5f0313 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/data/repository/TelephonyRepositoryKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony.data.repository + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.telephonyRepository: TelephonyRepository by Kosmos.Fixture { fakeTelephonyRepository } +val Kosmos.fakeTelephonyRepository by Kosmos.Fixture { FakeTelephonyRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt new file mode 100644 index 000000000000..02ca96e3521b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractorKosmos.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.telephony.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.telephony.data.repository.telephonyRepository + +val Kosmos.telephonyInteractor by Kosmos.Fixture { TelephonyInteractor(telephonyRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt index 8bce9b6d461d..9bb52623bdcd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/UserRepositoryKosmos.kt @@ -18,4 +18,5 @@ package com.android.systemui.user.data.repository import com.android.systemui.kosmos.Kosmos -val Kosmos.userRepository by Kosmos.Fixture { FakeUserRepository() } +var Kosmos.userRepository: UserRepository by Kosmos.Fixture { fakeUserRepository } +val Kosmos.fakeUserRepository by Kosmos.Fixture { FakeUserRepository() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt index e69570433d43..3b1c3f0198c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/GuestUserInteractorKosmos.kt @@ -16,28 +16,32 @@ package com.android.systemui.user.domain.interactor +import android.app.admin.devicePolicyManager +import android.content.applicationContext +import android.os.userManager +import com.android.internal.logging.uiEventLogger +import com.android.systemui.guestResetOrExitSessionReceiver +import com.android.systemui.guestResumeSessionReceiver import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.context -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.userManager +import com.android.systemui.statusbar.policy.deviceProvisionedController import com.android.systemui.user.data.repository.userRepository -import com.android.systemui.util.mockito.mock val Kosmos.guestUserInteractor by Kosmos.Fixture { GuestUserInteractor( - applicationContext = context, - applicationScope = lifecycleScope, + applicationContext = applicationContext, + applicationScope = applicationCoroutineScope, mainDispatcher = testDispatcher, backgroundDispatcher = testDispatcher, manager = userManager, - deviceProvisionedController = mock(), repository = userRepository, - devicePolicyManager = mock(), + deviceProvisionedController = deviceProvisionedController, + devicePolicyManager = devicePolicyManager, refreshUsersScheduler = refreshUsersScheduler, - uiEventLogger = mock(), - resumeSessionReceiver = mock(), - resetOrExitSessionReceiver = mock(), + uiEventLogger = uiEventLogger, + resumeSessionReceiver = guestResumeSessionReceiver, + resetOrExitSessionReceiver = guestResetOrExitSessionReceiver, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt new file mode 100644 index 000000000000..de9f69bc9215 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.user.domain.interactor + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.headlessSystemUserMode by Kosmos.Fixture { HeadlessSystemUserModeImpl() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt index 87a2fe0249e3..14da8b0f461c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerKosmos.kt @@ -17,15 +17,11 @@ package com.android.systemui.user.domain.interactor import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.user.data.repository.userRepository val Kosmos.refreshUsersScheduler by Kosmos.Fixture { - RefreshUsersScheduler( - applicationScope = lifecycleScope, - mainDispatcher = testDispatcher, - repository = userRepository, - ) + RefreshUsersScheduler(applicationCoroutineScope, testDispatcher, userRepository) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt new file mode 100644 index 000000000000..427f92a7f514 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.user.domain.interactor + +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.user.data.repository.userRepository + +val Kosmos.selectedUserInteractor by + Kosmos.Fixture { SelectedUserInteractor(userRepository, featureFlagsClassic) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt index 6d6b2683a7ea..42c77aaac53f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt @@ -16,42 +16,41 @@ package com.android.systemui.user.domain.interactor +import android.app.activityManager +import android.content.applicationContext +import android.os.userManager +import com.android.internal.logging.uiEventLogger +import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.broadcast.broadcastDispatcher -import com.android.systemui.flags.featureFlags -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.context -import com.android.systemui.kosmos.lifecycleScope +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.userManager -import com.android.systemui.telephony.data.repository.FakeTelephonyRepository -import com.android.systemui.telephony.domain.interactor.TelephonyInteractor +import com.android.systemui.plugins.activityStarter +import com.android.systemui.telephony.domain.interactor.telephonyInteractor import com.android.systemui.user.data.repository.userRepository -import com.android.systemui.util.mockito.mock +import com.android.systemui.utils.userRestrictionChecker val Kosmos.userSwitcherInteractor by Kosmos.Fixture { UserSwitcherInteractor( - applicationContext = context, + applicationContext = applicationContext, repository = userRepository, - activityStarter = mock(), - keyguardInteractor = - KeyguardInteractorFactory.create(featureFlags = featureFlags).keyguardInteractor, - featureFlags = featureFlags, + activityStarter = activityStarter, + keyguardInteractor = keyguardInteractor, + featureFlags = featureFlagsClassic, manager = userManager, - headlessSystemUserMode = mock(), - applicationScope = lifecycleScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), + headlessSystemUserMode = headlessSystemUserMode, + applicationScope = applicationCoroutineScope, + telephonyInteractor = telephonyInteractor, broadcastDispatcher = broadcastDispatcher, - keyguardUpdateMonitor = mock(), + keyguardUpdateMonitor = keyguardUpdateMonitor, backgroundDispatcher = testDispatcher, - activityManager = mock(), + activityManager = activityManager, refreshUsersScheduler = refreshUsersScheduler, guestUserInteractor = guestUserInteractor, - uiEventLogger = mock(), - userRestrictionChecker = mock() + uiEventLogger = uiEventLogger, + userRestrictionChecker = userRestrictionChecker, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt new file mode 100644 index 000000000000..914e65427f41 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/time/FakeSystemClockKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.time + +import com.android.systemui.kosmos.Kosmos + +var Kosmos.fakeSystemClock by Kosmos.Fixture { FakeSystemClock() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt new file mode 100644 index 000000000000..24d5d2f72289 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/UserRestrictionCheckerKosmos.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.utils + +import com.android.systemui.kosmos.Kosmos + +val Kosmos.userRestrictionChecker by Kosmos.Fixture { UserRestrictionChecker() } diff --git a/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt new file mode 100644 index 000000000000..a7a37b22c969 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/wm/shell/bubbles/BubblesKosmos.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.bubbles + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.util.mockito.mock +import java.util.Optional + +var Kosmos.bubblesOptional by Kosmos.Fixture { Optional.of(bubbles) } +var Kosmos.bubbles by Kosmos.Fixture { mock<Bubbles> {} } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index b9c269c91651..71a1f012e5cb 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -75,7 +75,6 @@ import android.companion.IOnMessageReceivedListener; import android.companion.IOnTransportsChangedListener; import android.companion.ISystemDataTransferCallback; import android.companion.datatransfer.PermissionSyncRequest; -import android.companion.utils.FeatureUtils; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; @@ -829,11 +828,6 @@ public class CompanionDeviceManagerService extends SystemService { @Override public PendingIntent buildPermissionTransferUserConsentIntent(String packageName, int userId, int associationId) { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling" - + " buildPermissionTransferUserConsentIntent, but this API is disabled by" - + " the system."); - } return mSystemDataTransferProcessor.buildPermissionTransferUserConsentIntent( packageName, userId, associationId); } @@ -841,10 +835,6 @@ public class CompanionDeviceManagerService extends SystemService { @Override public void startSystemDataTransfer(String packageName, int userId, int associationId, ISystemDataTransferCallback callback) { - if (!FeatureUtils.isPermSyncEnabled()) { - throw new UnsupportedOperationException("Calling startSystemDataTransfer, but this" - + " API is disabled by the system."); - } mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId, associationId, callback); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 1f6261383961..23e7ce68c1d0 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -20,6 +20,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; +import android.companion.Flags; import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; @@ -65,7 +66,14 @@ class CompanionDeviceShellCommand extends ShellCommand { public int onCommand(String cmd) { final PrintWriter out = getOutPrintWriter(); final int associationId; + try { + if ("simulate-device-event".equals(cmd) && Flags.devicePresence()) { + associationId = getNextIntArgRequired(); + int event = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, event); + return 0; + } switch (cmd) { case "list": { final int userId = getNextIntArgRequired(); @@ -107,10 +115,15 @@ class CompanionDeviceShellCommand extends ShellCommand { mService.loadAssociationsFromDisk(); break; - case "simulate-device-event": + case "simulate-device-appeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 0); + break; + + case "simulate-device-disappeared": associationId = getNextIntArgRequired(); - int event = getNextIntArgRequired(); - mDevicePresenceMonitor.simulateDeviceEvent(associationId, event); + mDevicePresenceMonitor.simulateDeviceEvent(associationId, /* event */ 1); + break; case "remove-inactive-associations": { // This command should trigger the same "clean-up" job as performed by the @@ -346,9 +359,7 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); - pw.println(" simulate-device-event ASSOCIATION_ID EVENT"); - pw.println(" Simulate the companion device event changes:"); - pw.println(" Case(0): "); + pw.println(" simulate-device-appeared ASSOCIATION_ID"); pw.println(" Make CDM act as if the given companion device has appeared."); pw.println(" I.e. bind the associated companion application's"); pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); @@ -356,18 +367,43 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'"); pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out" + "."); - pw.println(" Case(1): "); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-disappeared ASSOCIATION_ID"); pw.println(" Make CDM act as if the given companion device has disappeared."); pw.println(" I.e. unbind the associated companion application's"); pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback."); pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); pw.println(" 60 seconds ago."); - pw.println(" Case(2): "); - pw.println(" Make CDM act as if the given companion device is BT connected "); - pw.println(" Case(3): "); - pw.println(" Make CDM act as if the given companion device is BT disconnected "); - pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + if (Flags.devicePresence()) { + pw.println(" simulate-device-event ASSOCIATION_ID EVENT"); + pw.println(" Simulate the companion device event changes:"); + pw.println(" Case(0): "); + pw.println(" Make CDM act as if the given companion device has appeared."); + pw.println(" I.e. bind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); + pw.println(" The CDM will consider the devices as present for" + + "60 seconds and then"); + pw.println(" will act as if device disappeared, unless" + + "'simulate-device-disappeared'"); + pw.println(" or 'simulate-device-appeared' is called again before 60 seconds" + + "run out."); + pw.println(" Case(1): "); + pw.println(" Make CDM act as if the given companion device has disappeared."); + pw.println(" I.e. unbind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared()" + + "callback."); + pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); + pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); + pw.println(" 60 seconds ago."); + pw.println(" Case(2): "); + pw.println(" Make CDM act as if the given companion device is BT connected "); + pw.println(" Case(3): "); + pw.println(" Make CDM act as if the given companion device is BT disconnected "); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + } pw.println(" remove-inactive-associations"); pw.println(" Remove self-managed associations that have not been active "); diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 8fea078c3183..e42b9356cca3 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -79,7 +79,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange void onDeviceDisappeared(int associationId); /**Invoked when device has corresponding event changes. */ - void onDeviceEvent(int associationId, int state); + void onDeviceEvent(int associationId, int event); } private final @NonNull AssociationStore mAssociationStore; diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index 7907d616d1b5..77b6d583808c 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -1182,8 +1182,8 @@ public class BinaryTransparencyService extends SystemService { // we are only interested in doing things at PHASE_BOOT_COMPLETED if (phase == PHASE_BOOT_COMPLETED) { - Slog.i(TAG, "Boot completed. Getting VBMeta Digest."); - getVBMetaDigestInformation(); + Slog.i(TAG, "Boot completed. Getting boot integrity data."); + collectBootIntegrityInfo(); // Log to statsd // TODO(b/264061957): For now, biometric system properties are always collected if users @@ -1458,10 +1458,19 @@ public class BinaryTransparencyService extends SystemService { } } - private void getVBMetaDigestInformation() { + private void collectBootIntegrityInfo() { mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE); Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest)); FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest); + + if (android.security.Flags.binaryTransparencySepolicyHash()) { + byte[] sepolicyHash = PackageUtils.computeSha256DigestForLargeFileAsBytes( + "/sys/fs/selinux/policy", PackageUtils.createLargeFileBuffer()); + String sepolicyHashEncoded = HexEncoding.encodeToString(sepolicyHash, false); + Slog.d(TAG, "sepolicy hash: " + sepolicyHashEncoded); + FrameworkStatsLog.write(FrameworkStatsLog.BOOT_INTEGRITY_INFO_REPORTED, + sepolicyHashEncoded, mVbmetaDigest); + } } /** diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index b9ccbfbf55e7..c5073001a672 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -576,7 +576,7 @@ public final class AuthSession implements IBinder.DeathRecipient { } void onDialogAnimatedIn(boolean startFingerprintNow) { - if (mState != STATE_AUTH_STARTED) { + if (mState != STATE_AUTH_STARTED && mState != STATE_ERROR_PENDING_SYSUI) { Slog.e(TAG, "onDialogAnimatedIn, unexpected state: " + mState); return; } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index fac727f17283..dff14b5fbdd0 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -1114,12 +1114,22 @@ public final class DeviceStateManagerService extends SystemService { public void notifyDeviceStateInfoAsync(@NonNull DeviceStateInfo info) { mHandler.post(() -> { + boolean tracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER); + if (tracingEnabled) { // To avoid creating the string when not needed. + Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, + "notifyDeviceStateInfoAsync(pid=" + mPid + ")"); + } try { mCallback.onDeviceStateInfoChanged(info); } catch (RemoteException ex) { Slog.w(TAG, "Failed to notify process " + mPid + " that device state changed.", ex); } + finally { + if (tracingEnabled) { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } }); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index bae06347d8a2..c2a1b6ba6c34 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4021,11 +4021,8 @@ public class NotificationManagerService extends SystemService { Slog.e(TAG, "Failed to getApplicationInfo() in canUseFullScreenIntent()", e); return false; } - final boolean showStickyHunIfDenied = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags - .SHOW_STICKY_HUN_FOR_DENIED_FSI); return checkUseFullScreenIntentPermission(attributionSource, applicationInfo, - showStickyHunIfDenied /* isAppOpPermission */, false /* forDataDelivery */); + false /* forDataDelivery */); } @Override @@ -7274,28 +7271,12 @@ public class NotificationManagerService extends SystemService { notification.flags &= ~FLAG_FSI_REQUESTED_BUT_DENIED; if (notification.fullScreenIntent != null) { - final boolean forceDemoteFsiToStickyHun = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE); - if (forceDemoteFsiToStickyHun) { + final AttributionSource attributionSource = + new AttributionSource.Builder(notificationUid).setPackageName(pkg).build(); + final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission( + attributionSource, ai, true /* forDataDelivery */); + if (!canUseFullScreenIntent) { makeStickyHun(notification, pkg, userId); - } else { - final AttributionSource attributionSource = - new AttributionSource.Builder(notificationUid).setPackageName(pkg).build(); - final boolean showStickyHunIfDenied = mFlagResolver.isEnabled( - SystemUiSystemPropertiesFlags.NotificationFlags - .SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean canUseFullScreenIntent = checkUseFullScreenIntentPermission( - attributionSource, ai, showStickyHunIfDenied /* isAppOpPermission */, - true /* forDataDelivery */); - if (!canUseFullScreenIntent) { - if (showStickyHunIfDenied) { - makeStickyHun(notification, pkg, userId); - } else { - notification.fullScreenIntent = null; - Slog.w(TAG, "Package " + pkg + ": Use of fullScreenIntent requires the" - + "USE_FULL_SCREEN_INTENT permission"); - } - } } } @@ -7402,27 +7383,20 @@ public class NotificationManagerService extends SystemService { } private boolean checkUseFullScreenIntentPermission(@NonNull AttributionSource attributionSource, - @NonNull ApplicationInfo applicationInfo, boolean isAppOpPermission, + @NonNull ApplicationInfo applicationInfo, boolean forDataDelivery) { if (applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q) { return true; } - if (isAppOpPermission) { - final int permissionResult; - if (forDataDelivery) { - permissionResult = mPermissionManager.checkPermissionForDataDelivery( - permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null); - } else { - permissionResult = mPermissionManager.checkPermissionForPreflight( - permission.USE_FULL_SCREEN_INTENT, attributionSource); - } - return permissionResult == PermissionManager.PERMISSION_GRANTED; + final int permissionResult; + if (forDataDelivery) { + permissionResult = mPermissionManager.checkPermissionForDataDelivery( + permission.USE_FULL_SCREEN_INTENT, attributionSource, /* message= */ null); } else { - final int permissionResult = getContext().checkPermission( - permission.USE_FULL_SCREEN_INTENT, attributionSource.getPid(), - attributionSource.getUid()); - return permissionResult == PERMISSION_GRANTED; + permissionResult = mPermissionManager.checkPermissionForPreflight( + permission.USE_FULL_SCREEN_INTENT, attributionSource); } + return permissionResult == PermissionManager.PERMISSION_GRANTED; } private void checkRemoteViews(String pkg, String tag, int id, Notification notification) { diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index d2e980b7e355..9a6ea2c2aeb8 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -530,16 +530,13 @@ interface NotificationRecordLogger { this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter(); this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r); - final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() - .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean hasFullScreenIntent = p.r.getSbn().getNotification().fullScreenIntent != null; final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0; - this.fsi_state = NotificationRecordLogger.getFsiState(isStickyHunFlagEnabled, + this.fsi_state = NotificationRecordLogger.getFsiState( hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType); this.is_locked = p.r.isLocked(); @@ -587,13 +584,10 @@ interface NotificationRecordLogger { * @return FrameworkStatsLog enum of the state of the full screen intent posted with this * notification. */ - static int getFsiState(boolean isStickyHunFlagEnabled, - boolean hasFullScreenIntent, + static int getFsiState(boolean hasFullScreenIntent, boolean hasFsiRequestedButDeniedFlag, NotificationReportedEvent eventType) { - - if (!isStickyHunFlagEnabled - || eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) { + if (eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) { // Zeroes in protos take zero bandwidth, but non-zero numbers take bandwidth, // so we should log 0 when possible. return 0; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 783e9bbb034f..252664a7e4e4 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -2143,10 +2143,7 @@ public class PreferencesHelper implements RankingConfig { * @return State of the full screen intent permission for this package. */ @VisibleForTesting - int getFsiState(String pkg, int uid, boolean requestedFSIPermission, boolean isFlagEnabled) { - if (!isFlagEnabled) { - return 0; - } + int getFsiState(String pkg, int uid, boolean requestedFSIPermission) { if (!requestedFSIPermission) { return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; } @@ -2167,10 +2164,8 @@ public class PreferencesHelper implements RankingConfig { * the user. */ @VisibleForTesting - boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags, - boolean isStickyHunFlagEnabled) { - if (!isStickyHunFlagEnabled - || fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { + boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags) { + if (fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { return false; } return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; @@ -2213,22 +2208,18 @@ public class PreferencesHelper implements RankingConfig { pkgsWithPermissionsToHandle.remove(key); } - final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() - .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); - final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission( android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid); - final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission, - isStickyHunFlagEnabled); + final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission); final int currentPermissionFlags = mPm.getPermissionFlags( android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, UserHandle.getUserHandleForUid(r.uid)); final boolean fsiIsUserSet = - isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags, - isStickyHunFlagEnabled); + isFsiPermissionUserSet(r.pkg, r.uid, fsiState, + currentPermissionFlags); events.add(FrameworkStatsLog.buildStatsEvent( PACKAGE_NOTIFICATION_PREFERENCES, diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index 79cd2a0b236f..92d469ccbfac 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -259,6 +259,19 @@ public interface Computer extends PackageDataSnapshot { */ boolean shouldFilterApplicationIncludingUninstalled(@Nullable PackageStateInternal ps, int callingUid, int userId); + + /** + * Different from + * {@link #shouldFilterApplicationIncludingUninstalled(PackageStateInternal, int, int)}, the + * function returns {@code true} if: + * <ul> + * <li>The target package is not archived. + * <li>The package cannot be found in the device or has been uninstalled in the current user. + * </ul> + */ + boolean shouldFilterApplicationIncludingUninstalledNotArchived( + @Nullable PackageStateInternal ps, + int callingUid, int userId); /** * Different from {@link #shouldFilterApplication(SharedUserSetting, int, int)}, the function * returns {@code true} if packages with the same shared user are all uninstalled in the current diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 11a6d1b8f9a4..e5c4ccc73bc3 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -2455,7 +2455,8 @@ public class ComputerEngine implements Computer { */ public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid, @Nullable ComponentName component, - @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) { + @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall, + boolean filterArchived) { if (Process.isSdkSandboxUid(callingUid)) { int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid); // SDK sandbox should be able to see it's client app @@ -2469,14 +2470,20 @@ public class ComputerEngine implements Computer { } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; + final boolean packageArchivedForUser = ps != null && PackageArchiver.isArchived( + ps.getUserStateOrDefault(userId)); // Don't treat hiddenUntilInstalled as an uninstalled state, phone app needs to access // these hidden application details to customize carrier apps. Also, allowing the system // caller accessing to application across users. if (ps == null || (filterUninstall - && !isSystemOrRootOrShell(callingUid) - && !ps.isHiddenUntilInstalled() - && !ps.getUserStateOrDefault(userId).isInstalled())) { + && !isSystemOrRootOrShell(callingUid) + && !ps.isHiddenUntilInstalled() + && !ps.getUserStateOrDefault(userId).isInstalled() + // Archived packages behave like uninstalled packages. So if filterUninstall is + // set to true, we dismiss filtering some uninstalled package only if it is + // archived and filterArchived is set as false. + && (!packageArchivedForUser || filterArchived))) { // If caller is instant app or sdk sandbox and ps is null, pretend the application // exists, but, needs to be filtered return (callerIsInstantApp || filterUninstall || Process.isSdkSandboxUid(callingUid)); @@ -2524,7 +2531,20 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) + */ + public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, + int callingUid, @Nullable ComponentName component, + @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) { + return shouldFilterApplication( + ps, callingUid, component, componentType, userId, filterUninstall, + true /* filterArchived */); + } + + /** + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps, int callingUid, @Nullable ComponentName component, @@ -2534,7 +2554,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication( @Nullable PackageStateInternal ps, int callingUid, int userId) { @@ -2543,7 +2564,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplication(@NonNull SharedUserSetting sus, int callingUid, int userId) { @@ -2558,7 +2580,8 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplicationIncludingUninstalled( @Nullable PackageStateInternal ps, int callingUid, int userId) { @@ -2567,7 +2590,19 @@ public class ComputerEngine implements Computer { } /** - * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean) + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) + */ + public final boolean shouldFilterApplicationIncludingUninstalledNotArchived( + @Nullable PackageStateInternal ps, int callingUid, int userId) { + return shouldFilterApplication( + ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */, + false /* filterArchived */); + } + + /** + * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean, + * boolean) */ public final boolean shouldFilterApplicationIncludingUninstalled( @NonNull SharedUserSetting sus, int callingUid, int userId) { @@ -5015,7 +5050,7 @@ public class ComputerEngine implements Computer { String installerPackageName = installSource.mInstallerPackageName; if (installerPackageName != null) { final PackageStateInternal ps = mSettings.getPackage(installerPackageName); - if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, + if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid, UserHandle.getUserId(callingUid))) { installerPackageName = null; } @@ -5033,7 +5068,8 @@ public class ComputerEngine implements Computer { return InstallSource.EMPTY; } - if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { + if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid, + userId)) { return null; } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 65c6329587a5..3b3d79e7dee1 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -440,7 +440,7 @@ final class DeletePackageHelper { if (outInfo != null) { // Remember which users are affected, before the installed states are modified outInfo.mRemovedUsers = (systemApp || userId == UserHandle.USER_ALL) - ? ps.queryInstalledUsers(allUserHandles, /* installed= */true) + ? ps.queryUsersInstalledOrHasData(allUserHandles) : new int[]{userId}; } diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java index eff6157380c0..d2a4c2713097 100644 --- a/services/core/java/com/android/server/pm/PackageArchiver.java +++ b/services/core/java/com/android/server/pm/PackageArchiver.java @@ -37,6 +37,7 @@ import android.app.BroadcastOptions; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; import android.content.pm.ArchivedActivityParcel; import android.content.pm.ArchivedPackageParcel; import android.content.pm.LauncherActivityInfo; @@ -140,6 +141,8 @@ public class PackageArchiver { } snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "archiveApp"); + verifyUninstallPermissions(); + CompletableFuture<ArchiveState> archiveStateFuture; try { archiveStateFuture = createArchiveState(packageName, userId); @@ -182,6 +185,7 @@ public class PackageArchiver { throws PackageManager.NameNotFoundException { PackageStateInternal ps = getPackageState(packageName, mPm.snapshotComputer(), Binder.getCallingUid(), userId); + verifyNotSystemApp(ps.getFlags()); String responsibleInstallerPackage = getResponsibleInstallerPackage(ps); verifyInstaller(responsibleInstallerPackage, userId); verifyOptOutStatus(packageName, @@ -316,6 +320,13 @@ public class PackageArchiver { return intentReceivers != null && !intentReceivers.getList().isEmpty(); } + private void verifyNotSystemApp(int flags) throws PackageManager.NameNotFoundException { + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0 || ( + (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) { + throw new PackageManager.NameNotFoundException("System apps cannot be archived."); + } + } + /** * Returns true if the app is archivable. */ @@ -337,6 +348,11 @@ public class PackageArchiver { throw new ParcelableException(e); } + if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0 || ( + (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) { + return false; + } + if (isAppOptedOutOfArchiving(packageName, ps.getAppId())) { return false; } @@ -372,9 +388,11 @@ public class PackageArchiver { void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, + @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle) { Objects.requireNonNull(packageName); Objects.requireNonNull(callerPackageName); + Objects.requireNonNull(statusReceiver); Objects.requireNonNull(userHandle); Computer snapshot = mPm.snapshotComputer(); @@ -385,6 +403,8 @@ public class PackageArchiver { } snapshot.enforceCrossUserPermission(binderUid, userId, true, true, "unarchiveApp"); + verifyInstallPermissions(); + PackageStateInternal ps; try { ps = getPackageState(packageName, snapshot, binderUid, userId); @@ -400,9 +420,12 @@ public class PackageArchiver { packageName))); } + // TODO(b/305902395) Introduce a confirmation dialog if the requestor only holds + // REQUEST_INSTALL permission. int draftSessionId; try { - draftSessionId = createDraftSession(packageName, installerPackage, userId); + draftSessionId = createDraftSession(packageName, installerPackage, statusReceiver, + userId); } catch (RuntimeException e) { if (e.getCause() instanceof IOException) { throw ExceptionUtils.wrap((IOException) e.getCause()); @@ -415,11 +438,36 @@ public class PackageArchiver { () -> unarchiveInternal(packageName, userHandle, installerPackage, draftSessionId)); } - private int createDraftSession(String packageName, String installerPackage, int userId) { + private void verifyInstallPermissions() { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission( + Manifest.permission.REQUEST_INSTALL_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the com.android.permission.INSTALL_PACKAGES " + + "or com.android.permission.REQUEST_INSTALL_PACKAGES permission to request " + + "an unarchival."); + } + } + + private void verifyUninstallPermissions() { + if (mContext.checkCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES) + != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission( + Manifest.permission.REQUEST_DELETE_PACKAGES) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("You need the com.android.permission.DELETE_PACKAGES " + + "or com.android.permission.REQUEST_DELETE_PACKAGES permission to request " + + "an archival."); + } + } + + private int createDraftSession(String packageName, String installerPackage, + IntentSender statusReceiver, int userId) { PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); sessionParams.setAppPackageName(packageName); sessionParams.installFlags = INSTALL_UNARCHIVE_DRAFT; + sessionParams.unarchiveIntentSender = statusReceiver; + int installerUid = mPm.snapshotComputer().getPackageUid(installerPackage, 0, userId); // Handles case of repeated unarchival calls for the same package. int existingSessionId = mPm.mInstallerService.getExistingDraftSessionId(installerUid, diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index af43a8bec832..c9663fc30ef5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -16,8 +16,14 @@ package com.android.server.pm; +import static android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_DELETED_BY_DO; import static android.content.pm.PackageInstaller.LOCATION_DATA_APP; +import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE; +import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_NO_CONNECTIVITY; +import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED; +import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR; +import static android.content.pm.PackageInstaller.UNARCHIVAL_OK; import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -37,6 +43,7 @@ import android.app.BroadcastOptions; import android.app.Notification; import android.app.NotificationManager; import android.app.PackageDeleteObserver; +import android.app.PendingIntent; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManager; import android.app.admin.DevicePolicyManagerInternal; @@ -56,6 +63,7 @@ import android.content.pm.PackageInstaller.InstallConstraints; import android.content.pm.PackageInstaller.InstallConstraintsResult; import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageInstaller.SessionParams; +import android.content.pm.PackageInstaller.UnarchivalStatus; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; @@ -71,6 +79,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; +import android.os.ParcelableException; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallbackList; @@ -1630,8 +1639,10 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void requestUnarchive( @NonNull String packageName, @NonNull String callerPackageName, + @NonNull IntentSender statusReceiver, @NonNull UserHandle userHandle) { - mPackageArchiver.requestUnarchive(packageName, callerPackageName, userHandle); + mPackageArchiver.requestUnarchive(packageName, callerPackageName, statusReceiver, + userHandle); } @Override @@ -1689,6 +1700,102 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + // TODO(b/307299702) Implement error dialog and propagate userActionIntent. + @Override + public void reportUnarchivalStatus( + int unarchiveId, + @UnarchivalStatus int status, + long requiredStorageBytes, + @Nullable PendingIntent userActionIntent, + @NonNull UserHandle userHandle) { + verifyReportUnarchiveStatusInput( + status, requiredStorageBytes, userActionIntent, userHandle); + + int userId = userHandle.getIdentifier(); + int binderUid = Binder.getCallingUid(); + + synchronized (mSessions) { + PackageInstallerSession session = mSessions.get(unarchiveId); + if (session == null || session.userId != userId + || session.params.appPackageName == null) { + throw new ParcelableException(new PackageManager.NameNotFoundException( + TextUtils.formatSimple( + "No valid session with unarchival ID %s found for user %s.", + unarchiveId, userId))); + } + + if (!isCallingUidOwner(session)) { + throw new SecurityException(TextUtils.formatSimple( + "The caller UID %s does not have access to the session with unarchiveId " + + "%d.", + binderUid, unarchiveId)); + } + + IntentSender unarchiveIntentSender = session.params.unarchiveIntentSender; + if (unarchiveIntentSender == null) { + throw new IllegalStateException( + TextUtils.formatSimple( + "Unarchival status for ID %s has already been set or a " + + "session has been created for it already by the " + + "caller.", + unarchiveId)); + } + + // Execute expensive calls outside the sync block. + mPm.mHandler.post( + () -> notifyUnarchivalListener(status, session.params.appPackageName, + unarchiveIntentSender)); + session.params.unarchiveIntentSender = null; + if (status != UNARCHIVAL_OK) { + Binder.withCleanCallingIdentity(session::abandon); + } + } + } + + private static void verifyReportUnarchiveStatusInput(int status, long requiredStorageBytes, + @Nullable PendingIntent userActionIntent, + @NonNull UserHandle userHandle) { + Objects.requireNonNull(userHandle); + if (status == UNARCHIVAL_ERROR_USER_ACTION_NEEDED) { + Objects.requireNonNull(userActionIntent); + } + if (status == UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE && requiredStorageBytes <= 0) { + throw new IllegalStateException( + "Insufficient storage error set, but requiredStorageBytes unspecified."); + } + if (status != UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE && requiredStorageBytes > 0) { + throw new IllegalStateException( + TextUtils.formatSimple("requiredStorageBytes set, but error is %s.", status) + ); + } + if (!List.of( + UNARCHIVAL_OK, + UNARCHIVAL_ERROR_USER_ACTION_NEEDED, + UNARCHIVAL_ERROR_INSUFFICIENT_STORAGE, + UNARCHIVAL_ERROR_NO_CONNECTIVITY, + UNARCHIVAL_GENERIC_ERROR).contains(status)) { + throw new IllegalStateException("Invalid status code passed " + status); + } + } + + private void notifyUnarchivalListener(int status, String packageName, + IntentSender unarchiveIntentSender) { + final Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName); + fillIn.putExtra(PackageInstaller.EXTRA_UNARCHIVE_STATUS, status); + // TODO(b/307299702) Attach failure dialog with EXTRA_INTENT and requiredStorageBytes here. + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setPendingIntentBackgroundActivityStartMode( + MODE_BACKGROUND_ACTIVITY_START_DENIED); + try { + unarchiveIntentSender.sendIntent(mContext, 0, fillIn, /* onFinished= */ null, + /* handler= */ null, /* requiredPermission= */ null, + options.toBundle()); + } catch (SendIntentException e) { + Slog.e(TAG, TextUtils.formatSimple("Failed to send unarchive intent"), e); + } + } + private static int getSessionCount(SparseArray<PackageInstallerSession> sessions, int installerUid) { int count = 0; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 6f45d2befc6b..f992bd83a8de 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -4689,10 +4689,12 @@ class PackageManagerShellCommand extends ShellCommand { final int translatedUserId = translateUserId(userId, UserHandle.USER_SYSTEM, "runArchive"); + final LocalIntentReceiver receiver = new LocalIntentReceiver(); try { mInterface.getPackageInstaller().requestUnarchive(packageName, - /* callerPackageName= */ "", new UserHandle(translatedUserId)); + /* callerPackageName= */ "", receiver.getIntentSender(), + new UserHandle(translatedUserId)); } catch (Exception e) { pw.println("Failure [" + e.getMessage() + "]"); return 1; diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 72090f24a2ed..b50d0a07aa3a 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -779,6 +779,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal return readUserState(userId).isInstalled(); } + boolean isArchived(int userId) { + return PackageArchiver.isArchived(readUserState(userId)); + } + int getInstallReason(int userId) { return readUserState(userId).getInstallReason(); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 4e14c908b01b..85563172cf05 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1592,6 +1592,19 @@ public class UserManagerService extends IUserManager.Stub { */ private void showConfirmCredentialToDisableQuietMode( @UserIdInt int userId, @Nullable IntentSender target) { + if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) { + // TODO (b/308121702) It may be brittle to rely on user states to check profile state + int state; + synchronized (mUserStates) { + state = mUserStates.get(userId, UserState.STATE_NONE); + } + if (state != UserState.STATE_NONE) { + Slog.i(LOG_TAG, + "showConfirmCredentialToDisableQuietMode() called too early, user " + userId + + " is still alive."); + return; + } + } // otherwise, we show a profile challenge to trigger decryption of the user final KeyguardManager km = (KeyguardManager) mContext.getSystemService( Context.KEYGUARD_SERVICE); diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index ee46ce13ee73..b3672ecb194c 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -16,6 +16,8 @@ package com.android.server.webkit; +import static android.webkit.Flags.updateServiceV2; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -51,7 +53,7 @@ public class WebViewUpdateService extends SystemService { private static final String TAG = "WebViewUpdateService"; private BroadcastReceiver mWebViewUpdatedReceiver; - private WebViewUpdateServiceImpl mImpl; + private WebViewUpdateServiceInterface mImpl; static final int PACKAGE_CHANGED = 0; static final int PACKAGE_ADDED = 1; @@ -60,7 +62,11 @@ public class WebViewUpdateService extends SystemService { public WebViewUpdateService(Context context) { super(context); - mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance()); + if (updateServiceV2()) { + mImpl = new WebViewUpdateServiceImpl2(context, SystemImpl.getInstance()); + } else { + mImpl = new WebViewUpdateServiceImpl(context, SystemImpl.getInstance()); + } } @Override diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java index 43d62aaa120a..cfdef1471f83 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java @@ -63,7 +63,7 @@ import java.util.List; * * @hide */ -class WebViewUpdateServiceImpl { +class WebViewUpdateServiceImpl implements WebViewUpdateServiceInterface { private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName(); private static class WebViewPackageMissingException extends Exception { @@ -112,7 +112,8 @@ class WebViewUpdateServiceImpl { mSystemInterface = systemInterface; } - void packageStateChanged(String packageName, int changedState, int userId) { + @Override + public void packageStateChanged(String packageName, int changedState, int userId) { // We don't early out here in different cases where we could potentially early-out (e.g. if // we receive PACKAGE_CHANGED for another user than the system user) since that would // complicate this logic further and open up for more edge cases. @@ -163,7 +164,8 @@ class WebViewUpdateServiceImpl { } } - void prepareWebViewInSystemServer() { + @Override + public void prepareWebViewInSystemServer() { mSystemInterface.notifyZygote(isMultiProcessEnabled()); try { synchronized (mLock) { @@ -210,7 +212,8 @@ class WebViewUpdateServiceImpl { mSystemInterface.ensureZygoteStarted(); } - void handleNewUser(int userId) { + @Override + public void handleNewUser(int userId) { // The system user is always started at boot, and by that point we have already run one // round of the package-changing logic (through prepareWebViewInSystemServer()), so early // out here. @@ -218,7 +221,8 @@ class WebViewUpdateServiceImpl { handleUserChange(); } - void handleUserRemoved(int userId) { + @Override + public void handleUserRemoved(int userId) { handleUserChange(); } @@ -232,14 +236,16 @@ class WebViewUpdateServiceImpl { updateCurrentWebViewPackage(null); } - void notifyRelroCreationCompleted() { + @Override + public void notifyRelroCreationCompleted() { synchronized (mLock) { mNumRelroCreationsFinished++; checkIfRelrosDoneLocked(); } } - WebViewProviderResponse waitForAndGetProvider() { + @Override + public WebViewProviderResponse waitForAndGetProvider() { PackageInfo webViewPackage = null; final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; boolean webViewReady = false; @@ -284,7 +290,8 @@ class WebViewUpdateServiceImpl { * replacing that provider it will not be in use directly, but will be used when the relros * or the replacement are done). */ - String changeProviderAndSetting(String newProviderName) { + @Override + public String changeProviderAndSetting(String newProviderName) { PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName); if (newPackage == null) return ""; return newPackage.packageName; @@ -367,7 +374,8 @@ class WebViewUpdateServiceImpl { /** * Fetch only the currently valid WebView packages. **/ - WebViewProviderInfo[] getValidWebViewPackages() { + @Override + public WebViewProviderInfo[] getValidWebViewPackages() { ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length]; @@ -464,11 +472,13 @@ class WebViewUpdateServiceImpl { return true; } - WebViewProviderInfo[] getWebViewPackages() { + @Override + public WebViewProviderInfo[] getWebViewPackages() { return mSystemInterface.getWebViewPackages(); } - PackageInfo getCurrentWebViewPackage() { + @Override + public PackageInfo getCurrentWebViewPackage() { synchronized (mLock) { return mCurrentWebViewPackage; } @@ -620,7 +630,8 @@ class WebViewUpdateServiceImpl { return null; } - boolean isMultiProcessEnabled() { + @Override + public boolean isMultiProcessEnabled() { int settingValue = mSystemInterface.getMultiProcessSetting(mContext); if (mSystemInterface.isMultiProcessDefaultEnabled()) { // Multiprocess should be enabled unless the user has turned it off manually. @@ -631,7 +642,8 @@ class WebViewUpdateServiceImpl { } } - void enableMultiProcess(boolean enable) { + @Override + public void enableMultiProcess(boolean enable) { PackageInfo current = getCurrentWebViewPackage(); mSystemInterface.setMultiProcessSetting(mContext, enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); @@ -644,7 +656,8 @@ class WebViewUpdateServiceImpl { /** * Dump the state of this Service. */ - void dumpState(PrintWriter pw) { + @Override + public void dumpState(PrintWriter pw) { pw.println("Current WebView Update Service state"); pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); synchronized (mLock) { diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java new file mode 100644 index 000000000000..e618c7e2a80c --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java @@ -0,0 +1,747 @@ +/* + * Copyright (C) 2016 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.webkit; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.AsyncTask; +import android.os.Trace; +import android.os.UserHandle; +import android.util.Slog; +import android.webkit.UserPackage; +import android.webkit.WebViewFactory; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewProviderResponse; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of the WebViewUpdateService. + * This class doesn't depend on the android system like the actual Service does and can be used + * directly by tests (as long as they implement a SystemInterface). + * + * This class keeps track of and prepares the current WebView implementation, and needs to keep + * track of a couple of different things such as what package is used as WebView implementation. + * + * The package-visible methods in this class are accessed from WebViewUpdateService either on the UI + * thread or on one of multiple Binder threads. The WebView preparation code shares state between + * threads meaning that code that chooses a new WebView implementation or checks which + * implementation is being used needs to hold a lock. + * + * The WebViewUpdateService can be accessed in a couple of different ways. + * 1. It is started from the SystemServer at boot - at that point we just initiate some state such + * as the WebView preparation class. + * 2. The SystemServer calls WebViewUpdateService.prepareWebViewInSystemServer. This happens at boot + * and the WebViewUpdateService should not have been accessed before this call. In this call we + * choose WebView implementation for the first time. + * 3. The update service listens for Intents related to package installs and removals. These intents + * are received and processed on the UI thread. Each intent can result in changing WebView + * implementation. + * 4. The update service can be reached through Binder calls which are handled on specific binder + * threads. These calls can be made from any process. Generally they are used for changing WebView + * implementation (from Settings), getting information about the current WebView implementation (for + * loading WebView into an app process), or notifying the service about Relro creation being + * completed. + * + * @hide + */ +class WebViewUpdateServiceImpl2 implements WebViewUpdateServiceInterface { + private static final String TAG = WebViewUpdateServiceImpl2.class.getSimpleName(); + + private static class WebViewPackageMissingException extends Exception { + WebViewPackageMissingException(String message) { + super(message); + } + + WebViewPackageMissingException(Exception e) { + super(e); + } + } + + private static final int WAIT_TIMEOUT_MS = 1000; // KEY_DISPATCHING_TIMEOUT is 5000. + private static final long NS_PER_MS = 1000000; + + private static final int VALIDITY_OK = 0; + private static final int VALIDITY_INCORRECT_SDK_VERSION = 1; + private static final int VALIDITY_INCORRECT_VERSION_CODE = 2; + private static final int VALIDITY_INCORRECT_SIGNATURE = 3; + private static final int VALIDITY_NO_LIBRARY_FLAG = 4; + + private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE; + private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE; + + private final SystemInterface mSystemInterface; + private final Context mContext; + + private long mMinimumVersionCode = -1; + + // Keeps track of the number of running relro creations + private int mNumRelroCreationsStarted = 0; + private int mNumRelroCreationsFinished = 0; + // Implies that we need to rerun relro creation because we are using an out-of-date package + private boolean mWebViewPackageDirty = false; + private boolean mAnyWebViewInstalled = false; + + private static final int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; + + // The WebView package currently in use (or the one we are preparing). + private PackageInfo mCurrentWebViewPackage = null; + + private final Object mLock = new Object(); + + WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) { + mContext = context; + mSystemInterface = systemInterface; + } + + @Override + public void packageStateChanged(String packageName, int changedState, int userId) { + // We don't early out here in different cases where we could potentially early-out (e.g. if + // we receive PACKAGE_CHANGED for another user than the system user) since that would + // complicate this logic further and open up for more edge cases. + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + String webviewPackage = provider.packageName; + + if (webviewPackage.equals(packageName)) { + boolean updateWebView = false; + boolean removedOrChangedOldPackage = false; + String oldProviderName = null; + PackageInfo newPackage = null; + synchronized (mLock) { + try { + newPackage = findPreferredWebViewPackage(); + if (mCurrentWebViewPackage != null) { + oldProviderName = mCurrentWebViewPackage.packageName; + } + // Only trigger update actions if the updated package is the one + // that will be used, or the one that was in use before the + // update, or if we haven't seen a valid WebView package before. + updateWebView = + provider.packageName.equals(newPackage.packageName) + || provider.packageName.equals(oldProviderName) + || mCurrentWebViewPackage == null; + // We removed the old package if we received an intent to remove + // or replace the old package. + removedOrChangedOldPackage = + provider.packageName.equals(oldProviderName); + if (updateWebView) { + onWebViewProviderChanged(newPackage); + } + } catch (WebViewPackageMissingException e) { + mCurrentWebViewPackage = null; + Slog.e(TAG, "Could not find valid WebView package to create relro with " + + e); + } + } + if (updateWebView && !removedOrChangedOldPackage + && oldProviderName != null) { + // If the provider change is the result of adding or replacing a + // package that was not the previous provider then we must kill + // packages dependent on the old package ourselves. The framework + // only kills dependents of packages that are being removed. + mSystemInterface.killPackageDependents(oldProviderName); + } + return; + } + } + } + + @Override + public void prepareWebViewInSystemServer() { + mSystemInterface.notifyZygote(isMultiProcessEnabled()); + try { + synchronized (mLock) { + mCurrentWebViewPackage = findPreferredWebViewPackage(); + String userSetting = mSystemInterface.getUserChosenWebViewProvider(mContext); + if (userSetting != null + && !userSetting.equals(mCurrentWebViewPackage.packageName)) { + // Don't persist the user-chosen setting across boots if the package being + // chosen is not used (could be disabled or uninstalled) so that the user won't + // be surprised by the device switching to using a certain webview package, + // that was uninstalled/disabled a long time ago, if it is installed/enabled + // again. + mSystemInterface.updateUserSetting(mContext, + mCurrentWebViewPackage.packageName); + } + onWebViewProviderChanged(mCurrentWebViewPackage); + } + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Slog.e(TAG, "error preparing webview provider from system server", t); + } + + if (getCurrentWebViewPackage() == null) { + // We didn't find a valid WebView implementation. Try explicitly re-enabling the + // fallback package for all users in case it was disabled, even if we already did the + // one-time migration before. If this actually changes the state, we will see the + // PackageManager broadcast shortly and try again. + WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages(); + WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); + if (fallbackProvider != null) { + Slog.w(TAG, "No valid provider, trying to enable " + fallbackProvider.packageName); + mSystemInterface.enablePackageForAllUsers(mContext, fallbackProvider.packageName, + true); + } else { + Slog.e(TAG, "No valid provider and no fallback available."); + } + } + } + + private void startZygoteWhenReady() { + // Wait on a background thread for RELRO creation to be done. We ignore the return value + // because even if RELRO creation failed we still want to start the zygote. + waitForAndGetProvider(); + mSystemInterface.ensureZygoteStarted(); + } + + @Override + public void handleNewUser(int userId) { + // The system user is always started at boot, and by that point we have already run one + // round of the package-changing logic (through prepareWebViewInSystemServer()), so early + // out here. + if (userId == UserHandle.USER_SYSTEM) return; + handleUserChange(); + } + + @Override + public void handleUserRemoved(int userId) { + handleUserChange(); + } + + /** + * Called when a user was added or removed to ensure WebView preparation is triggered. + * This has to be done since the WebView package we use depends on the enabled-state + * of packages for all users (so adding or removing a user might cause us to change package). + */ + private void handleUserChange() { + // Potentially trigger package-changing logic. + updateCurrentWebViewPackage(null); + } + + @Override + public void notifyRelroCreationCompleted() { + synchronized (mLock) { + mNumRelroCreationsFinished++; + checkIfRelrosDoneLocked(); + } + } + + @Override + public WebViewProviderResponse waitForAndGetProvider() { + PackageInfo webViewPackage = null; + final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS; + boolean webViewReady = false; + int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS; + synchronized (mLock) { + webViewReady = webViewIsReadyLocked(); + while (!webViewReady) { + final long timeNowMs = System.nanoTime() / NS_PER_MS; + if (timeNowMs >= timeoutTimeMs) break; + try { + mLock.wait(timeoutTimeMs - timeNowMs); + } catch (InterruptedException e) { + // ignore + } + webViewReady = webViewIsReadyLocked(); + } + // Make sure we return the provider that was used to create the relro file + webViewPackage = mCurrentWebViewPackage; + if (webViewReady) { + // success + } else if (!mAnyWebViewInstalled) { + webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; + } else { + // Either the current relro creation isn't done yet, or the new relro creatioin + // hasn't kicked off yet (the last relro creation used an out-of-date WebView). + webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO; + String timeoutError = "Timed out waiting for relro creation, relros started " + + mNumRelroCreationsStarted + + " relros finished " + mNumRelroCreationsFinished + + " package dirty? " + mWebViewPackageDirty; + Slog.e(TAG, timeoutError); + Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, timeoutError); + } + } + if (!webViewReady) Slog.w(TAG, "creating relro file timed out"); + return new WebViewProviderResponse(webViewPackage, webViewStatus); + } + + /** + * Change WebView provider and provider setting and kill packages using the old provider. + * Return the new provider (in case we are in the middle of creating relro files, or + * replacing that provider it will not be in use directly, but will be used when the relros + * or the replacement are done). + */ + @Override + public String changeProviderAndSetting(String newProviderName) { + PackageInfo newPackage = updateCurrentWebViewPackage(newProviderName); + if (newPackage == null) return ""; + return newPackage.packageName; + } + + /** + * Update the current WebView package. + * @param newProviderName the package to switch to, null if no package has been explicitly + * chosen. + */ + private PackageInfo updateCurrentWebViewPackage(@Nullable String newProviderName) { + PackageInfo oldPackage = null; + PackageInfo newPackage = null; + boolean providerChanged = false; + synchronized (mLock) { + oldPackage = mCurrentWebViewPackage; + + if (newProviderName != null) { + mSystemInterface.updateUserSetting(mContext, newProviderName); + } + + try { + newPackage = findPreferredWebViewPackage(); + providerChanged = (oldPackage == null) + || !newPackage.packageName.equals(oldPackage.packageName); + } catch (WebViewPackageMissingException e) { + // If updated the Setting but don't have an installed WebView package, the + // Setting will be used when a package is available. + mCurrentWebViewPackage = null; + Slog.e(TAG, "Couldn't find WebView package to use " + e); + return null; + } + // Perform the provider change if we chose a new provider + if (providerChanged) { + onWebViewProviderChanged(newPackage); + } + } + // Kill apps using the old provider only if we changed provider + if (providerChanged && oldPackage != null) { + mSystemInterface.killPackageDependents(oldPackage.packageName); + } + // Return the new provider, this is not necessarily the one we were asked to switch to, + // but the persistent setting will now be pointing to the provider we were asked to + // switch to anyway. + return newPackage; + } + + /** + * This is called when we change WebView provider, either when the current provider is + * updated or a new provider is chosen / takes precedence. + */ + private void onWebViewProviderChanged(PackageInfo newPackage) { + synchronized (mLock) { + mAnyWebViewInstalled = true; + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + mCurrentWebViewPackage = newPackage; + + // The relro creations might 'finish' (not start at all) before + // WebViewFactory.onWebViewProviderChanged which means we might not know the + // number of started creations before they finish. + mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; + mNumRelroCreationsFinished = 0; + mNumRelroCreationsStarted = + mSystemInterface.onWebViewProviderChanged(newPackage); + // If the relro creations finish before we know the number of started creations + // we will have to do any cleanup/notifying here. + checkIfRelrosDoneLocked(); + } else { + mWebViewPackageDirty = true; + } + } + + // Once we've notified the system that the provider has changed and started RELRO creation, + // try to restart the zygote so that it will be ready when apps use it. + if (isMultiProcessEnabled()) { + AsyncTask.THREAD_POOL_EXECUTOR.execute(this::startZygoteWhenReady); + } + } + + /** + * Fetch only the currently valid WebView packages. + **/ + @Override + public WebViewProviderInfo[] getValidWebViewPackages() { + ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos(); + WebViewProviderInfo[] providers = + new WebViewProviderInfo[providersAndPackageInfos.length]; + for (int n = 0; n < providersAndPackageInfos.length; n++) { + providers[n] = providersAndPackageInfos[n].provider; + } + return providers; + } + + private static class ProviderAndPackageInfo { + public final WebViewProviderInfo provider; + public final PackageInfo packageInfo; + + ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) { + this.provider = provider; + this.packageInfo = packageInfo; + } + } + + private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() { + WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); + List<ProviderAndPackageInfo> providers = new ArrayList<>(); + for (int n = 0; n < allProviders.length; n++) { + try { + PackageInfo packageInfo = + mSystemInterface.getPackageInfoForProvider(allProviders[n]); + if (validityResult(allProviders[n], packageInfo) == VALIDITY_OK) { + providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo)); + } + } catch (NameNotFoundException e) { + // Don't add non-existent packages + } + } + return providers.toArray(new ProviderAndPackageInfo[providers.size()]); + } + + /** + * Returns either the package info of the WebView provider determined in the following way: + * If the user has chosen a provider then use that if it is valid, + * otherwise use the first package in the webview priority list that is valid. + * + */ + private PackageInfo findPreferredWebViewPackage() throws WebViewPackageMissingException { + ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos(); + + String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext); + + // If the user has chosen provider, use that (if it's installed and enabled for all + // users). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.packageName.equals(userChosenProvider)) { + // userPackages can contain null objects. + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, + providerAndPackage.provider); + if (isInstalledAndEnabledForAllUsers(userPackages)) { + return providerAndPackage.packageInfo; + } + } + } + + // User did not choose, or the choice failed; use the most stable provider that is + // installed and enabled for all users, and available by default (not through + // user choice). + for (ProviderAndPackageInfo providerAndPackage : providers) { + if (providerAndPackage.provider.availableByDefault) { + // userPackages can contain null objects. + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, + providerAndPackage.provider); + if (isInstalledAndEnabledForAllUsers(userPackages)) { + return providerAndPackage.packageInfo; + } + } + } + + // This should never happen during normal operation (only with modified system images). + mAnyWebViewInstalled = false; + throw new WebViewPackageMissingException("Could not find a loadable WebView package"); + } + + /** + * Return true iff {@param packageInfos} point to only installed and enabled packages. + * The given packages {@param packageInfos} should all be pointing to the same package, but each + * PackageInfo representing a different user's package. + */ + private static boolean isInstalledAndEnabledForAllUsers( + List<UserPackage> userPackages) { + for (UserPackage userPackage : userPackages) { + if (!userPackage.isInstalledPackage() || !userPackage.isEnabledPackage()) { + return false; + } + } + return true; + } + + @Override + public WebViewProviderInfo[] getWebViewPackages() { + return mSystemInterface.getWebViewPackages(); + } + + @Override + public PackageInfo getCurrentWebViewPackage() { + synchronized (mLock) { + return mCurrentWebViewPackage; + } + } + + /** + * Returns whether WebView is ready and is not going to go through its preparation phase + * again directly. + */ + private boolean webViewIsReadyLocked() { + return !mWebViewPackageDirty + && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) + // The current package might be replaced though we haven't received an intent + // declaring this yet, the following flag makes anyone loading WebView to wait in + // this case. + && mAnyWebViewInstalled; + } + + private void checkIfRelrosDoneLocked() { + if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { + if (mWebViewPackageDirty) { + mWebViewPackageDirty = false; + // If we have changed provider since we started the relro creation we need to + // redo the whole process using the new package instead. + try { + PackageInfo newPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(newPackage); + } catch (WebViewPackageMissingException e) { + mCurrentWebViewPackage = null; + // If we can't find any valid WebView package we are now in a state where + // mAnyWebViewInstalled is false, so loading WebView will be blocked and we + // should simply wait until we receive an intent declaring a new package was + // installed. + } + } else { + mLock.notifyAll(); + } + } + } + + private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) { + // Ensure the provider targets this framework release (or a later one). + if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) { + return VALIDITY_INCORRECT_SDK_VERSION; + } + if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode()) + && !mSystemInterface.systemIsDebuggable()) { + // Webview providers may be downgraded arbitrarily low, prevent that by enforcing + // minimum version code. This check is only enforced for user builds. + return VALIDITY_INCORRECT_VERSION_CODE; + } + if (!providerHasValidSignature(configInfo, packageInfo, mSystemInterface)) { + return VALIDITY_INCORRECT_SIGNATURE; + } + if (WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) == null) { + return VALIDITY_NO_LIBRARY_FLAG; + } + return VALIDITY_OK; + } + + /** + * Both versionCodes should be from a WebView provider package implemented by Chromium. + * VersionCodes from other kinds of packages won't make any sense in this method. + * + * An introduction to Chromium versionCode scheme: + * "BBBBPPPXX" + * BBBB: 4 digit branch number. It monotonically increases over time. + * PPP: patch number in the branch. It is padded with zeroes to the left. These three digits + * may change their meaning in the future. + * XX: Digits to differentiate different APK builds of the same source version. + * + * This method takes the "BBBB" of versionCodes and compare them. + * + * https://www.chromium.org/developers/version-numbers describes general Chromium versioning; + * https://source.chromium.org/chromium/chromium/src/+/master:build/util/android_chrome_version.py + * is the canonical source for how Chromium versionCodes are calculated. + * + * @return true if versionCode1 is higher than or equal to versionCode2. + */ + private static boolean versionCodeGE(long versionCode1, long versionCode2) { + long v1 = versionCode1 / 100000; + long v2 = versionCode2 / 100000; + + return v1 >= v2; + } + + /** + * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode + * of all available-by-default WebView provider packages. If there is no such WebView provider + * package on the system, then return -1, which means all positive versionCode WebView packages + * are accepted. + * + * Note that this is a private method that handles a variable (mMinimumVersionCode) which is + * shared between threads. Furthermore, this method does not hold mLock meaning that we must + * take extra care to ensure this method is thread-safe. + */ + private long getMinimumVersionCode() { + if (mMinimumVersionCode > 0) { + return mMinimumVersionCode; + } + + long minimumVersionCode = -1; + for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) { + if (provider.availableByDefault) { + try { + long versionCode = + mSystemInterface.getFactoryPackageVersion(provider.packageName); + if (minimumVersionCode < 0 || versionCode < minimumVersionCode) { + minimumVersionCode = versionCode; + } + } catch (NameNotFoundException e) { + // Safe to ignore. + } + } + } + + mMinimumVersionCode = minimumVersionCode; + return mMinimumVersionCode; + } + + private static boolean providerHasValidSignature(WebViewProviderInfo provider, + PackageInfo packageInfo, SystemInterface systemInterface) { + // Skip checking signatures on debuggable builds, for development purposes. + if (systemInterface.systemIsDebuggable()) return true; + + // Allow system apps to be valid providers regardless of signature. + if (packageInfo.applicationInfo.isSystemApp()) return true; + + // We don't support packages with multiple signatures. + if (packageInfo.signatures.length != 1) return false; + + // If any of the declared signatures match the package signature, it's valid. + for (Signature signature : provider.signatures) { + if (signature.equals(packageInfo.signatures[0])) return true; + } + + return false; + } + + /** + * Returns the only fallback provider in the set of given packages, or null if there is none. + */ + private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) { + for (WebViewProviderInfo provider : webviewPackages) { + if (provider.isFallback) { + return provider; + } + } + return null; + } + + @Override + public boolean isMultiProcessEnabled() { + int settingValue = mSystemInterface.getMultiProcessSetting(mContext); + if (mSystemInterface.isMultiProcessDefaultEnabled()) { + // Multiprocess should be enabled unless the user has turned it off manually. + return settingValue > MULTIPROCESS_SETTING_OFF_VALUE; + } else { + // Multiprocess should not be enabled, unless the user has turned it on manually. + return settingValue >= MULTIPROCESS_SETTING_ON_VALUE; + } + } + + @Override + public void enableMultiProcess(boolean enable) { + PackageInfo current = getCurrentWebViewPackage(); + mSystemInterface.setMultiProcessSetting(mContext, + enable ? MULTIPROCESS_SETTING_ON_VALUE : MULTIPROCESS_SETTING_OFF_VALUE); + mSystemInterface.notifyZygote(enable); + if (current != null) { + mSystemInterface.killPackageDependents(current.packageName); + } + } + + /** + * Dump the state of this Service. + */ + @Override + public void dumpState(PrintWriter pw) { + pw.println("Current WebView Update Service state"); + pw.println(String.format(" Multiprocess enabled: %b", isMultiProcessEnabled())); + synchronized (mLock) { + if (mCurrentWebViewPackage == null) { + pw.println(" Current WebView package is null"); + } else { + pw.println(String.format(" Current WebView package (name, version): (%s, %s)", + mCurrentWebViewPackage.packageName, + mCurrentWebViewPackage.versionName)); + } + pw.println(String.format(" Minimum targetSdkVersion: %d", + UserPackage.MINIMUM_SUPPORTED_SDK)); + pw.println(String.format(" Minimum WebView version code: %d", + mMinimumVersionCode)); + pw.println(String.format(" Number of relros started: %d", + mNumRelroCreationsStarted)); + pw.println(String.format(" Number of relros finished: %d", + mNumRelroCreationsFinished)); + pw.println(String.format(" WebView package dirty: %b", mWebViewPackageDirty)); + pw.println(String.format(" Any WebView package installed: %b", + mAnyWebViewInstalled)); + + try { + PackageInfo preferredWebViewPackage = findPreferredWebViewPackage(); + pw.println(String.format( + " Preferred WebView package (name, version): (%s, %s)", + preferredWebViewPackage.packageName, + preferredWebViewPackage.versionName)); + } catch (WebViewPackageMissingException e) { + pw.println(String.format(" Preferred WebView package: none")); + } + + dumpAllPackageInformationLocked(pw); + } + } + + private void dumpAllPackageInformationLocked(PrintWriter pw) { + WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages(); + pw.println(" WebView packages:"); + for (WebViewProviderInfo provider : allProviders) { + List<UserPackage> userPackages = + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider); + PackageInfo systemUserPackageInfo = + userPackages.get(UserHandle.USER_SYSTEM).getPackageInfo(); + if (systemUserPackageInfo == null) { + pw.println(String.format(" %s is NOT installed.", provider.packageName)); + continue; + } + + int validity = validityResult(provider, systemUserPackageInfo); + String packageDetails = String.format( + "versionName: %s, versionCode: %d, targetSdkVersion: %d", + systemUserPackageInfo.versionName, + systemUserPackageInfo.getLongVersionCode(), + systemUserPackageInfo.applicationInfo.targetSdkVersion); + if (validity == VALIDITY_OK) { + boolean installedForAllUsers = isInstalledAndEnabledForAllUsers( + mSystemInterface.getPackageInfoForProviderAllUsers(mContext, provider)); + pw.println(String.format( + " Valid package %s (%s) is %s installed/enabled for all users", + systemUserPackageInfo.packageName, + packageDetails, + installedForAllUsers ? "" : "NOT")); + } else { + pw.println(String.format(" Invalid package %s (%s), reason: %s", + systemUserPackageInfo.packageName, + packageDetails, + getInvalidityReason(validity))); + } + } + } + + private static String getInvalidityReason(int invalidityReason) { + switch (invalidityReason) { + case VALIDITY_INCORRECT_SDK_VERSION: + return "SDK version too low"; + case VALIDITY_INCORRECT_VERSION_CODE: + return "Version code too low"; + case VALIDITY_INCORRECT_SIGNATURE: + return "Incorrect signature"; + case VALIDITY_NO_LIBRARY_FLAG: + return "No WebView-library manifest flag"; + default: + return "Unexcepted validity-reason"; + } + } +} diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java new file mode 100644 index 000000000000..a9c3dc45842f --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceInterface.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.webkit; + +import android.content.pm.PackageInfo; +import android.webkit.WebViewProviderInfo; +import android.webkit.WebViewProviderResponse; + +import java.io.PrintWriter; + +interface WebViewUpdateServiceInterface { + void packageStateChanged(String packageName, int changedState, int userId); + + void handleNewUser(int userId); + + void handleUserRemoved(int userId); + + WebViewProviderInfo[] getWebViewPackages(); + + void prepareWebViewInSystemServer(); + + void notifyRelroCreationCompleted(); + + WebViewProviderResponse waitForAndGetProvider(); + + String changeProviderAndSetting(String newProviderName); + + WebViewProviderInfo[] getValidWebViewPackages(); + + PackageInfo getCurrentWebViewPackage(); + + boolean isMultiProcessEnabled(); + + void enableMultiProcess(boolean enable); + + void dumpState(PrintWriter pw); +} diff --git a/services/core/java/com/android/server/webkit/flags.aconfig b/services/core/java/com/android/server/webkit/flags.aconfig new file mode 100644 index 000000000000..1411acc4ab84 --- /dev/null +++ b/services/core/java/com/android/server/webkit/flags.aconfig @@ -0,0 +1,9 @@ +package: "android.webkit" + +flag { + name: "update_service_v2" + namespace: "webview" + description: "Using a new version of the WebView update service" + bug: "308907090" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index faccca8981c8..315e7d85df24 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -55,6 +55,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller; +import static com.android.window.flags.Flags.allowDisableActivityRecordInputSink; import android.Manifest; import android.annotation.ColorInt; @@ -1688,4 +1689,20 @@ class ActivityClientController extends IActivityClientController.Stub { return r.mRequestedLaunchingTaskFragmentToken == taskFragmentToken; } } + + @Override + public void setActivityRecordInputSinkEnabled(IBinder activityToken, boolean enabled) { + if (!allowDisableActivityRecordInputSink()) { + return; + } + + mService.mAmInternal.enforceCallingPermission( + Manifest.permission.INTERNAL_SYSTEM_WINDOW, "setActivityRecordInputSinkEnabled"); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.forTokenLocked(activityToken); + if (r != null) { + r.mActivityRecordInputSinkEnabled = enabled; + } + } + } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 081759d563a5..d90d4ff6bbd6 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -970,6 +970,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mWaitForEnteringPinnedMode; final ActivityRecordInputSink mActivityRecordInputSink; + // System activities with INTERNAL_SYSTEM_WINDOW can disable ActivityRecordInputSink. + boolean mActivityRecordInputSinkEnabled = true; // Activities with this uid are allowed to not create an input sink while being in the same // task and directly above this ActivityRecord. This field is updated whenever a new activity diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index be7d9b63f779..c61d86355c8b 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -86,7 +86,8 @@ class ActivityRecordInputSink { final boolean allowPassthrough = activityBelowInTask != null && ( activityBelowInTask.mAllowedTouchUid == mActivityRecord.getUid() || activityBelowInTask.isUid(mActivityRecord.getUid())); - if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition()) { + if (allowPassthrough || !mIsCompatEnabled || mActivityRecord.isInTransition() + || !mActivityRecord.mActivityRecordInputSinkEnabled) { mInputWindowHandleWrapper.setInputConfigMasked(InputConfig.NOT_TOUCHABLE, InputConfig.NOT_TOUCHABLE); } else { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index a9f11e488881..34c7eee45f34 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -281,6 +281,7 @@ import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.uri.NeededUriGrants; import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wallpaper.WallpaperManagerInternal; +import com.android.wm.shell.Flags; import java.io.BufferedReader; import java.io.File; @@ -316,8 +317,6 @@ import java.util.Set; public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private static final String GRAMMATICAL_GENDER_PROPERTY = "persist.sys.grammatical_gender"; private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_ATM; - private static final String ENABLE_PIP2_IMPLEMENTATION = - "persist.wm.debug.enable_pip2_implementation"; static final String TAG_ROOT_TASK = TAG + POSTFIX_ROOT_TASK; static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; @@ -7262,6 +7261,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } static boolean isPip2ExperimentEnabled() { - return SystemProperties.getBoolean(ENABLE_PIP2_IMPLEMENTATION, false); + return Flags.enablePip2Implementation(); } } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 287aaf9da42a..8cc197c2f3d0 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -46,7 +46,6 @@ import android.app.BackgroundStartPrivileges; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.compat.annotation.Overridable; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; @@ -88,7 +87,6 @@ public class BackgroundActivityStartController { /** If enabled the creator will not allow BAL on its behalf by default. */ @ChangeId @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) - @Overridable private static final long DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_CREATOR = 296478951; public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED = diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index d0d7f493b969..e6bbd52807bd 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -101,10 +101,8 @@ class InsetsPolicy { mPolicy = displayContent.getDisplayPolicy(); final Resources r = mPolicy.getContext().getResources(); mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard); - mTransientControlTarget = new ControlTarget( - stateController, displayContent.mWmService.mH, "TransientControlTarget"); - mPermanentControlTarget = new ControlTarget( - stateController, displayContent.mWmService.mH, "PermanentControlTarget"); + mTransientControlTarget = new ControlTarget(displayContent, "TransientControlTarget"); + mPermanentControlTarget = new ControlTarget(displayContent, "PermanentControlTarget"); } /** Updates the target which can control system bars. */ @@ -699,24 +697,35 @@ class InsetsPolicy { } } - private static class ControlTarget implements InsetsControlTarget { + private static class ControlTarget implements InsetsControlTarget, Runnable { + private final Handler mHandler; + private final Object mGlobalLock; private final InsetsState mState = new InsetsState(); - private final InsetsController mInsetsController; private final InsetsStateController mStateController; + private final InsetsController mInsetsController; private final String mName; - ControlTarget(InsetsStateController stateController, Handler handler, String name) { - mStateController = stateController; - mInsetsController = new InsetsController(new Host(handler, name)); + ControlTarget(DisplayContent displayContent, String name) { + mHandler = displayContent.mWmService.mH; + mGlobalLock = displayContent.mWmService.mGlobalLock; + mStateController = displayContent.getInsetsStateController(); + mInsetsController = new InsetsController(new Host(mHandler, name)); mName = name; } @Override public void notifyInsetsControlChanged() { - mState.set(mStateController.getRawInsetsState(), true /* copySources */); - mInsetsController.onStateChanged(mState); - mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); + mHandler.post(this); + } + + @Override + public void run() { + synchronized (mGlobalLock) { + mState.set(mStateController.getRawInsetsState(), true /* copySources */); + mInsetsController.onStateChanged(mState); + mInsetsController.onControlsChanged(mStateController.getControlsForDispatch(this)); + } } @Override diff --git a/services/core/java/com/android/server/wm/WindowManagerFlags.java b/services/core/java/com/android/server/wm/WindowManagerFlags.java index b3a36501b7cf..89a70e502415 100644 --- a/services/core/java/com/android/server/wm/WindowManagerFlags.java +++ b/services/core/java/com/android/server/wm/WindowManagerFlags.java @@ -43,8 +43,6 @@ class WindowManagerFlags { /* Start Available Flags */ - final boolean mSyncWindowConfigUpdateFlag = Flags.syncWindowConfigUpdateFlag(); - final boolean mWindowStateResizeItemFlag = Flags.windowStateResizeItemFlag(); final boolean mWallpaperOffsetAsync = Flags.wallpaperOffsetAsync(); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java index a9f5b14fc48d..18a2accf071d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverTest.java @@ -37,6 +37,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.ComponentName; @@ -171,6 +172,12 @@ public class PackageArchiverTest { when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); when(mActivityManager.getLauncherLargeIconDensity()).thenReturn(100); + when(mContext.checkCallingOrSelfPermission( + eq(Manifest.permission.REQUEST_INSTALL_PACKAGES))).thenReturn( + PackageManager.PERMISSION_DENIED); + when(mContext.checkCallingOrSelfPermission( + eq(Manifest.permission.REQUEST_DELETE_PACKAGES))).thenReturn( + PackageManager.PERMISSION_DENIED); when(mAppOpsManager.checkOp( eq(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), @@ -386,7 +393,7 @@ public class PackageArchiverTest { Exception e = assertThrows( SecurityException.class, () -> mArchiveManager.requestUnarchive(PACKAGE, "different", - UserHandle.CURRENT)); + mIntentSender, UserHandle.CURRENT)); assertThat(e).hasMessageThat().isEqualTo( String.format( "The UID %s of callerPackageName set by the caller doesn't match the " @@ -404,7 +411,7 @@ public class PackageArchiverTest { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, - UserHandle.CURRENT)); + mIntentSender, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s not found.", PACKAGE)); @@ -416,7 +423,7 @@ public class PackageArchiverTest { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, - UserHandle.CURRENT)); + mIntentSender, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s is not currently archived.", PACKAGE)); @@ -428,7 +435,7 @@ public class PackageArchiverTest { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, - UserHandle.CURRENT)); + mIntentSender, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("Package %s is not currently archived.", PACKAGE)); @@ -452,7 +459,7 @@ public class PackageArchiverTest { Exception e = assertThrows( ParcelableException.class, () -> mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, - UserHandle.CURRENT)); + mIntentSender, UserHandle.CURRENT)); assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class); assertThat(e.getCause()).hasMessageThat().isEqualTo( String.format("No installer found to unarchive app %s.", PACKAGE)); @@ -462,7 +469,8 @@ public class PackageArchiverTest { public void unarchiveApp_success() { mUserState.setArchiveState(createArchiveState()).setInstalled(false); - mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT); + mArchiveManager.requestUnarchive(PACKAGE, CALLER_PACKAGE, mIntentSender, + UserHandle.CURRENT); rule.mocks().getHandler().flush(); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 0f3daec263e0..74eb79d7554c 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -28,6 +28,8 @@ import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUT import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_STARTED_UI_SHOWING; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_ERROR_PENDING_SYSUI; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -248,6 +250,45 @@ public class AuthSessionTest { } @Test + public void testOnErrorReceivedBeforeOnDialogAnimatedIn() throws RemoteException { + final int fingerprintId = 0; + final int faceId = 1; + setupFingerprint(fingerprintId, FingerprintSensorProperties.TYPE_REAR); + setupFace(faceId, true /* confirmationAlwaysRequired */, + mock(IBiometricAuthenticator.class)); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + + for (BiometricSensor sensor : session.mPreAuthInfo.eligibleSensors) { + assertThat(sensor.getSensorState()).isEqualTo(BiometricSensor.STATE_WAITING_FOR_COOKIE); + session.onCookieReceived( + session.mPreAuthInfo.eligibleSensors.get(sensor.id).getCookie()); + } + assertThat(session.allCookiesReceived()).isTrue(); + assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED); + + final BiometricSensor faceSensor = session.mPreAuthInfo.eligibleSensors.get(faceId); + final BiometricSensor fingerprintSensor = session.mPreAuthInfo.eligibleSensors.get( + fingerprintId); + final int cookie = faceSensor.getCookie(); + session.onErrorReceived(0, cookie, BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL, 0); + + assertThat(faceSensor.getSensorState()).isEqualTo(BiometricSensor.STATE_STOPPED); + assertThat(session.getState()).isEqualTo(STATE_ERROR_PENDING_SYSUI); + + session.onDialogAnimatedIn(true); + + assertThat(session.getState()).isEqualTo(STATE_AUTH_STARTED_UI_SHOWING); + assertThat(fingerprintSensor.getSensorState()).isEqualTo( + BiometricSensor.STATE_AUTHENTICATING); + } + + @Test public void testCancelReducesAppetiteForCookies() throws Exception { setupFace(0 /* id */, false /* confirmationAlwaysRequired */, mock(IBiometricAuthenticator.class)); 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 7fb8b30dea06..a47fbceea51c 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -87,8 +87,6 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; -import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE; -import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER; import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER; @@ -739,9 +737,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { clearInvocations(mRankingHandler); when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - mTestFlagResolver.setFlagOverride(FSI_FORCE_DEMOTE, false); - mTestFlagResolver.setFlagOverride(SHOW_STICKY_HUN_FOR_DENIED_FSI, false); - var checker = mock(TestableNotificationManagerService.ComponentPermissionChecker.class); mService.permissionChecker = checker; when(checker.check(anyString(), anyInt(), anyInt(), anyBoolean())) @@ -11472,14 +11467,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class)); } - private void verifyStickyHun(Flag flag, int permissionState, boolean appRequested, + private void verifyStickyHun(int permissionState, boolean appRequested, boolean isSticky) throws Exception { when(mPermissionHelper.hasRequestedPermission(Manifest.permission.USE_FULL_SCREEN_INTENT, PKG, mUserId)).thenReturn(appRequested); - mTestFlagResolver.setFlagOverride(flag, true); - when(mPermissionManager.checkPermissionForDataDelivery( eq(Manifest.permission.USE_FULL_SCREEN_INTENT), any(), any())) .thenReturn(permissionState); @@ -11503,8 +11496,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionHardDenied_showStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, /* isSticky= */ true); } @@ -11512,16 +11504,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionSoftDenied_showStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, /* isSticky= */ true); } @Test public void testFixNotification_fsiPermissionSoftDenied_appNotRequest_noShowStickyHun() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, false, /* isSticky= */ false); } @@ -11530,39 +11520,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testFixNotification_flagEnableStickyHun_fsiPermissionGranted_showFsi() throws Exception { - verifyStickyHun(/* flag= */ SHOW_STICKY_HUN_FOR_DENIED_FSI, - /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, + verifyStickyHun(/* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, /* isSticky= */ false); } @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionHardDenied_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_HARD_DENIED, true, - /* isSticky= */ true); - } - - @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionSoftDenied_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_SOFT_DENIED, true, - /* isSticky= */ true); - } - - @Test - public void testFixNotification_flagForceStickyHun_fsiPermissionGranted_showStickyHun() - throws Exception { - - verifyStickyHun(/* flag= */ FSI_FORCE_DEMOTE, - /* permissionState= */ PermissionManager.PERMISSION_GRANTED, true, - /* isSticky= */ true); - } - - @Test public void fixNotification_withFgsFlag_butIsNotFgs() throws Exception { final ApplicationInfo applicationInfo = new ApplicationInfo(); when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java index 5147a08b5216..29848d0139b1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java @@ -171,19 +171,8 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { } @Test - public void testGetFsiState_stickyHunFlagDisabled_zero() { - final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ false, - /* hasFullScreenIntent= */ true, - /* hasFsiRequestedButDeniedFlag= */ true, - /* eventType= */ NOTIFICATION_POSTED); - assertEquals(0, fsiState); - } - - @Test public void testGetFsiState_isUpdate_zero() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ true, /* hasFsiRequestedButDeniedFlag= */ true, /* eventType= */ NOTIFICATION_UPDATED); @@ -193,7 +182,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_hasFsi_allowedEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ true, /* hasFsiRequestedButDeniedFlag= */ false, /* eventType= */ NOTIFICATION_POSTED); @@ -203,7 +191,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_fsiPermissionDenied_deniedEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ false, /* hasFsiRequestedButDeniedFlag= */ true, /* eventType= */ NOTIFICATION_POSTED); @@ -213,7 +200,6 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { @Test public void testGetFsiState_noFsi_noFsiEnum() { final int fsiState = NotificationRecordLogger.getFsiState( - /* isStickyHunFlagEnabled= */ true, /* hasFullScreenIntent= */ false, /* hasFsiRequestedButDeniedFlag= */ false, /* eventType= */ NOTIFICATION_POSTED); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index c156e376ce3e..fe21103096ca 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -5733,17 +5733,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testGetFsiState_flagDisabled_zero() { - final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission */ true, /* isFlagEnabled= */ false); - - assertEquals(0, fsiState); - } - - @Test public void testGetFsiState_appDidNotRequest_enumNotRequested() { final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission */ false, /* isFlagEnabled= */ true); + /* requestedFsiPermission */ false); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, fsiState); } @@ -5754,7 +5746,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_GRANTED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission= */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission= */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, fsiState); } @@ -5765,7 +5757,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_SOFT_DENIED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission = */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); } @@ -5776,7 +5768,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { .thenReturn(PermissionManager.PERMISSION_HARD_DENIED); final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, - /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + /* requestedFsiPermission = */ true); assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); } @@ -5785,18 +5777,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_appDidNotRequest_false() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); - - assertFalse(isUserSet); - } - - @Test - public void testIsFsiPermissionUserSet_flagDisabled_false() { - final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, - /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ false); + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET); assertFalse(isUserSet); } @@ -5805,8 +5786,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_userSet_true() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET); assertTrue(isUserSet); } @@ -5815,8 +5795,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testIsFsiPermissionUserSet_notUserSet_false() { final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, - /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET, - /* isStickyHunFlagEnabled= */ true); + /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET); assertFalse(isUserSet); } diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index 1b8d746f271f..e83f03d155aa 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -99,4 +99,7 @@ android_test { enabled: false, }, + data: [ + ":OverlayTestApp", + ], } diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 762e23c8e288..f2a1fe859634 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -119,6 +119,9 @@ </intent-filter> </activity> + <activity android:name="com.android.server.wm.ActivityRecordInputSinkTests$TestActivity" + android:exported="true"> + </activity> </application> <instrumentation diff --git a/services/tests/wmtests/AndroidTest.xml b/services/tests/wmtests/AndroidTest.xml index 2717ef901216..f8ebeaddcb7e 100644 --- a/services/tests/wmtests/AndroidTest.xml +++ b/services/tests/wmtests/AndroidTest.xml @@ -21,6 +21,7 @@ <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> <option name="test-file-name" value="WmTests.apk" /> + <option name="test-file-name" value="OverlayTestApp.apk" /> </target_preparer> <option name="test-tag" value="WmTests" /> diff --git a/services/tests/wmtests/OverlayApp/Android.bp b/services/tests/wmtests/OverlayApp/Android.bp new file mode 100644 index 000000000000..77d5b2295dc9 --- /dev/null +++ b/services/tests/wmtests/OverlayApp/Android.bp @@ -0,0 +1,19 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "OverlayTestApp", + + srcs: ["**/*.java"], + + resource_dirs: ["res"], + + certificate: "platform", + platform_apis: true, +} diff --git a/services/tests/wmtests/OverlayApp/AndroidManifest.xml b/services/tests/wmtests/OverlayApp/AndroidManifest.xml new file mode 100644 index 000000000000..5b4ef577112a --- /dev/null +++ b/services/tests/wmtests/OverlayApp/AndroidManifest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.overlay_app"> + <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" /> + + <application> + <activity android:name=".OverlayApp" + android:exported="true" + android:theme="@style/TranslucentFloatingTheme"> + </activity> + </application> +</manifest> diff --git a/services/tests/wmtests/OverlayApp/res/values/styles.xml b/services/tests/wmtests/OverlayApp/res/values/styles.xml new file mode 100644 index 000000000000..fff10a3f29ac --- /dev/null +++ b/services/tests/wmtests/OverlayApp/res/values/styles.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <style name="TranslucentFloatingTheme" > + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowIsFloating">true</item> + <item name="android:windowNoTitle">true</item> + + <!-- Disables starting window. --> + <item name="android:windowDisablePreview">true</item> + </style> +</resources> diff --git a/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java new file mode 100644 index 000000000000..89161c51dc53 --- /dev/null +++ b/services/tests/wmtests/OverlayApp/src/com/android/server/wm/overlay_app/OverlayApp.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.overlay_app; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; +import android.view.Gravity; +import android.view.WindowManager; +import android.widget.LinearLayout; + +/** + * Test app that is translucent not touchable modal. + * If launched with "disableInputSink" extra boolean value, this activity disables + * ActivityRecordInputSinkEnabled as long as the permission is granted. + */ +public class OverlayApp extends Activity { + private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout tv = new LinearLayout(this); + tv.setBackgroundColor(Color.GREEN); + tv.setPadding(50, 50, 50, 50); + tv.setGravity(Gravity.CENTER); + setContentView(tv); + + getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); + + if (getIntent().getBooleanExtra(KEY_DISABLE_INPUT_SINK, false)) { + setActivityRecordInputSinkEnabled(false); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java new file mode 100644 index 000000000000..3b280d9d45bd --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordInputSinkTests.java @@ -0,0 +1,209 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.UiAutomation; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.SystemClock; +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 android.view.MotionEvent; +import android.view.ViewGroup; +import android.widget.Button; +import android.window.WindowInfosListenerForTest; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.MediumTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.window.flags.Flags; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * Internal variant of {@link android.server.wm.window.ActivityRecordInputSinkTests}. + */ +@MediumTest +@RunWith(AndroidJUnit4.class) +public class ActivityRecordInputSinkTests { + private static final String OVERLAY_APP_PKG = "com.android.server.wm.overlay_app"; + private static final String OVERLAY_ACTIVITY = OVERLAY_APP_PKG + "/.OverlayApp"; + private static final String KEY_DISABLE_INPUT_SINK = "disableInputSink"; + + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + + @Rule + public final ActivityScenarioRule<TestActivity> mActivityRule = + new ActivityScenarioRule<>(TestActivity.class); + + private UiAutomation mUiAutomation; + + @Before + public void setUp() { + mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); + } + + @After + public void tearDown() { + ActivityManager am = + InstrumentationRegistry.getInstrumentation().getContext().getSystemService( + ActivityManager.class); + mUiAutomation.adoptShellPermissionIdentity(); + try { + am.forceStopPackage(OVERLAY_APP_PKG); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + @Test + public void testSimpleButtonPress() { + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(1, a.mNumClicked); + }); + } + + @Test + public void testSimpleButtonPress_withOverlay() throws InterruptedException { + startOverlayApp(false); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(0, a.mNumClicked); + }); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK) + public void testSimpleButtonPress_withOverlayDisableInputSink() throws InterruptedException { + startOverlayApp(true); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(1, a.mNumClicked); + }); + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_ALLOW_DISABLE_ACTIVITY_RECORD_INPUT_SINK) + public void testSimpleButtonPress_withOverlayDisableInputSink_flagDisabled() + throws InterruptedException { + startOverlayApp(true); + waitForOverlayApp(); + + injectTapOnButton(); + + mActivityRule.getScenario().onActivity(a -> { + assertEquals(0, a.mNumClicked); + }); + } + + private void startOverlayApp(boolean disableInputSink) { + String launchCommand = "am start -n " + OVERLAY_ACTIVITY; + if (disableInputSink) { + launchCommand += " --ez " + KEY_DISABLE_INPUT_SINK + " true"; + } + + mUiAutomation.adoptShellPermissionIdentity(); + try { + mUiAutomation.executeShellCommand(launchCommand); + } finally { + mUiAutomation.dropShellPermissionIdentity(); + } + } + + private void waitForOverlayApp() throws InterruptedException { + final var listenerHost = new WindowInfosListenerForTest(); + final var latch = new CountDownLatch(1); + final Consumer<List<WindowInfosListenerForTest.WindowInfo>> listener = windowInfos -> { + final boolean inputSinkReady = windowInfos.stream().anyMatch(info -> + info.isVisible + && info.name.contains("ActivityRecordInputSink " + OVERLAY_ACTIVITY)); + if (inputSinkReady) { + latch.countDown(); + } + }; + + listenerHost.addWindowInfosListener(listener); + try { + assertTrue(latch.await(5, TimeUnit.SECONDS)); + } finally { + listenerHost.removeWindowInfosListener(listener); + } + } + + private void injectTapOnButton() { + Rect buttonBounds = new Rect(); + mActivityRule.getScenario().onActivity(a -> { + a.mButton.getBoundsOnScreen(buttonBounds); + }); + final int x = buttonBounds.centerX(); + final int y = buttonBounds.centerY(); + + MotionEvent down = MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0); + mUiAutomation.injectInputEvent(down, true); + + SystemClock.sleep(10); + + MotionEvent up = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), + MotionEvent.ACTION_UP, x, y, 0); + mUiAutomation.injectInputEvent(up, true); + + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + public static class TestActivity extends Activity { + int mNumClicked = 0; + Button mButton; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mButton = new Button(this); + mButton.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + setContentView(mButton); + mButton.setOnClickListener(v -> mNumClicked++); + } + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 63de41f80c23..4c56f33af430 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -1953,6 +1953,13 @@ public class UsageStatsService extends SystemService implements } } + // Flags status. + pw.println("Flags:"); + pw.println(" " + Flags.FLAG_USER_INTERACTION_TYPE_API + + ": " + Flags.userInteractionTypeApi()); + pw.println(" " + Flags.FLAG_USE_PARCELED_LIST + + ": " + Flags.useParceledList()); + final int[] userIds; synchronized (mLock) { final int userCount = mUserState.size(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a584fc9b2216..b77596391be1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -2437,8 +2437,6 @@ public class VoiceInteractionManagerService extends SystemService { synchronized (VoiceInteractionManagerServiceStub.this) { Slog.i(TAG, "Force stopping current voice recognizer: " + getCurRecognizer(userHandle)); - // TODO: Figure out why the interactor was being cleared and document it. - setCurInteractor(null, userHandle); initRecognizer(userHandle); } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index f8608b8fead2..e12a815a84f5 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -23,6 +23,7 @@ import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; import android.annotation.DurationMillisLong; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -70,6 +71,7 @@ import android.util.Pair; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.HandlerExecutor; import com.android.internal.util.FunctionalUtils; import com.android.internal.util.Preconditions; @@ -95,7 +97,22 @@ import java.util.function.Consumer; import java.util.stream.Collectors; /** - * Subscription manager provides the mobile subscription information. + * Subscription manager provides the mobile subscription information that are associated with the + * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + * <p>For example, if we have + * <ul> + * <li> Subscription 1 associated with personal profile. + * <li> Subscription 2 associated with work profile. + * </ul> + * Then for SDK 35+, if the caller identity is personal profile, then + * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert the instance to see all. Additional permission + * may be required as documented on the each API. + * */ @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION) @@ -1446,6 +1463,16 @@ public class SubscriptionManager { } } + /** + * {@code true} if the SubscriptionManager instance should see all subscriptions regardless its + * association with particular user profile. + * + * <p> It only applies to Android SDK 35(V) and above. For Android SDK 34(U) and below, the + * caller can see all subscription across user profiles as it does today today even if it's + * {@code false}. + */ + private boolean mIsForAllUserProfiles = false; + /** @hide */ @UnsupportedAppUsage public SubscriptionManager(Context context) { @@ -1776,8 +1803,23 @@ public class SubscriptionManager { } /** - * Get the SubscriptionInfo(s) of the currently active SIM(s). The records will be sorted - * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}. + * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller + * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U) + * and below can see all subscriptions as it does today. + * + * <p>For example, if we have + * <ul> + * <li> Subscription 1 associated with personal profile. + * <li> Subscription 2 associated with work profile. + * </ul> + * Then for SDK 35+, if the caller identity is personal profile, then this will return + * subscription 1 only and vice versa. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert this instance to see all. + * + * <p> The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by + * {@link SubscriptionInfo#getSubscriptionId}. * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see @@ -1800,8 +1842,25 @@ public class SubscriptionManager { * </ul> */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + // @RequiresPermission(TODO(b/308809058)) public List<SubscriptionInfo> getActiveSubscriptionInfoList() { - return getActiveSubscriptionInfoList(/* userVisibleonly */true); + List<SubscriptionInfo> activeList = null; + + try { + ISub iSub = TelephonyManager.getSubscriptionService(); + if (iSub != null) { + activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), + mContext.getAttributionTag(), mIsForAllUserProfiles); + } + } catch (RemoteException ex) { + // ignore it + } + + if (activeList != null) { + activeList = activeList.stream().filter(subInfo -> isSubscriptionVisible(subInfo)) + .collect(Collectors.toList()); + } + return activeList; } /** @@ -1835,6 +1894,26 @@ public class SubscriptionManager { } /** + * Convert this subscription manager instance into one that can see all subscriptions across + * user profiles. + * + * @return a SubscriptionManager that can see all subscriptions regardless its user profile + * association. + * + * @see #getActiveSubscriptionInfoList + * @see #getActiveSubscriptionInfoCount + * @see UserHandle + */ + @FlaggedApi(Flags.FLAG_WORK_PROFILE_API_SPLIT) + // @RequiresPermission(TODO(b/308809058)) + // The permission check for accessing all subscriptions will be enforced upon calling the + // individual APIs linked above. + @NonNull public SubscriptionManager createForAllUserProfiles() { + mIsForAllUserProfiles = true; + return this; + } + + /** * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly * is true, it will filter out the hidden subscriptions. * @@ -1847,7 +1926,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), true /*isForAllUserProfiles*/); } } catch (RemoteException ex) { // ignore it @@ -2002,13 +2081,19 @@ public class SubscriptionManager { } /** - * Get the active subscription count. + * Get the active subscription count associated with the current caller user profile for + * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as + * it does today. + * + * <p>If the caller needs to see all subscriptions across user profiles, + * use {@link #createForAllUserProfiles} to convert this instance to see all. * * @return The current number of active subscriptions. * * @see #getActiveSubscriptionInfoList() */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + // @RequiresPermission(TODO(b/308809058)) public int getActiveSubscriptionInfoCount() { int result = 0; @@ -2016,7 +2101,7 @@ public class SubscriptionManager { ISub iSub = TelephonyManager.getSubscriptionService(); if (iSub != null) { result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(), - mContext.getAttributionTag()); + mContext.getAttributionTag(), mIsForAllUserProfiles); } } catch (RemoteException ex) { // ignore it diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index d2dbeb7aff74..3f41d5667107 100644 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -66,6 +66,8 @@ interface ISub { * * @param callingPackage The package maing the call. * @param callingFeatureId The feature in the package + * @param isForAllProfiles whether the caller intends to see all subscriptions regardless + * association. * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device. * <ul> * <li> @@ -83,14 +85,17 @@ interface ISub { * </ul> */ List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage, - String callingFeatureId); + String callingFeatureId, boolean isForAllProfiles); /** * @param callingPackage The package making the call. * @param callingFeatureId The feature in the package. + * @param isForAllProfile whether the caller intends to see all subscriptions regardless + * association. * @return the number of active subscriptions */ - int getActiveSubInfoCount(String callingPackage, String callingFeatureId); + int getActiveSubInfoCount(String callingPackage, String callingFeatureId, + boolean isForAllProfile); /** * @return the maximum number of subscriptions this device will support at any one time. diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp index 15aaa463cce7..83ced2c2258f 100644 --- a/tests/InputScreenshotTest/Android.bp +++ b/tests/InputScreenshotTest/Android.bp @@ -7,12 +7,27 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +filegroup { + name: "InputScreenshotTestRNGFiles", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + exclude_srcs: [ + "src/android/input/screenshot/KeyboardLayoutPreviewAnsiScreenshotTest.kt", + "src/android/input/screenshot/KeyboardLayoutPreviewJisScreenshotTest.kt", + ], +} + android_test { name: "InputScreenshotTests", srcs: [ "src/**/*.java", "src/**/*.kt", ], + exclude_srcs: [ + "src/android/input/screenshot/package-info.java", + ], platform_apis: true, certificate: "platform", static_libs: [ @@ -43,6 +58,7 @@ android_test { "hamcrest-library", "kotlin-test", "flag-junit", + "platform-parametric-runner-lib", "platform-test-annotations", "services.core.unboosted", "testables", diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp new file mode 100644 index 000000000000..912f4b8069b4 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/Android.bp @@ -0,0 +1,71 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_library { + name: "InputRoboRNGTestsAssetsLib", + asset_dirs: ["assets"], + sdk_version: "current", + platform_apis: true, + manifest: "AndroidManifest.xml", + optimize: { + enabled: false, + }, + use_resource_processor: true, +} + +android_app { + name: "InputRoboApp", + srcs: [], + static_libs: [ + "androidx.test.espresso.core", + "androidx.appcompat_appcompat", + "flag-junit", + "guava", + "InputRoboRNGTestsAssetsLib", + "platform-screenshot-diff-core", + "PlatformComposeSceneTransitionLayoutTestsUtils", + ], + manifest: "robo-manifest.xml", + aaptflags: [ + "--extra-packages", + "com.android.input.screenshot", + ], + dont_merge_manifests: true, + platform_apis: true, + system_ext_specific: true, + certificate: "platform", + privileged: true, + resource_dirs: [], + kotlincflags: ["-Xjvm-default=all"], + + plugins: ["dagger2-compiler"], + use_resource_processor: true, +} + +android_robolectric_test { + name: "InputRoboRNGTests", + srcs: [ + ":InputScreenshotTestRNGFiles", + ":flag-junit", + ":platform-test-screenshot-rules", + ], + // Do not add any new libraries here, they should be added to SystemUIGoogleRobo above. + static_libs: [ + "androidx.compose.runtime_runtime", + "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", + "inline-mockito-robolectric-prebuilt", + "platform-parametric-runner-lib", + "uiautomator-helpers", + ], + libs: [ + "android.test.runner", + "android.test.base", + "android.test.mock", + "truth", + ], + upstream: true, + java_resource_dirs: ["config"], + instrumentation_for: "InputRoboApp", +} diff --git a/tests/InputScreenshotTest/robotests/AndroidManifest.xml b/tests/InputScreenshotTest/robotests/AndroidManifest.xml new file mode 100644 index 000000000000..56893113288d --- /dev/null +++ b/tests/InputScreenshotTest/robotests/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.input.screenshot"> + <uses-sdk android:minSdkVersion="21"/> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> +</manifest> diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png Binary files differnew file mode 100644 index 000000000000..baf204a6cfb3 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/phone/light_landscape_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png Binary files differnew file mode 100644 index 000000000000..deb3ceeca7fb --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/phone/light_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png Binary files differnew file mode 100644 index 000000000000..34e25f73d953 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/assets/tablet/dark_portrait_layout-preview.png diff --git a/tests/InputScreenshotTest/robotests/config/robolectric.properties b/tests/InputScreenshotTest/robotests/config/robolectric.properties new file mode 100644 index 000000000000..83d7549551ce --- /dev/null +++ b/tests/InputScreenshotTest/robotests/config/robolectric.properties @@ -0,0 +1,15 @@ +# Copyright (C) 2022 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. +# +sdk=NEWEST_SDK
\ No newline at end of file diff --git a/tests/InputScreenshotTest/robotests/robo-manifest.xml b/tests/InputScreenshotTest/robotests/robo-manifest.xml new file mode 100644 index 000000000000..e86f58ef0e55 --- /dev/null +++ b/tests/InputScreenshotTest/robotests/robo-manifest.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!--- Include all the namespaces we will ever need anywhere, because this is the source the manifest merger uses for namespaces --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="com.android.input.screenshot" + coreApp="true"> + <application> + <activity + android:name="androidx.activity.ComponentActivity" + android:exported="true"> + </activity> + </application> +</manifest> diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt index c2c3d5530a00..75dab41d3609 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/InputScreenshotTestRule.kt @@ -18,6 +18,7 @@ package com.android.input.screenshot import android.content.Context import android.graphics.Bitmap +import android.os.Build import androidx.activity.ComponentActivity import androidx.compose.foundation.Image import androidx.compose.ui.platform.ViewRootForTest @@ -49,15 +50,17 @@ class InputScreenshotTestRule( ) ) private val composeRule = createAndroidComposeRule<ComponentActivity>() - private val delegateRule = - RuleChain.outerRule(colorsRule) - .around(deviceEmulationRule) + private val roboRule = + RuleChain.outerRule(deviceEmulationRule) .around(screenshotRule) .around(composeRule) + private val delegateRule = RuleChain.outerRule(colorsRule).around(roboRule) private val matcher = UnitTestBitmapMatcher + private val isRobolectric = if (Build.FINGERPRINT.contains("robolectric")) true else false override fun apply(base: Statement, description: Description): Statement { - return delegateRule.apply(base, description) + val ruleToApply = if (isRobolectric) roboRule else delegateRule + return ruleToApply.apply(base, description) } /** @@ -84,4 +87,4 @@ class InputScreenshotTestRule( val view = (composeRule.onRoot().fetchSemanticsNode().root as ViewRootForTest).view screenshotRule.assertBitmapAgainstGolden(view.drawIntoBitmap(), goldenIdentifier, matcher) } -}
\ No newline at end of file +} diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt index 8ae6dfd8b63b..ab7bb4eda899 100644 --- a/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt +++ b/tests/InputScreenshotTest/src/android/input/screenshot/KeyboardLayoutPreviewIsoScreenshotTest.kt @@ -26,14 +26,15 @@ import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters import platform.test.screenshot.DeviceEmulationSpec /** A screenshot test for Keyboard layout preview for Iso physical layout. */ -@RunWith(Parameterized::class) +@RunWith(ParameterizedAndroidJunit4::class) class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) { companion object { - @Parameterized.Parameters(name = "{0}") + @Parameters(name = "{0}") @JvmStatic fun getTestSpecs() = DeviceEmulationSpec.PhoneAndTabletMinimal } @@ -55,4 +56,4 @@ class KeyboardLayoutPreviewIsoScreenshotTest(emulationSpec: DeviceEmulationSpec) } } -}
\ No newline at end of file +} diff --git a/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java new file mode 100644 index 000000000000..4b5a56d3bd1d --- /dev/null +++ b/tests/InputScreenshotTest/src/android/input/screenshot/package-info.java @@ -0,0 +1,4 @@ +@GraphicsMode(GraphicsMode.Mode.NATIVE) +package com.android.input.screenshot; + +import org.robolectric.annotation.GraphicsMode; diff --git a/tests/SmokeTestApps/Android.bp b/tests/SmokeTestApps/Android.bp index 3505fe1c4afb..38ee8ac99747 100644 --- a/tests/SmokeTestApps/Android.bp +++ b/tests/SmokeTestApps/Android.bp @@ -11,4 +11,7 @@ android_test { name: "SmokeTestTriggerApps", srcs: ["src/**/*.java"], sdk_version: "current", + errorprone: { + enabled: false, + }, } diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java index 4a3a79803b65..668c94c0f91c 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java @@ -27,9 +27,10 @@ import java.util.concurrent.atomic.AtomicLong; /** * Tentative, partial implementation of the Parcel native methods, using Java's - * {@link ByteBuffer}. It turned out there's enough semantics differences between Parcel - * and {@link ByteBuffer}, so it didn't actually work. - * (e.g. Parcel seems to allow moving the data position to be beyond its size? Which + * {@code byte[]}. + * (We don't use a {@link ByteBuffer} because there's enough semantics differences between Parcel + * and {@link ByteBuffer}, and it didn't work out. + * e.g. Parcel seems to allow moving the data position to be beyond its size? Which * {@link ByteBuffer} wouldn't allow...) */ public class Parcel_host { diff --git a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh index 89daa2084420..85038be80c51 100755 --- a/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh +++ b/tools/hoststubgen/hoststubgen/invoketest/hoststubgen-invoke-test.sh @@ -13,6 +13,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# This command is expected to be executed with: atest hoststubgen-invoke-test + set -e # Exit when any command files # This script runs HostStubGen directly with various arguments and make sure @@ -35,6 +37,12 @@ if [[ "$TEMP" == "" ]] ; then mkdir -p $TEMP fi +cleanup_temp() { + rm -fr $TEMP/* +} + +cleanup_temp + JAR=hoststubgen-test-tiny-framework.jar STUB=$TEMP/stub.jar IMPL=$TEMP/impl.jar @@ -47,12 +55,10 @@ HOSTSTUBGEN_OUT=$TEMP/output.txt # HostStubGen result in it. HOSTSTUBGEN_RC=0 -# Define the functions to - - # Note, because the build rule will only install hoststubgen.jar, but not the wrapper script, # we need to execute it manually with the java command. hoststubgen() { + echo "Running hoststubgen with: $*" java -jar ./hoststubgen.jar "$@" } @@ -62,7 +68,7 @@ run_hoststubgen() { echo "# Test: $test_name" - rm -f $HOSTSTUBGEN_OUT + cleanup_temp local filter_arg="" @@ -73,11 +79,21 @@ run_hoststubgen() { cat $ANNOTATION_FILTER fi + local stub_arg="" + local impl_arg="" + + if [[ "$STUB" != "" ]] ; then + stub_arg="--out-stub-jar $STUB" + fi + if [[ "$IMPL" != "" ]] ; then + impl_arg="--out-impl-jar $IMPL" + fi + hoststubgen \ --debug \ --in-jar $JAR \ - --out-stub-jar $STUB \ - --out-impl-jar $IMPL \ + $stub_arg \ + $impl_arg \ --stub-annotation \ android.hosttest.annotation.HostSideTestStub \ --keep-annotation \ @@ -105,6 +121,21 @@ run_hoststubgen() { return 0 } +assert_file_generated() { + local file="$1" + if [[ "$file" == "" ]] ; then + if [[ -f "$file" ]] ; then + echo "HostStubGen shouldn't have generated $file" + return 1 + fi + else + if ! [[ -f "$file" ]] ; then + echo "HostStubGen didn't generate $file" + return 1 + fi + fi +} + run_hoststubgen_for_success() { run_hoststubgen "$@" @@ -112,6 +143,9 @@ run_hoststubgen_for_success() { echo "HostStubGen expected to finish successfully, but failed with $rc" return 1 fi + + assert_file_generated "$STUB" + assert_file_generated "$IMPL" } run_hoststubgen_for_failure() { @@ -189,6 +223,11 @@ run_hoststubgen_for_success "One specific class disallowed, but it doesn't use a * # All other classes allowed " +STUB="" run_hoststubgen_for_success "No stub generation" "" + +IMPL="" run_hoststubgen_for_success "No impl generation" "" + +STUB="" IMPL="" run_hoststubgen_for_success "No stub, no impl generation" "" echo "All tests passed" diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 07bd2dc7c867..3cdddc23b332 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -237,8 +237,8 @@ class HostStubGen(val options: HostStubGenOptions) { */ private fun convert( inJar: String, - outStubJar: String, - outImplJar: String, + outStubJar: String?, + outImplJar: String?, filter: OutputFilter, enableChecker: Boolean, classes: ClassNodes, @@ -254,8 +254,8 @@ class HostStubGen(val options: HostStubGenOptions) { log.withIndent { // Open the input jar file and process each entry. ZipFile(inJar).use { inZip -> - ZipOutputStream(FileOutputStream(outStubJar)).use { stubOutStream -> - ZipOutputStream(FileOutputStream(outImplJar)).use { implOutStream -> + maybeWithZipOutputStream(outStubJar) { stubOutStream -> + maybeWithZipOutputStream(outImplJar) { implOutStream -> val inEntries = inZip.entries() while (inEntries.hasMoreElements()) { val entry = inEntries.nextElement() @@ -265,22 +265,29 @@ class HostStubGen(val options: HostStubGenOptions) { log.i("Converted all entries.") } } - log.i("Created stub: $outStubJar") - log.i("Created impl: $outImplJar") + outStubJar?.let { log.i("Created stub: $it") } + outImplJar?.let { log.i("Created impl: $it") } } } val end = System.currentTimeMillis() log.v("Done transforming the jar in %.1f second(s).", (end - start) / 1000.0) } + private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T { + if (filename == null) { + return block(null) + } + return ZipOutputStream(FileOutputStream(filename)).use(block) + } + /** * Convert a single ZIP entry, which may or may not be a class file. */ private fun convertSingleEntry( inZip: ZipFile, entry: ZipEntry, - stubOutStream: ZipOutputStream, - implOutStream: ZipOutputStream, + stubOutStream: ZipOutputStream?, + implOutStream: ZipOutputStream?, filter: OutputFilter, packageRedirector: PackageRedirectRemapper, enableChecker: Boolean, @@ -316,8 +323,8 @@ class HostStubGen(val options: HostStubGenOptions) { // Unknown type, we just copy it to both output zip files. // TODO: We probably shouldn't do it for stub jar? log.v("Copying: %s", entry.name) - copyZipEntry(inZip, entry, stubOutStream) - copyZipEntry(inZip, entry, implOutStream) + stubOutStream?.let { copyZipEntry(inZip, entry, it) } + implOutStream?.let { copyZipEntry(inZip, entry, it) } } } @@ -346,8 +353,8 @@ class HostStubGen(val options: HostStubGenOptions) { private fun processSingleClass( inZip: ZipFile, entry: ZipEntry, - stubOutStream: ZipOutputStream, - implOutStream: ZipOutputStream, + stubOutStream: ZipOutputStream?, + implOutStream: ZipOutputStream?, filter: OutputFilter, packageRedirector: PackageRedirectRemapper, enableChecker: Boolean, @@ -361,7 +368,7 @@ class HostStubGen(val options: HostStubGenOptions) { return } // Generate stub first. - if (classPolicy.policy.needsInStub) { + if (stubOutStream != null && classPolicy.policy.needsInStub) { log.v("Creating stub class: %s Policy: %s", classInternalName, classPolicy) log.withIndent { BufferedInputStream(inZip.getInputStream(entry)).use { bis -> @@ -374,8 +381,8 @@ class HostStubGen(val options: HostStubGenOptions) { } } } - log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy) - if (classPolicy.policy.needsInImpl) { + if (implOutStream != null && classPolicy.policy.needsInImpl) { + log.v("Creating impl class: %s Policy: %s", classInternalName, classPolicy) log.withIndent { BufferedInputStream(inZip.getInputStream(entry)).use { bis -> val newEntry = ZipEntry(entry.name) diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt index da5348707528..83f873d38f1b 100644 --- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt +++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt @@ -28,10 +28,10 @@ class HostStubGenOptions( var inJar: String = "", /** Output stub jar file */ - var outStubJar: String = "", + var outStubJar: String? = null, /** Output implementation jar file */ - var outImplJar: String = "", + var outImplJar: String? = null, var inputJarDumpFile: String? = null, @@ -71,7 +71,7 @@ class HostStubGenOptions( var enablePreTrace: Boolean = false, var enablePostTrace: Boolean = false, - var enableNonStubMethodCallDetection: Boolean = true, + var enableNonStubMethodCallDetection: Boolean = false, ) { companion object { @@ -209,11 +209,14 @@ class HostStubGenOptions( if (ret.inJar.isEmpty()) { throw ArgumentsException("Required option missing: --in-jar") } - if (ret.outStubJar.isEmpty()) { - throw ArgumentsException("Required option missing: --out-stub-jar") + if (ret.outStubJar == null && ret.outImplJar == null) { + log.w("Neither --out-stub-jar nor --out-impl-jar is set." + + " $COMMAND_NAME will not generate jar files.") } - if (ret.outImplJar.isEmpty()) { - throw ArgumentsException("Required option missing: --out-impl-jar") + + if (ret.enableNonStubMethodCallDetection) { + log.w("--enable-non-stub-method-check is not fully implemented yet." + + " See the todo in doesMethodNeedNonStubCallCheck().") } return ret diff --git a/tools/hoststubgen/hoststubgen/test-framework/README.md b/tools/hoststubgen/hoststubgen/test-framework/README.md index 20e2f873b152..f616ad61d219 100644 --- a/tools/hoststubgen/hoststubgen/test-framework/README.md +++ b/tools/hoststubgen/hoststubgen/test-framework/README.md @@ -14,12 +14,6 @@ tests. $ atest --no-bazel-mode HostStubGenTest-framework-test-host-test ``` -- With `run-ravenwood-test` - -``` -$ run-ravenwood-test HostStubGenTest-framework-test-host-test -``` - - Advanced option: `run-test-without-atest.sh` runs the test without using `atest` or `run-ravenwood-test` ``` diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md index f3c0450d42a3..3bfad9bd673b 100644 --- a/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md +++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/README.md @@ -13,12 +13,6 @@ This test doesn't use the actual android framework code. $ atest hoststubgen-test-tiny-test ``` -- With `run-ravenwood-test` should work too. This is the proper way to run it. - -``` -$ run-ravenwood-test hoststubgen-test-tiny-test -``` - - `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without using the build system. This is useful for debugging the tool. diff --git a/tools/hoststubgen/scripts/Android.bp b/tools/hoststubgen/scripts/Android.bp index 5da805e5640e..b1ba07ec540d 100644 --- a/tools/hoststubgen/scripts/Android.bp +++ b/tools/hoststubgen/scripts/Android.bp @@ -18,9 +18,3 @@ genrule_defaults { tools: ["dump-jar"], cmd: "$(location dump-jar) -s -o $(out) $(in)", } - -sh_binary_host { - name: "run-ravenwood-test", - src: "run-ravenwood-test", - visibility: ["//visibility:public"], -} diff --git a/tools/hoststubgen/scripts/run-all-tests.sh b/tools/hoststubgen/scripts/run-all-tests.sh index 2dac08969d44..82faa91e2cac 100755 --- a/tools/hoststubgen/scripts/run-all-tests.sh +++ b/tools/hoststubgen/scripts/run-all-tests.sh @@ -22,10 +22,10 @@ cd .. READY_TEST_MODULES=( HostStubGenTest-framework-all-test-host-test hoststubgen-test-tiny-test + CtsUtilTestCasesRavenwood ) MUST_BUILD_MODULES=( - run-ravenwood-test "${NOT_READY_TEST_MODULES[*]}" HostStubGenTest-framework-test ) @@ -51,8 +51,6 @@ run ./scripts/build-framework-hostside-jars-and-extract.sh # run ./scripts/build-framework-hostside-jars-without-genrules.sh # These tests should all pass. -run-ravenwood-test ${READY_TEST_MODULES[*]} - -run atest CtsUtilTestCasesRavenwood +run atest ${READY_TEST_MODULES[*]} echo ""${0##*/}" finished, with no unexpected failures. Ready to submit!"
\ No newline at end of file diff --git a/tools/hoststubgen/scripts/run-ravenwood-test b/tools/hoststubgen/scripts/run-ravenwood-test deleted file mode 100755 index 9bbb859f5c3d..000000000000 --- a/tools/hoststubgen/scripts/run-ravenwood-test +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash -# Copyright (C) 2023 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e - -# Script to run a "Ravenwood" host side test. -# -# A proper way to run these tests is to use `atest`, but `atest` has a known issue of loading -# unrelated jar files as the class path, so for now we use this script to run host side tests. - -# Copy (with some changes) some functions from ../common.sh, so this script can be used without it. - -m() { - if (( $SKIP_BUILD )) ; then - echo "Skipping build: $*" 1>&2 - return 0 - fi - run ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" -} - -run() { - echo "Running: $*" 1>&2 - "$@" -} - -run_junit_test_jar() { - local jar="$1" - echo "Starting test: $jar ..." - run cd "${jar%/*}" - - run ${JAVA:-java} $JAVA_OPTS \ - -cp $jar \ - org.junit.runner.JUnitCore \ - com.android.hoststubgen.hosthelper.HostTestSuite || return 1 - return 0 -} - -help() { - cat <<'EOF' - - run-ravenwood-test -- Run Ravenwood host tests - - Usage: - run-ravenwood-test [options] MODULE-NAME ... - - Options: - -h: Help - -t: Run test only, without building - -b: Build only, without running - - Example: - run-ravenwood-test HostStubGenTest-framework-test-host-test - -EOF -} - -#------------------------------------------------------------------------- -# Parse options -#------------------------------------------------------------------------- -build=0 -test=0 - -while getopts "htb" opt; do - case "$opt" in - h) help; exit 0 ;; - t) - test=1 - ;; - b) - build=1 - ;; - esac -done -shift $(($OPTIND - 1)) - -# If neither -t nor -b is provided, then build and run./ -if (( ( $build + $test ) == 0 )) ; then - build=1 - test=1 -fi - - -modules=("${@}") - -if (( "${#modules[@]}" == 0 )); then - help - exit 1 -fi - -#------------------------------------------------------------------------- -# Build -#------------------------------------------------------------------------- -if (( $build )) ; then - run m "${modules[@]}" -fi - -#------------------------------------------------------------------------- -# Run -#------------------------------------------------------------------------- - -failures=0 -if (( test )) ; then - for module in "${modules[@]}"; do - if run_junit_test_jar "$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/${module}/${module}.jar"; then - : # passed. - else - failures=$(( failures + 1 )) - fi - done - - if (( $failures > 0 )) ; then - echo "$failures test jar(s) failed." 1>&2 - exit 2 - fi -fi - -exit 0
\ No newline at end of file |