diff options
66 files changed, 1587 insertions, 365 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 858620b41088..f44f5feff6dd 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -30,7 +30,7 @@ aconfig_declarations_group { "android.security.flags-aconfig-java", "com.android.hardware.camera2-aconfig-java", "com.android.hardware.input-aconfig-java", - "com.android.net.thread.flags-aconfig-java", + "com.android.net.thread.platform.flags-aconfig-java", "com.android.text.flags-aconfig-java", "com.android.window.flags.window-aconfig-java", // !!! KEEP THIS LIST ALPHABETICAL !!! @@ -241,8 +241,8 @@ aconfig_declarations { // Thread network aconfig_declarations { - name: "com.android.net.thread.flags-aconfig", - package: "com.android.net.thread.flags", + name: "com.android.net.thread.platform.flags-aconfig", + package: "com.android.net.thread.platform.flags", srcs: ["core/java/android/net/thread/flags.aconfig"], } @@ -254,8 +254,8 @@ java_aconfig_library { } java_aconfig_library { - name: "com.android.net.thread.flags-aconfig-java", - aconfig_declarations: "com.android.net.thread.flags-aconfig", + name: "com.android.net.thread.platform.flags-aconfig-java", + aconfig_declarations: "com.android.net.thread.platform.flags-aconfig", defaults: ["framework-minus-apex-aconfig-java-defaults"], } diff --git a/Android.bp b/Android.bp index 65ec01592fce..d4b63c427778 100644 --- a/Android.bp +++ b/Android.bp @@ -479,8 +479,6 @@ java_library { lint: { baseline_filename: "lint-baseline.xml", }, - // For jarjar repackaging - jarjar_prefix: "com.android.internal.hidden_from_bootclasspath", } java_library { @@ -511,6 +509,7 @@ java_library { lint: { baseline_filename: "lint-baseline.xml", }, + jarjar_prefix: "com.android.internal.hidden_from_bootclasspath", } java_library { diff --git a/INPUT_OWNERS b/INPUT_OWNERS index 44b2f3805495..06ead06fc13a 100644 --- a/INPUT_OWNERS +++ b/INPUT_OWNERS @@ -1,4 +1,6 @@ # Bug component: 136048 +arpitks@google.com +asmitapoddar@google.com hcutts@google.com joseprio@google.com michaelwr@google.com @@ -32,6 +32,7 @@ per-file **.bp,**.mk = hansson@google.com, joeo@google.com, lamontjones@google.c per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS +per-file INPUT_OWNERS = file:/INPUT_OWNERS per-file ZYGOTE_OWNERS = file:/ZYGOTE_OWNERS per-file SQLITE_OWNERS = file:/SQLITE_OWNERS @@ -40,4 +41,4 @@ per-file *Ravenwood* = file:ravenwood/OWNERS per-file PERFORMANCE_OWNERS = file:/PERFORMANCE_OWNERS -per-file PACKAGE_MANAGER_OWNERS = file:/PACKAGE_MANAGER_OWNERS
\ No newline at end of file +per-file PACKAGE_MANAGER_OWNERS = file:/PACKAGE_MANAGER_OWNERS diff --git a/api/Android.bp b/api/Android.bp index 14320c96aafc..bc217f3afc20 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -122,6 +122,7 @@ combined_apis { "framework-nfc", "framework-ondevicepersonalization", "framework-pdf", + "framework-pdf-v", "framework-permission", "framework-permission-s", "framework-scheduling", diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp index 2edbd9138f6e..d94890e23ae5 100644 --- a/api/StubLibraries.bp +++ b/api/StubLibraries.bp @@ -458,13 +458,21 @@ java_defaults { libs: ["stub-annotations"], } +java_defaults { + name: "android-non-updatable_everything_from_text_defaults", + defaults: [ + "android-non-updatable_from_text_defaults", + ], + stubs_type: "everything", +} + java_api_library { name: "android-non-updatable.stubs.from-text", api_surface: "public", api_contributions: [ "api-stubs-docs-non-updatable.api.contribution", ], - defaults: ["android-non-updatable_from_text_defaults"], + defaults: ["android-non-updatable_everything_from_text_defaults"], full_api_surface_stub: "android_stubs_current.from-text", } @@ -475,7 +483,7 @@ java_api_library { "api-stubs-docs-non-updatable.api.contribution", "system-api-stubs-docs-non-updatable.api.contribution", ], - defaults: ["android-non-updatable_from_text_defaults"], + defaults: ["android-non-updatable_everything_from_text_defaults"], full_api_surface_stub: "android_system_stubs_current.from-text", } @@ -487,7 +495,7 @@ java_api_library { "system-api-stubs-docs-non-updatable.api.contribution", "test-api-stubs-docs-non-updatable.api.contribution", ], - defaults: ["android-non-updatable_from_text_defaults"], + defaults: ["android-non-updatable_everything_from_text_defaults"], full_api_surface_stub: "android_test_stubs_current.from-text", } @@ -499,7 +507,7 @@ java_api_library { "system-api-stubs-docs-non-updatable.api.contribution", "module-lib-api-stubs-docs-non-updatable.api.contribution", ], - defaults: ["android-non-updatable_from_text_defaults"], + defaults: ["android-non-updatable_everything_from_text_defaults"], full_api_surface_stub: "android_module_lib_stubs_current_full.from-text", } @@ -515,7 +523,7 @@ java_api_library { "test-api-stubs-docs-non-updatable.api.contribution", "module-lib-api-stubs-docs-non-updatable.api.contribution", ], - defaults: ["android-non-updatable_from_text_defaults"], + defaults: ["android-non-updatable_everything_from_text_defaults"], full_api_surface_stub: "android_test_module_lib_stubs_current.from-text", // This module is only used for hiddenapi, and other modules should not @@ -827,6 +835,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -843,6 +852,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -861,6 +871,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -879,6 +890,7 @@ java_api_library { "system-api-stubs-docs-non-updatable.api.contribution", ], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -899,6 +911,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -913,6 +926,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -938,6 +952,7 @@ java_api_library { "//visibility:private", ], enable_validation: false, + stubs_type: "everything", } java_api_library { @@ -955,6 +970,7 @@ java_api_library { ], visibility: ["//visibility:public"], enable_validation: false, + stubs_type: "everything", } //////////////////////////////////////////////////////////////////////// diff --git a/core/api/current.txt b/core/api/current.txt index 8f5374c9dbc6..2900bc848930 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -193,7 +193,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_SYSTEM_APPS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS"; field public static final String MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS"; field public static final String MANAGE_DEVICE_POLICY_SYSTEM_UPDATES = "android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES"; - field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"; + field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String MANAGE_DEVICE_POLICY_THREAD_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK"; field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME"; field public static final String MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING = "android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING"; field public static final String MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER = "android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER"; @@ -12739,7 +12739,7 @@ package android.content.pm { field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access"; field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription"; field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television"; - field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network"; + field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network"; field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch"; field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct"; @@ -33616,7 +33616,7 @@ package android.os { field public static final String DISALLOW_SHARING_ADMIN_CONFIGURED_WIFI = "no_sharing_admin_configured_wifi"; field public static final String DISALLOW_SMS = "no_sms"; field public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs"; - field @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network"; + field @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") public static final String DISALLOW_THREAD_NETWORK = "no_thread_network"; field public static final String DISALLOW_ULTRA_WIDEBAND_RADIO = "no_ultra_wideband_radio"; field public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password"; field public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; @@ -39247,6 +39247,7 @@ package android.security.keystore { method @Deprecated public boolean isInsideSecureHardware(); method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isTrustedUserPresenceRequired(); + method @FlaggedApi("android.security.keyinfo_unlocked_device_required") public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware(); method public boolean isUserAuthenticationValidWhileOnBody(); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index eca138b83dbe..12802e4d3a3d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -298,7 +298,7 @@ package android { field public static final String RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE"; field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; - field @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; + field @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine") public static final String REGISTER_NSD_OFFLOAD_ENGINE = "android.permission.REGISTER_NSD_OFFLOAD_ENGINE"; field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM"; field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER"; @@ -357,7 +357,7 @@ package android { field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY"; field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA"; field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED"; - field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED"; + field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED"; field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE"; field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION"; field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE"; @@ -3482,7 +3482,7 @@ package android.content { field public static final String SYSTEM_CONFIG_SERVICE = "system_config"; field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; field public static final String TETHERING_SERVICE = "tethering"; - field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network"; + field @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network"; field public static final String TIME_MANAGER_SERVICE = "time_manager"; field public static final String TRANSLATION_MANAGER_SERVICE = "translation"; field public static final String UI_TRANSLATION_SERVICE = "ui_translation"; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ced35549769a..bd5095809a80 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -7363,6 +7363,7 @@ public class Notification implements Parcelable /** * @hide */ + @SuppressWarnings("HiddenAbstractMethod") public abstract boolean areNotificationsVisiblyDifferent(Style other); /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index bfb041cf3edd..6d54223af68a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4767,7 +4767,7 @@ public abstract class Context { * @see android.net.thread.ThreadNetworkManager * @hide */ - @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM) + @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM) @SystemApi public static final String THREAD_NETWORK_SERVICE = "thread_network"; diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS index d7a0cbd3f601..a5acb384cd87 100644 --- a/core/java/android/content/pm/OWNERS +++ b/core/java/android/content/pm/OWNERS @@ -17,6 +17,8 @@ per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS per-file UserInfo* = file:/MULTIUSER_OWNERS # Bug component: 578329 = per-file *UserProperties* per-file *UserProperties* = file:/MULTIUSER_OWNERS +# Bug component: 578329 = per-file *multiuser* +per-file *multiuser* = file:/MULTIUSER_OWNERS # Bug component: 1219020 = per-file *BackgroundInstallControl* per-file *BackgroundInstallControl* = file:/services/core/java/com/android/server/pm/BACKGROUND_INSTALL_OWNERS
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c943789c2e33..92bfcefae4e1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3756,7 +3756,7 @@ public abstract class PackageManager { * The device is capable of communicating with other devices via * <a href="https://www.threadgroup.org">Thread</a> networking protocol. */ - @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM) + @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM) @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network"; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index d6c58ac1d7fe..738b6d9ca27b 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -1379,7 +1379,6 @@ public final class OutputConfiguration implements Parcelable { mSurfaceType != other.mSurfaceType || mIsDeferredConfig != other.mIsDeferredConfig || mIsShared != other.mIsShared || - mConfiguredFormat != other.mConfiguredFormat || mConfiguredDataspace != other.mConfiguredDataspace || mConfiguredGenerationId != other.mConfiguredGenerationId || !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) || diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig index 311dc09eb516..9f9aef8acc6a 100644 --- a/core/java/android/net/flags.aconfig +++ b/core/java/android/net/flags.aconfig @@ -9,3 +9,17 @@ flag { description: "The flag controls the access for getIpSecTransformState and IpSecTransformState" bug: "308011229" } + +flag { + name: "powered_off_finding_platform" + namespace: "nearby" + description: "Controls whether the Powered Off Finding feature is enabled" + bug: "307898240" +} + +flag { + name: "register_nsd_offload_engine" + namespace: "android_core_networking" + description: "Flag for registerOffloadEngine API in NsdManager" + bug: "294777050" +} diff --git a/core/java/android/net/thread/flags.aconfig b/core/java/android/net/thread/flags.aconfig index ff762d78c412..d679f9c3acb8 100644 --- a/core/java/android/net/thread/flags.aconfig +++ b/core/java/android/net/thread/flags.aconfig @@ -1,4 +1,4 @@ -package: "com.android.net.thread.flags" +package: "com.android.net.thread.platform.flags" # This file contains aconfig flags used from platform code # Flags used for module APIs must be in aconfig files under each modules diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS index 19a3ba01a4ba..9c165f7f84df 100644 --- a/core/java/android/os/OWNERS +++ b/core/java/android/os/OWNERS @@ -83,6 +83,7 @@ per-file ArtModuleServiceManager.java = file:platform/art:/OWNERS # PerformanceHintManager per-file PerformanceHintManager.java = file:/ADPF_OWNERS +per-file WorkDuration.java = file:/ADPF_OWNERS # IThermal interfaces per-file IThermal* = file:/THERMAL_OWNERS diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f60a8a48289c..ca87410ed438 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1867,7 +1867,7 @@ public class UserManager { * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) * @see #getUserRestrictions() */ - @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED) + @FlaggedApi(com.android.net.thread.platform.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED) public static final String DISALLOW_THREAD_NETWORK = "no_thread_network"; /** diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS index 5e0a5635d4bd..a26fb6274f55 100644 --- a/core/java/android/os/storage/OWNERS +++ b/core/java/android/os/storage/OWNERS @@ -3,11 +3,15 @@ # Please assign new bugs to android-storage-triage@, not to individual people # Android Storage Team +aibra@google.com +akgaurav@google.com alukin@google.com ankitavyas@google.com dipankarb@google.com gargshivam@google.com +ishneet@google.com krishang@google.com +oeissa@google.com riyaghai@google.com sahanas@google.com shikhamalhotra@google.com diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 76314546b4f0..5e7edda31c19 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -31,6 +31,13 @@ flag { } flag { + name: "keyinfo_unlocked_device_required" + namespace: "hardware_backed_security" + description: "Add the API android.security.keystore.KeyInfo#isUnlockedDeviceRequired()" + bug: "296475382" +} + +flag { name: "deprecate_fsv_sig" namespace: "hardware_backed_security" description: "Feature flag for deprecating .fsv_sig" diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index a436e08a0a2d..2eece6d4a454 100644 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -69,6 +69,7 @@ public final class InputDevice implements Parcelable { private final String mName; private final int mVendorId; private final int mProductId; + private final int mDeviceBus; private final String mDescriptor; private final InputDeviceIdentifier mIdentifier; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) @@ -468,8 +469,8 @@ public final class InputDevice implements Parcelable { * Called by native code */ private InputDevice(int id, int generation, int controllerNumber, String name, int vendorId, - int productId, String descriptor, boolean isExternal, int sources, int keyboardType, - KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag, + int productId, int deviceBus, String descriptor, boolean isExternal, int sources, + int keyboardType, KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag, @Nullable String keyboardLayoutType, boolean hasVibrator, boolean hasMicrophone, boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery, int usiVersionMajor, int usiVersionMinor, int associatedDisplayId) { @@ -479,6 +480,7 @@ public final class InputDevice implements Parcelable { mName = name; mVendorId = vendorId; mProductId = productId; + mDeviceBus = deviceBus; mDescriptor = descriptor; mIsExternal = isExternal; mSources = sources; @@ -512,6 +514,7 @@ public final class InputDevice implements Parcelable { mName = in.readString(); mVendorId = in.readInt(); mProductId = in.readInt(); + mDeviceBus = in.readInt(); mDescriptor = in.readString(); mIsExternal = in.readInt() != 0; mSources = in.readInt(); @@ -551,6 +554,7 @@ public final class InputDevice implements Parcelable { private String mName = ""; private int mVendorId = 0; private int mProductId = 0; + private int mDeviceBus = 0; private String mDescriptor = ""; private boolean mIsExternal = false; private int mSources = 0; @@ -604,6 +608,12 @@ public final class InputDevice implements Parcelable { return this; } + /** @see InputDevice#getDeviceBus() */ + public Builder setDeviceBus(int deviceBus) { + mDeviceBus = deviceBus; + return this; + } + /** @see InputDevice#getDescriptor() */ public Builder setDescriptor(String descriptor) { mDescriptor = descriptor; @@ -705,6 +715,7 @@ public final class InputDevice implements Parcelable { mName, mVendorId, mProductId, + mDeviceBus, mDescriptor, mIsExternal, mSources, @@ -846,6 +857,21 @@ public final class InputDevice implements Parcelable { } /** + * Gets the device bus used by given device, if available. + * <p> + * The device bus is the communication system used for transferring data + * (e.g. USB, Bluetooth etc.). This value comes from the kernel (from input.h). + * A value of 0 will be assigned where the device bus is not available. + * </p> + * + * @return The device bus of a given device + * @hide + */ + public int getDeviceBus() { + return mDeviceBus; + } + + /** * Gets the input device descriptor, which is a stable identifier for an input device. * <p> * An input device descriptor uniquely identifies an input device. Its value @@ -1444,6 +1470,7 @@ public final class InputDevice implements Parcelable { out.writeString(mName); out.writeInt(mVendorId); out.writeInt(mProductId); + out.writeInt(mDeviceBus); out.writeString(mDescriptor); out.writeInt(mIsExternal ? 1 : 0); out.writeInt(mSources); diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index ad326e41a146..a2f767d002f4 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -102,6 +102,7 @@ per-file ISurfaceControlViewHost*.aidl = file:/services/core/java/com/android/se per-file IWindow*.aidl = file:/services/core/java/com/android/server/wm/OWNERS per-file RemoteAnimation*.java = file:/services/core/java/com/android/server/wm/OWNERS per-file RemoteAnimation*.aidl = file:/services/core/java/com/android/server/wm/OWNERS +per-file ScreenRecordingCallbacks.java = file:/services/core/java/com/android/server/wm/OWNERS per-file *SurfaceControl*.java = file:/services/core/java/com/android/server/wm/OWNERS per-file SurfaceControl*.aidl = file:/services/core/java/com/android/server/wm/OWNERS per-file SurfaceSession.java = file:/services/core/java/com/android/server/wm/OWNERS diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS index 3fa376003123..fd73d35e00d2 100644 --- a/core/java/android/window/flags/OWNERS +++ b/core/java/android/window/flags/OWNERS @@ -1,2 +1,3 @@ per-file responsible_apis.aconfig = file:/BAL_OWNERS per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS +per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index f97d41b6a122..baea3bc3e04f 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -83,7 +83,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi deviceInfo.getId(), deviceInfo.getGeneration(), deviceInfo.getControllerNumber(), nameObj.get(), static_cast<int32_t>(ident.vendor), - static_cast<int32_t>(ident.product), descriptorObj.get(), + static_cast<int32_t>(ident.product), + static_cast<int32_t>(ident.bus), descriptorObj.get(), deviceInfo.isExternal(), deviceInfo.getSources(), deviceInfo.getKeyboardType(), kcmObj.get(), keyboardLanguageTagObj.get(), keyboardLayoutTypeObj.get(), @@ -113,7 +114,7 @@ int register_android_view_InputDevice(JNIEnv* env) gInputDeviceClassInfo.clazz = MakeGlobalRefOrDie(env, gInputDeviceClassInfo.clazz); gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>", - "(IIILjava/lang/String;IILjava/lang/" + "(IIILjava/lang/String;IIILjava/lang/" "String;ZIILandroid/view/KeyCharacterMap;Ljava/" "lang/String;Ljava/lang/String;ZZZZZIII)V"); diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 2a2e9038c27a..cc1649464626 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1828,7 +1828,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // If this zygote isn't root, it won't be able to create a process group, // since the directory is owned by root. - if (!is_system_server && getuid() == 0) { + if (getuid() == 0) { const int rc = createProcessGroup(uid, getpid()); if (rc != 0) { fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing " diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 04a70df6920f..5713e9ee1a80 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2237,7 +2237,7 @@ <!-- @SystemApi @hide Allows changing Thread network state and access to Thread network credentials such as Network Key and PSKc. <p>Not for use by third-party applications. - @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") --> + @FlaggedApi("com.android.net.thread.platform.flags.thread_enabled_platform") --> <permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED" android:protectionLevel="signature|privileged" /> @@ -2287,7 +2287,7 @@ <!-- Allows system apps to call methods to register itself as a mDNS offload engine. <p>Not for use by third-party or privileged applications. @SystemApi - @FlaggedApi("com.android.net.flags.register_nsd_offload_engine") + @FlaggedApi("android.net.platform.flags.register_nsd_offload_engine") @hide This should only be used by system apps. --> <permission android:name="android.permission.REGISTER_NSD_OFFLOAD_ENGINE" @@ -3524,7 +3524,7 @@ <!-- Allows an application to set policy related to <a href="https://www.threadgroup.org">Thread</a> network. - @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled") + @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK" android:protectionLevel="internal|role" /> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 11b827117aa3..bd9abec22325 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -21,12 +21,14 @@ import android.os.Build; import android.os.StrictMode; /** - * @hide This should not be made public in its present form because it - * assumes that private and secret key bytes are available and would - * preclude the use of hardware crypto. + * This class provides some constants and helper methods related to Android's Keystore service. + * This class was originally much larger, but its functionality was superseded by other classes. + * It now just contains a few remaining pieces for which the users haven't been updated yet. + * You may be looking for {@link java.security.KeyStore} instead. + * + * @hide */ public class KeyStore { - private static final String TAG = "KeyStore"; // ResponseCodes - see system/security/keystore/include/keystore/keystore.h @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -42,50 +44,6 @@ public class KeyStore { return KEY_STORE; } - /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public byte[] get(String key) { - return null; - } - - /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean delete(String key) { - return false; - } - - /** - * List uids of all keys that are auth bound to the current user. - * Only system is allowed to call this method. - * @hide - * @deprecated This function always returns null. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public int[] listUidsOfAuthBoundKeys() { - return null; - } - - - /** - * @hide - * @deprecated This function has no effect. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public boolean unlock(String password) { - return false; - } - - /** - * - * @return - * @deprecated This function always returns true. - * @hide - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public boolean isEmpty() { - return true; - } - /** * Add an authentication record to the keystore authorization table. * @@ -105,13 +63,4 @@ public class KeyStore { public void onDeviceOffBody() { AndroidKeyStoreMaintenance.onDeviceOffBody(); } - - /** - * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error - * code. - */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static KeyStoreException getKeyStoreException(int errorCode) { - return new KeyStoreException(-10000, "Should not be called."); - } } diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java index f50efd2c3328..5cffe46936a2 100644 --- a/keystore/java/android/security/keystore/KeyInfo.java +++ b/keystore/java/android/security/keystore/KeyInfo.java @@ -16,6 +16,7 @@ package android.security.keystore; +import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; @@ -81,6 +82,7 @@ public class KeyInfo implements KeySpec { private final @KeyProperties.AuthEnum int mUserAuthenticationType; private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware; private final boolean mUserAuthenticationValidWhileOnBody; + private final boolean mUnlockedDeviceRequired; private final boolean mTrustedUserPresenceRequired; private final boolean mInvalidatedByBiometricEnrollment; private final boolean mUserConfirmationRequired; @@ -107,6 +109,7 @@ public class KeyInfo implements KeySpec { @KeyProperties.AuthEnum int userAuthenticationType, boolean userAuthenticationRequirementEnforcedBySecureHardware, boolean userAuthenticationValidWhileOnBody, + boolean unlockedDeviceRequired, boolean trustedUserPresenceRequired, boolean invalidatedByBiometricEnrollment, boolean userConfirmationRequired, @@ -132,6 +135,7 @@ public class KeyInfo implements KeySpec { mUserAuthenticationRequirementEnforcedBySecureHardware = userAuthenticationRequirementEnforcedBySecureHardware; mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; + mUnlockedDeviceRequired = unlockedDeviceRequired; mTrustedUserPresenceRequired = trustedUserPresenceRequired; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mUserConfirmationRequired = userConfirmationRequired; @@ -275,6 +279,20 @@ public class KeyInfo implements KeySpec { } /** + * Returns {@code true} if the key is authorized to be used only when the device is unlocked. + * + * <p>This authorization applies only to secret key and private key operations. Public key + * operations are not restricted. + * + * @see KeyGenParameterSpec.Builder#setUnlockedDeviceRequired(boolean) + * @see KeyProtection.Builder#setUnlockedDeviceRequired(boolean) + */ + @FlaggedApi(android.security.Flags.FLAG_KEYINFO_UNLOCKED_DEVICE_REQUIRED) + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * Returns {@code true} if the key is authorized to be used only for messages confirmed by the * user. * diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java index 27ff50c743b1..22230916b084 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java @@ -90,6 +90,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { long userAuthenticationValidityDurationSeconds = 0; boolean userAuthenticationRequired = true; boolean userAuthenticationValidWhileOnBody = false; + boolean unlockedDeviceRequired = false; boolean trustedUserPresenceRequired = false; boolean trustedUserConfirmationRequired = false; int remainingUsageCount = KeyProperties.UNRESTRICTED_USAGE_COUNT; @@ -181,6 +182,9 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { + userAuthenticationValidityDurationSeconds + " seconds"); } break; + case KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED: + unlockedDeviceRequired = true; + break; case KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY: userAuthenticationValidWhileOnBody = KeyStore2ParameterUtils.isSecureHardware(a.securityLevel); @@ -254,6 +258,7 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi { : keymasterSwEnforcedUserAuthenticators, userAuthenticationRequirementEnforcedBySecureHardware, userAuthenticationValidWhileOnBody, + unlockedDeviceRequired, trustedUserPresenceRequired, invalidatedByBiometricEnrollment, trustedUserConfirmationRequired, diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index a8d170d00ef7..f4d6b8cd9989 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -218,7 +218,7 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } // Perform clipping - if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) { + if (props.getClipDamageToBounds()) { if (!frame->pendingDirty.intersect(SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { frame->pendingDirty.setEmpty(); } diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index f6907831e11f..bbbb0ca2ce6b 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -1031,6 +1031,7 @@ SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) { if (dirty->isEmpty()) { dirty->setIWH(frame.width(), frame.height()); + return *dirty; } // At this point dirty is the area of the window to update. However, diff --git a/packages/CrashRecovery/framework/Android.bp b/packages/CrashRecovery/framework/Android.bp index c0d93531a1e6..9480327253f4 100644 --- a/packages/CrashRecovery/framework/Android.bp +++ b/packages/CrashRecovery/framework/Android.bp @@ -3,7 +3,7 @@ soong_config_module_type { module_type: "filegroup", config_namespace: "ANDROID", bool_variables: [ - "move_crashrecovery_files", + "crashrecovery_files_in_platform", ], properties: [ "srcs", @@ -12,14 +12,13 @@ soong_config_module_type { platform_filegroup { name: "framework-crashrecovery-sources", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ], soong_config_variables: { - // if the flag is enabled, then files would be moved to module - move_crashrecovery_files: { - srcs: [], + // if this flag is enabled, then files are part of platform + crashrecovery_files_in_platform: { + srcs: [ + "java/**/*.java", + "java/**/*.aidl", + ], }, }, path: "java", @@ -31,7 +30,7 @@ soong_config_module_type { module_type: "filegroup", config_namespace: "ANDROID", bool_variables: [ - "move_crashrecovery_files", + "crashrecovery_files_in_module", ], properties: [ "srcs", @@ -40,10 +39,9 @@ soong_config_module_type { module_filegroup { name: "framework-crashrecovery-module-sources", - srcs: [], soong_config_variables: { - // if the flag is enabled, then files would be moved to module - move_crashrecovery_files: { + // if this flag is enabled, then files are part of module + crashrecovery_files_in_module: { srcs: [ "java/**/*.java", "java/**/*.aidl", diff --git a/packages/CrashRecovery/services/Android.bp b/packages/CrashRecovery/services/Android.bp index ab10b5a23676..961b41f4a633 100644 --- a/packages/CrashRecovery/services/Android.bp +++ b/packages/CrashRecovery/services/Android.bp @@ -3,7 +3,7 @@ soong_config_module_type { module_type: "filegroup", config_namespace: "ANDROID", bool_variables: [ - "move_crashrecovery_files", + "crashrecovery_files_in_platform", ], properties: [ "srcs", @@ -12,15 +12,14 @@ soong_config_module_type { platform_filegroup { name: "services-crashrecovery-sources", - srcs: [ - "java/**/*.java", - "java/**/*.aidl", - ":statslog-crashrecovery-java-gen", - ], soong_config_variables: { - // if the flag is enabled, then files would be moved to module - move_crashrecovery_files: { - srcs: [], + // if this flag is enabled, then files are part of platform + crashrecovery_files_in_platform: { + srcs: [ + "java/**/*.java", + "java/**/*.aidl", + ":statslog-crashrecovery-java-gen", + ], }, }, visibility: ["//frameworks/base:__subpackages__"], @@ -31,7 +30,7 @@ soong_config_module_type { module_type: "filegroup", config_namespace: "ANDROID", bool_variables: [ - "move_crashrecovery_files", + "crashrecovery_files_in_module", ], properties: [ "srcs", @@ -40,10 +39,9 @@ soong_config_module_type { module_filegroup { name: "services-crashrecovery-module-sources", - srcs: [], soong_config_variables: { - // if the flag is enabled, then files would be moved to module - move_crashrecovery_files: { + // if this flag is enabled, then files are part of module + crashrecovery_files_in_module: { srcs: [ "java/**/*.java", "java/**/*.aidl", diff --git a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java index b5cf011c32a6..5d71b7d98fdc 100644 --- a/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java +++ b/packages/CrashRecovery/services/java/com/android/server/PackageWatchdog.java @@ -38,13 +38,13 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.BackgroundThread; import android.util.LongArrayQueue; import android.util.Slog; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; @@ -100,13 +100,15 @@ public class PackageWatchdog { public static final int FAILURE_REASON_EXPLICIT_HEALTH_CHECK = 2; public static final int FAILURE_REASON_APP_CRASH = 3; public static final int FAILURE_REASON_APP_NOT_RESPONDING = 4; + public static final int FAILURE_REASON_BOOT_LOOP = 5; @IntDef(prefix = { "FAILURE_REASON_" }, value = { FAILURE_REASON_UNKNOWN, FAILURE_REASON_NATIVE_CRASH, FAILURE_REASON_EXPLICIT_HEALTH_CHECK, FAILURE_REASON_APP_CRASH, - FAILURE_REASON_APP_NOT_RESPONDING + FAILURE_REASON_APP_NOT_RESPONDING, + FAILURE_REASON_BOOT_LOOP }) @Retention(RetentionPolicy.SOURCE) public @interface FailureReasons {} @@ -542,7 +544,7 @@ public class PackageWatchdog { mNumberOfNativeCrashPollsRemaining--; // Check if native watchdog reported a crash if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) { - // We rollback everything available when crash is unattributable + // We rollback all available low impact rollbacks when crash is unattributable onPackageFailure(Collections.EMPTY_LIST, FAILURE_REASON_NATIVE_CRASH); // we stop polling after an attempt to execute rollback, regardless of whether the // attempt succeeds or not @@ -572,6 +574,7 @@ public class PackageWatchdog { PackageHealthObserverImpact.USER_IMPACT_LEVEL_30, PackageHealthObserverImpact.USER_IMPACT_LEVEL_50, PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, PackageHealthObserverImpact.USER_IMPACT_LEVEL_100}) public @interface PackageHealthObserverImpact { /** No action to take. */ @@ -582,6 +585,7 @@ public class PackageWatchdog { int USER_IMPACT_LEVEL_30 = 30; int USER_IMPACT_LEVEL_50 = 50; int USER_IMPACT_LEVEL_70 = 70; + int USER_IMPACT_LEVEL_90 = 90; /* Action has high user impact, a last resort, user of a device will be very frustrated. */ int USER_IMPACT_LEVEL_100 = 100; } diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java index dd74a2a978b2..0fb932735ab4 100644 --- a/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/packages/CrashRecovery/services/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -28,6 +28,7 @@ import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; +import android.crashrecovery.flags.Flags; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -45,7 +46,6 @@ import com.android.server.PackageWatchdog; import com.android.server.PackageWatchdog.FailureReasons; import com.android.server.PackageWatchdog.PackageHealthObserver; import com.android.server.PackageWatchdog.PackageHealthObserverImpact; -import com.android.server.SystemConfig; import com.android.server.crashrecovery.proto.CrashRecoveryStatsLog; import com.android.server.pm.ApexManager; @@ -57,6 +57,7 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -74,6 +75,9 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { private static final int PERSISTENT_MASK = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; + private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = + "persist.device_config.configuration.disable_high_impact_rollback"; + private final Context mContext; private final Handler mHandler; private final ApexManager mApexManager; @@ -84,7 +88,8 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { // True if needing to roll back only rebootless apexes when native crash happens private boolean mTwoPhaseRollbackEnabled; - RollbackPackageHealthObserver(Context context) { + @VisibleForTesting + RollbackPackageHealthObserver(Context context, ApexManager apexManager) { mContext = context; HandlerThread handlerThread = new HandlerThread("RollbackPackageHealthObserver"); handlerThread.start(); @@ -94,7 +99,7 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { mLastStagedRollbackIdsFile = new File(dataDir, "last-staged-rollback-ids"); mTwoPhaseRollbackEnabledFile = new File(dataDir, "two-phase-rollback-enabled"); PackageWatchdog.getInstance(mContext).registerHealthObserver(this); - mApexManager = ApexManager.getInstance(); + mApexManager = apexManager; if (SystemProperties.getBoolean("sys.boot_completed", false)) { // Load the value from the file if system server has crashed and restarted @@ -107,24 +112,46 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } } + RollbackPackageHealthObserver(Context context) { + this(context, ApexManager.getInstance()); + } + @Override public int onHealthCheckFailed(@Nullable VersionedPackage failedPackage, @FailureReasons int failureReason, int mitigationCount) { - boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) - .getAvailableRollbacks().isEmpty(); int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; - - if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH - && anyRollbackAvailable) { - // For native crashes, we will directly roll back any available rollbacks - // Note: For non-native crashes the rollback-all step has higher impact - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; - } else if (getAvailableRollback(failedPackage) != null) { - // Rollback is available, we may get a callback into #execute - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; - } else if (anyRollbackAvailable) { - // If any rollbacks are available, we will commit them - impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (!lowImpactRollbacks.isEmpty()) { + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + // For native crashes, we will directly roll back any available rollbacks at low + // impact level + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getRollbackForPackage(failedPackage, lowImpactRollbacks) != null) { + // Rollback is available for crashing low impact package + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else { + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } + } + } else { + boolean anyRollbackAvailable = !mContext.getSystemService(RollbackManager.class) + .getAvailableRollbacks().isEmpty(); + + if (failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH + && anyRollbackAvailable) { + // For native crashes, we will directly roll back any available rollbacks + // Note: For non-native crashes the rollback-all step has higher impact + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (getAvailableRollback(failedPackage) != null) { + // Rollback is available, we may get a callback into #execute + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30; + } else if (anyRollbackAvailable) { + // If any rollbacks are available, we will commit them + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + } } return impact; @@ -133,16 +160,34 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @Override public boolean execute(@Nullable VersionedPackage failedPackage, @FailureReasons int rollbackReason, int mitigationCount) { - if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { - mHandler.post(() -> rollbackAll(rollbackReason)); - return true; - } + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + return true; + } - RollbackInfo rollback = getAvailableRollback(failedPackage); - if (rollback != null) { - mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackInfo rollback = getRollbackForPackage(failedPackage, lowImpactRollbacks); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else if (!lowImpactRollbacks.isEmpty()) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } } else { - mHandler.post(() -> rollbackAll(rollbackReason)); + if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) { + mHandler.post(() -> rollbackAll(rollbackReason)); + return true; + } + + RollbackInfo rollback = getAvailableRollback(failedPackage); + if (rollback != null) { + mHandler.post(() -> rollbackPackage(rollback, failedPackage, rollbackReason)); + } else { + mHandler.post(() -> rollbackAll(rollbackReason)); + } } // Assume rollbacks executed successfully @@ -150,6 +195,31 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } @Override + public int onBootLoop(int mitigationCount) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + if (!availableRollbacks.isEmpty()) { + impact = getUserImpactBasedOnRollbackImpactLevel(availableRollbacks); + } + } + return impact; + } + + @Override + public boolean executeBootLoopMitigation(int mitigationCount) { + if (Flags.recoverabilityDetection()) { + List<RollbackInfo> availableRollbacks = getAvailableRollbacks(); + + triggerLeastImpactLevelRollback(availableRollbacks, + PackageWatchdog.FAILURE_REASON_BOOT_LOOP); + return true; + } + return false; + } + + + @Override public String getName() { return NAME; } @@ -161,13 +231,16 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @Override public boolean mayObservePackage(String packageName) { - if (mContext.getSystemService(RollbackManager.class) - .getAvailableRollbacks().isEmpty()) { + if (getAvailableRollbacks().isEmpty()) { return false; } return isPersistentSystemApp(packageName); } + private List<RollbackInfo> getAvailableRollbacks() { + return mContext.getSystemService(RollbackManager.class).getAvailableRollbacks(); + } + private boolean isPersistentSystemApp(@NonNull String packageName) { PackageManager pm = mContext.getPackageManager(); try { @@ -272,6 +345,40 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { return null; } + @AnyThread + private RollbackInfo getRollbackForPackage(@Nullable VersionedPackage failedPackage, + List<RollbackInfo> availableRollbacks) { + if (failedPackage == null) { + return null; + } + + for (RollbackInfo rollback : availableRollbacks) { + for (PackageRollbackInfo packageRollback : rollback.getPackages()) { + if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) { + return rollback; + } + // TODO(b/147666157): Extract version number of apk-in-apex so that we don't have + // to rely on complicated reasoning as below + + // Due to b/147666157, for apk in apex, we do not know the version we are rolling + // back from. But if a package X is embedded in apex A exclusively (not embedded in + // any other apex), which is not guaranteed, then it is sufficient to check only + // package names here, as the version of failedPackage and the PackageRollbackInfo + // can't be different. If failedPackage has a higher version, then it must have + // been updated somehow. There are two ways: it was updated by an update of apex A + // or updated directly as apk. In both cases, this rollback would have gotten + // expired when onPackageReplaced() was called. Since the rollback exists, it has + // same version as failedPackage. + if (packageRollback.isApkInApex() + && packageRollback.getVersionRolledBackFrom().getPackageName() + .equals(failedPackage.getPackageName())) { + return rollback; + } + } + } + return null; + } + /** * Returns {@code true} if staged session associated with {@code rollbackId} was marked * as handled, {@code false} if already handled. @@ -396,12 +503,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { @FailureReasons int rollbackReason) { assertInWorkerThread(); - if (isAutomaticRollbackDenied(SystemConfig.getInstance(), failedPackage)) { - Slog.d(TAG, "Automatic rollback not allowed for package " - + failedPackage.getPackageName()); - return; - } - final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class); int reasonToLog = WatchdogRollbackLogger.mapFailureReasonToMetric(rollbackReason); final String failedPackageToLog; @@ -465,17 +566,6 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } /** - * Returns true if this package is not eligible for automatic rollback. - */ - @VisibleForTesting - @AnyThread - public static boolean isAutomaticRollbackDenied(SystemConfig systemConfig, - VersionedPackage versionedPackage) { - return systemConfig.getAutomaticRollbackDenylistedPackages() - .contains(versionedPackage.getPackageName()); - } - - /** * Two-phase rollback: * 1. roll back rebootless apexes first * 2. roll back all remaining rollbacks if native crash doesn't stop after (1) is done @@ -495,14 +585,66 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { boolean found = false; for (RollbackInfo rollback : rollbacks) { if (isRebootlessApex(rollback)) { - VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); - rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH); found = true; } } return found; } + /** + * Rollback the package that has minimum rollback impact level. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + private void triggerLeastImpactLevelRollback(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + int minRollbackImpactLevel = getMinRollbackImpactLevel(availableRollbacks); + + if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_LOW) { + // Apply all available low impact rollbacks. + mHandler.post(() -> rollbackAllLowImpact(availableRollbacks, rollbackReason)); + } else if (minRollbackImpactLevel == PackageManager.ROLLBACK_USER_IMPACT_HIGH) { + // Check disable_high_impact_rollback device config before performing rollback + if (SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { + return; + } + // Rollback one package at a time. If that doesn't resolve the issue, rollback + // next with same impact level. + mHandler.post(() -> rollbackHighImpact(availableRollbacks, rollbackReason)); + } + } + + /** + * sort the available high impact rollbacks by first package name to have a deterministic order. + * Apply the first available rollback. + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollback + */ + @WorkerThread + private void rollbackHighImpact(List<RollbackInfo> availableRollbacks, + @FailureReasons int rollbackReason) { + assertInWorkerThread(); + List<RollbackInfo> highImpactRollbacks = + getRollbacksAvailableForImpactLevel( + availableRollbacks, PackageManager.ROLLBACK_USER_IMPACT_HIGH); + + // sort rollbacks based on package name of the first package. This is to have a + // deterministic order of rollbacks. + List<RollbackInfo> sortedHighImpactRollbacks = highImpactRollbacks.stream().sorted( + Comparator.comparing(a -> a.getPackages().get(0).getPackageName())).toList(); + VersionedPackage firstRollback = + sortedHighImpactRollbacks + .get(0) + .getPackages() + .get(0) + .getVersionRolledBackFrom(); + rollbackPackage(sortedHighImpactRollbacks.get(0), firstRollback, rollbackReason); + } + @WorkerThread private void rollbackAll(@FailureReasons int rollbackReason) { assertInWorkerThread(); @@ -522,8 +664,79 @@ final class RollbackPackageHealthObserver implements PackageHealthObserver { } for (RollbackInfo rollback : rollbacks) { - VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom(); - rollbackPackage(rollback, sample, rollbackReason); + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); + } + } + + /** + * Rollback all available low impact rollbacks + * @param availableRollbacks all available rollbacks + * @param rollbackReason reason to rollbacks + */ + @WorkerThread + private void rollbackAllLowImpact( + List<RollbackInfo> availableRollbacks, @FailureReasons int rollbackReason) { + assertInWorkerThread(); + + List<RollbackInfo> lowImpactRollbacks = getRollbacksAvailableForImpactLevel( + availableRollbacks, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + if (useTwoPhaseRollback(lowImpactRollbacks)) { + return; + } + + Slog.i(TAG, "Rolling back all available low impact rollbacks"); + // Add all rollback ids to mPendingStagedRollbackIds, so that we do not reboot before all + // pending staged rollbacks are handled. + for (RollbackInfo rollback : lowImpactRollbacks) { + if (rollback.isStaged()) { + mPendingStagedRollbackIds.add(rollback.getRollbackId()); + } } + + for (RollbackInfo rollback : lowImpactRollbacks) { + VersionedPackage firstRollback = + rollback.getPackages().get(0).getVersionRolledBackFrom(); + rollbackPackage(rollback, firstRollback, rollbackReason); + } + } + + private List<RollbackInfo> getRollbacksAvailableForImpactLevel( + List<RollbackInfo> availableRollbacks, int impactLevel) { + return availableRollbacks.stream() + .filter(rollbackInfo -> rollbackInfo.getRollbackImpactLevel() == impactLevel) + .toList(); + } + + private int getMinRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + return availableRollbacks.stream() + .mapToInt(RollbackInfo::getRollbackImpactLevel) + .min() + .orElse(-1); + } + + private int getUserImpactBasedOnRollbackImpactLevel(List<RollbackInfo> availableRollbacks) { + int impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + int minImpact = getMinRollbackImpactLevel(availableRollbacks); + switch (minImpact) { + case PackageManager.ROLLBACK_USER_IMPACT_LOW: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70; + break; + case PackageManager.ROLLBACK_USER_IMPACT_HIGH: + if (!SystemProperties.getBoolean(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, false)) { + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_90; + } + break; + default: + impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_0; + } + return impact; + } + + @VisibleForTesting + Handler getHandler() { + return mHandler; } } diff --git a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java index 898c5439a293..519c0edfc532 100644 --- a/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java +++ b/packages/CrashRecovery/services/java/com/android/server/rollback/WatchdogRollbackLogger.java @@ -18,6 +18,7 @@ package com.android.server.rollback; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; +import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH; import static com.android.server.crashrecovery.proto.CrashRecoveryStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT; @@ -258,6 +259,8 @@ public final class WatchdogRollbackLogger { return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH; case PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING: return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING; + case PackageWatchdog.FAILURE_REASON_BOOT_LOOP: + return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_BOOT_LOOPING; default: return WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN; } diff --git a/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java b/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java new file mode 100644 index 000000000000..a6ae68f62f10 --- /dev/null +++ b/packages/CrashRecovery/services/java/com/android/util/BackgroundThread.java @@ -0,0 +1,103 @@ +/* + * * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.NonNull; +import android.os.Handler; +import android.os.HandlerThread; + +import com.android.internal.annotations.GuardedBy; + +import java.util.concurrent.Executor; + +/** + * Thread for asynchronous event processing. This thread is configured as + * {@link android.os.Process#THREAD_PRIORITY_BACKGROUND}, which means fewer CPU + * resources will be dedicated to it, and it will "have less chance of impacting + * the responsiveness of the user interface." + * <p> + * This thread is best suited for tasks that the user is not actively waiting + * for, or for tasks that the user expects to be executed eventually. + * + * @see com.android.internal.os.BackgroundThread + * + * TODO: b/326916057 depend on modules-utils-backgroundthread instead + * @hide + */ +public final class BackgroundThread extends HandlerThread { + private static final Object sLock = new Object(); + + @GuardedBy("sLock") + private static BackgroundThread sInstance; + @GuardedBy("sLock") + private static Handler sHandler; + @GuardedBy("sLock") + private static HandlerExecutor sHandlerExecutor; + + private BackgroundThread() { + super(BackgroundThread.class.getName(), android.os.Process.THREAD_PRIORITY_BACKGROUND); + } + + @GuardedBy("sLock") + private static void ensureThreadLocked() { + if (sInstance == null) { + sInstance = new BackgroundThread(); + sInstance.start(); + sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); + } + } + + /** + * Get the singleton instance of this class. + * + * @return the singleton instance of this class + */ + @NonNull + public static BackgroundThread get() { + synchronized (sLock) { + ensureThreadLocked(); + return sInstance; + } + } + + /** + * Get the singleton {@link Handler} for this class. + * + * @return the singleton {@link Handler} for this class. + */ + @NonNull + public static Handler getHandler() { + synchronized (sLock) { + ensureThreadLocked(); + return sHandler; + } + } + + /** + * Get the singleton {@link Executor} for this class. + * + * @return the singleton {@link Executor} for this class. + */ + @NonNull + public static Executor getExecutor() { + synchronized (sLock) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } +} diff --git a/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java b/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java new file mode 100644 index 000000000000..948ebcca0263 --- /dev/null +++ b/packages/CrashRecovery/services/java/com/android/util/HandlerExecutor.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +import android.annotation.NonNull; +import android.os.Handler; + +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.concurrent.RejectedExecutionException; + +/** + * An adapter {@link Executor} that posts all executed tasks onto the given + * {@link Handler}. + * + * TODO: b/326916057 depend on modules-utils-backgroundthread instead + * @hide + */ +public class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(@NonNull Handler handler) { + mHandler = Objects.requireNonNull(handler); + } + + @Override + public void execute(Runnable command) { + if (!mHandler.post(command)) { + throw new RejectedExecutionException(mHandler + " is shutting down"); + } + } +} diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 8cb25dc637d4..bb05a75d8306 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -368,7 +368,7 @@ public class PackageInstallerActivity extends AlertActivity { final SessionInfo info = mInstaller.getSessionInfo(sessionId); String resolvedPath = info != null ? info.getResolvedBaseApkPath() : null; if (info == null || !info.isSealed() || resolvedPath == null) { - Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); + Log.w(TAG, "Session " + sessionId + " in funky state; ignoring"); finish(); return; } @@ -383,7 +383,7 @@ public class PackageInstallerActivity extends AlertActivity { -1 /* defaultValue */); final SessionInfo info = mInstaller.getSessionInfo(sessionId); if (info == null || !info.isPreApprovalRequested()) { - Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); + Log.w(TAG, "Session " + sessionId + " in funky state; ignoring"); finish(); return; } @@ -797,7 +797,9 @@ public class PackageInstallerActivity extends AlertActivity { // work for the multiple user case, i.e. the caller task user and started // Activity user are not the same. To avoid having multiple PIAs in the task, // finish the current PackageInstallerActivity - finish(); + // Because finish() is overridden to set the installation result, we must use + // the original finish() method, or the confirmation dialog fails to appear. + PackageInstallerActivity.super.finish(); } }, 500); diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java index 56b940c534fb..1073f610bff4 100644 --- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java +++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java @@ -300,6 +300,8 @@ public final class RingtonePickerActivity extends AlertActivity implements } }; installTask.execute(data.getData()); + } else if (requestCode == ADD_FILE_REQUEST_CODE && resultCode == RESULT_CANCELED) { + setupAlert(); } } diff --git a/packages/SystemUI/src/com/android/systemui/media/OWNERS b/packages/SystemUI/src/com/android/systemui/media/OWNERS index 69ea57bfd397..5eb14fc40327 100644 --- a/packages/SystemUI/src/com/android/systemui/media/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/media/OWNERS @@ -1 +1,9 @@ -per-file MediaProjectionPermissionActivity.java = michaelwr@google.com +# Bug component: 78010 + +asc@google.com +ethibodeau@google.com +michaelmikhil@google.com + +# Audio team +per-file RingtonePlayer.java = file:/services/core/java/com/android/server/audio/OWNERS +per-file NotificationPlayer.java = file:/services/core/java/com/android/server/audio/OWNERS diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS new file mode 100644 index 000000000000..95b8fa74feeb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 1280508 + +# Files in this directory should still be reviewed by a member of SystemUI team diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/OWNERS b/packages/SystemUI/src/com/android/systemui/mediaprojection/OWNERS new file mode 100644 index 000000000000..bd7407729774 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/OWNERS @@ -0,0 +1,5 @@ +# Bug component: 1345447 + +include /media/java/android/media/projection/OWNERS +chrisgollner@google.com +nickchameyev@google.com diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6dbf70754b3b..4a5d84a361ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; +import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -376,7 +377,7 @@ public class PhoneStatusBarPolicy } @Override - public void onConfigChanged(ZenModeConfig config) { + public void onConsolidatedPolicyChanged(NotificationManager.Policy policy) { updateVolumeZen(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS new file mode 100644 index 000000000000..e6f218f85b1f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/OWNERS @@ -0,0 +1 @@ +include /packages/SystemUI/src/com/android/systemui/media/OWNERS diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OWNERS new file mode 100644 index 000000000000..100dd2e9cbb8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/OWNERS @@ -0,0 +1 @@ +include /packages/SystemUI/src/com/android/systemui/media/dialog/OWNERS diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/OWNERS new file mode 100644 index 000000000000..f87d93a24939 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/OWNERS @@ -0,0 +1 @@ +include /packages/SystemUI/src/com/android/systemui/mediaprojection/OWNERS diff --git a/rs/java/android/renderscript/ScriptC.java b/rs/java/android/renderscript/ScriptC.java index 67c2caa338a6..e9f6031e5048 100644 --- a/rs/java/android/renderscript/ScriptC.java +++ b/rs/java/android/renderscript/ScriptC.java @@ -38,12 +38,12 @@ public class ScriptC extends Script { private static final String TAG = "ScriptC"; /** - * In targetSdkVersion 35 and above, Renderscript's ScriptC stops being supported + * In targetSdkVersion 36 and above, Renderscript's ScriptC stops being supported * and an exception is thrown when the class is instantiated. - * In targetSdkVersion 34 and below, Renderscript's ScriptC still works. + * In targetSdkVersion 35 and below, Renderscript's ScriptC still works. */ @ChangeId - @EnabledAfter(targetSdkVersion = 35) + @EnabledAfter(targetSdkVersion = 36) private static final long RENDERSCRIPT_SCRIPTC_DEPRECATION_CHANGE_ID = 297019750L; /** @@ -101,9 +101,21 @@ public class ScriptC extends Script { setID(id); } - private static void throwExceptionIfSDKTooHigh() { + private static void throwExceptionIfScriptCUnsupported() { + // Checks that this device actually does have an ABI that supports ScriptC. + // + // For an explanation as to why `System.loadLibrary` is used, see discussion at + // https://android-review.googlesource.com/c/platform/frameworks/base/+/2957974/comment/2f908b80_a05292ee + try { + System.loadLibrary("RS"); + } catch (UnsatisfiedLinkError e) { + String s = "This device does not have an ABI that supports ScriptC."; + throw new UnsupportedOperationException(s); + } + + // Throw an exception if the target API is 36 or above String message = - "ScriptC scripts are not supported when targeting an API Level >= 35. Please refer " + "ScriptC scripts are not supported when targeting an API Level >= 36. Please refer " + "to https://developer.android.com/guide/topics/renderscript/migration-guide " + "for proposed alternatives."; Slog.w(TAG, message); @@ -113,7 +125,7 @@ public class ScriptC extends Script { } private static synchronized long internalCreate(RenderScript rs, Resources resources, int resourceID) { - throwExceptionIfSDKTooHigh(); + throwExceptionIfScriptCUnsupported(); byte[] pgm; int pgmLength; InputStream is = resources.openRawResource(resourceID); @@ -150,7 +162,7 @@ public class ScriptC extends Script { private static synchronized long internalStringCreate(RenderScript rs, String resName, byte[] bitcode) { // Log.v(TAG, "Create script for resource = " + resName); - throwExceptionIfSDKTooHigh(); + throwExceptionIfScriptCUnsupported(); return rs.nScriptCCreate(resName, RenderScript.getCachePath(), bitcode, bitcode.length); } } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index b04c7c52efbd..31db840dac00 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -326,7 +326,6 @@ public class SystemConfig { private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>(); private final ArraySet<String> mRollbackWhitelistedPackages = new ArraySet<>(); - private final ArraySet<String> mAutomaticRollbackDenylistedPackages = new ArraySet<>(); private final ArraySet<String> mWhitelistedStagedInstallers = new ArraySet<>(); // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. @@ -475,10 +474,6 @@ public class SystemConfig { return mRollbackWhitelistedPackages; } - public Set<String> getAutomaticRollbackDenylistedPackages() { - return mAutomaticRollbackDenylistedPackages; - } - public Set<String> getWhitelistedStagedInstallers() { return mWhitelistedStagedInstallers; } @@ -1396,16 +1391,6 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; - case "automatic-rollback-denylisted-app": { - String pkgname = parser.getAttributeValue(null, "package"); - if (pkgname == null) { - Slog.w(TAG, "<" + name + "> without package in " + permFile - + " at " + parser.getPositionDescription()); - } else { - mAutomaticRollbackDenylistedPackages.add(pkgname); - } - XmlUtils.skipCurrentTag(parser); - } break; case "whitelisted-staged-installer": { if (allowAppConfigs) { String pkgname = parser.getAttributeValue(null, "package"); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 2464eb0141b8..a454a36e19d6 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1940,7 +1940,7 @@ public class DisplayDeviceConfig { final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint(); final int size = points.size(); - if (size <= 0) { + if (size == 0) { return null; } diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java index 08e597701ea2..8e2484e616ab 100644 --- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java +++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java @@ -369,15 +369,15 @@ public final class KeyboardMetricsCollector { if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { return; } - int vendorId = inputDevice.getVendorId(); - int productId = inputDevice.getProductId(); if (keyboardSystemEvent == null) { Slog.w(TAG, "Invalid keyboard event logging, keycode = " + Arrays.toString(keyCodes) + ", modifier state = " + modifierState); return; } FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, - vendorId, productId, keyboardSystemEvent.getIntValue(), keyCodes, modifierState); + inputDevice.getVendorId(), inputDevice.getProductId(), + keyboardSystemEvent.getIntValue(), keyCodes, modifierState, + inputDevice.getDeviceBus()); if (DEBUG) { Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemEvent.mName); @@ -402,7 +402,7 @@ public final class KeyboardMetricsCollector { // Push the atom to Statsd FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED, event.isFirstConfiguration(), event.getVendorId(), event.getProductId(), - proto.getBytes()); + proto.getBytes(), event.getDeviceBus()); if (DEBUG) { Slog.d(TAG, "Logging Keyboard configuration event: " + event); @@ -467,6 +467,10 @@ public final class KeyboardMetricsCollector { return mInputDevice.getProductId(); } + public int getDeviceBus() { + return mInputDevice.getDeviceBus(); + } + public boolean isFirstConfiguration() { return mIsFirstConfiguration; } @@ -479,6 +483,7 @@ public final class KeyboardMetricsCollector { public String toString() { return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId()) + ", ProductId = " + Integer.toHexString(getProductId()) + + ", Device Bus = " + Integer.toHexString(getDeviceBus()) + "}, isFirstConfiguration = " + mIsFirstConfiguration + ", LayoutConfigurations = " + mLayoutConfigurations; } diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 115421db4d31..bcbd36c95491 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -1321,7 +1321,9 @@ public class LocationManagerService extends ILocationManager.Stub implements "setAutomotiveGnssSuspended only allowed on automotive devices"); } - mGnssManagerService.setAutomotiveGnssSuspended(suspended); + if (mGnssManagerService != null) { + mGnssManagerService.setAutomotiveGnssSuspended(suspended); + } } @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_AUTOMOTIVE_GNSS) @@ -1336,7 +1338,10 @@ public class LocationManagerService extends ILocationManager.Stub implements "isAutomotiveGnssSuspended only allowed on automotive devices"); } - return mGnssManagerService.isAutomotiveGnssSuspended(); + if (mGnssManagerService != null) { + return mGnssManagerService.isAutomotiveGnssSuspended(); + } + return false; } @Override diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index c11356b7308e..44c4b57a52c9 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -339,8 +339,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { static final String TAG = NetworkPolicyLogger.TAG; private static final boolean LOGD = NetworkPolicyLogger.LOGD; private static final boolean LOGV = NetworkPolicyLogger.LOGV; - // TODO: b/304347838 - Remove once the feature is in staging. - private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false; /** * No opportunistic quota could be calculated from user data plan or data settings. @@ -728,7 +726,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Map of uid -> UidStateCallbackInfo objects holding the data received from * {@link IUidObserver#onUidStateChanged(int, int, long, int)} callbacks. In order to avoid * creating a new object for every callback received, we hold onto the object created for each - * uid and reuse it. + * uid and reuse it until the uid stays alive. * * Note that the lock used for accessing this object should not be used for anything else and we * should not be acquiring new locks or doing any heavy work while this lock is held since this @@ -801,6 +799,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { Clock.systemUTC()); } + @VisibleForTesting + UidState getUidStateForTest(int uid) { + synchronized (mUidRulesFirstLock) { + return mUidState.get(uid); + } + } + static class Dependencies { final Context mContext; final NetworkStatsManager mNetworkStatsManager; @@ -1062,8 +1067,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // The flag is boot-stable. - mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK - && Flags.networkBlockedForTopSleepingAndAbove(); + mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove(); if (mBackgroundNetworkRestricted) { // Firewall rules and UidBlockedState will get updated in // updateRulesForGlobalChangeAL below. @@ -1256,6 +1260,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override public void onUidGone(int uid, boolean disabled) { + synchronized (mUidStateCallbackInfos) { + mUidStateCallbackInfos.remove(uid); + } + // TODO: b/327058756 - Remove any pending UID_MSG_STATE_CHANGED on the handler. mUidEventHandler.obtainMessage(UID_MSG_GONE, uid, 0).sendToTarget(); } }; @@ -5899,7 +5907,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { void handleUidGone(int uid) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidGone"); try { - boolean updated; + final boolean updated; synchronized (mUidRulesFirstLock) { updated = removeUidStateUL(uid); } diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java index cc26c9b5f76c..9159851a5470 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java +++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java @@ -538,9 +538,10 @@ class PackageDynamicCodeLoading extends AbstractStatsBase<Void> { } else { if (fileInfo.mUserId != userId) { // This should be impossible: private app files are always user-specific and - // can't be accessed from different users. - throw new IllegalArgumentException("Cannot change userId for '" + path - + "' from " + fileInfo.mUserId + " to " + userId); + // can't be accessed from different users. But it does very occasionally happen + // (b/323665257). Ignore such cases - we shouldn't record data from a different + // user. + return false; } // Changing file type (i.e. loading the same file in different ways is possible if // unlikely. We allow it but ignore it. diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index d96fc332ce5a..e5cdf4577408 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -1212,13 +1212,20 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba rollback.makeAvailable(); mPackageHealthObserver.notifyRollbackAvailable(rollback.info); - // TODO(zezeozue): Provide API to explicitly start observing instead - // of doing this for all rollbacks. If we do this for all rollbacks, - // should document in PackageInstaller.SessionParams#setEnableRollback - // After enabling and committing any rollback, observe packages and - // prepare to rollback if packages crashes too frequently. - mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), - mRollbackLifetimeDurationInMillis); + if (Flags.recoverabilityDetection()) { + if (rollback.info.getRollbackImpactLevel() == PackageManager.ROLLBACK_USER_IMPACT_LOW) { + // TODO(zezeozue): Provide API to explicitly start observing instead + // of doing this for all rollbacks. If we do this for all rollbacks, + // should document in PackageInstaller.SessionParams#setEnableRollback + // After enabling and committing any rollback, observe packages and + // prepare to rollback if packages crashes too frequently. + mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), + mRollbackLifetimeDurationInMillis); + } + } else { + mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(), + mRollbackLifetimeDurationInMillis); + } runExpiration(); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 9375b297be2b..ecdf09f698d1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -2016,6 +2016,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub ownerTask.addChild(taskFragment, position); taskFragment.setWindowingMode(creationParams.getWindowingMode()); if (!creationParams.getInitialRelativeBounds().isEmpty()) { + // The surface operations for the task fragment should sync with the transition. + // This avoid using pending transaction before collectExistenceChange is called. + if (transition != null) { + addToSyncSet(transition.getSyncId(), taskFragment); + } // Set relative bounds instead of using setBounds. This will avoid unnecessary update in // case the parent has resized since the last time parent info is sent to the organizer. taskFragment.setRelativeEmbeddedBounds(creationParams.getInitialRelativeBounds()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index b14d37df9892..aab14adecf9d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -483,7 +483,6 @@ import com.android.internal.widget.PasswordValidationError; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.net.module.util.ProxyUtils; -import com.android.net.thread.flags.Flags; import com.android.server.AlarmManagerInternal; import com.android.server.LocalManagerRegistry; import com.android.server.LocalServices; @@ -13836,7 +13835,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS}); USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS}); - if (Flags.threadUserRestrictionEnabled()) { + if (com.android.net.thread.platform.flags.Flags.threadUserRestrictionEnabled()) { USER_RESTRICTION_PERMISSIONS.put( UserManager.DISALLOW_THREAD_NETWORK, new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK}); diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java index a14073006c31..6e416857abbf 100644 --- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java @@ -23,7 +23,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; @@ -33,14 +41,18 @@ import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; -import android.util.Log; -import android.util.Xml; +import android.crashrecovery.flags.Flags; +import android.os.Handler; +import android.os.MessageQueue; +import android.os.SystemProperties; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.runner.AndroidJUnit4; import com.android.dx.mockito.inline.extended.ExtendedMockito; import com.android.server.PackageWatchdog; import com.android.server.SystemConfig; +import com.android.server.pm.ApexManager; import org.junit.After; import org.junit.Before; @@ -49,18 +61,17 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; -import org.xmlpull.v1.XmlPullParser; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; +import java.time.Duration; +import java.util.HashMap; import java.util.List; -import java.util.Scanner; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @@ -78,12 +89,23 @@ public class RollbackPackageHealthObserverTest { @Mock PackageManager mMockPackageManager; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private ApexManager mApexManager; + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private HashMap<String, String> mSystemSettingsMap; private MockitoSession mSession; private static final String APP_A = "com.package.a"; private static final String APP_B = "com.package.b"; + private static final String APP_C = "com.package.c"; private static final long VERSION_CODE = 1L; + private static final long VERSION_CODE_2 = 2L; private static final String LOG_TAG = "RollbackPackageHealthObserverTest"; + private static final String PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG = + "persist.device_config.configuration.disable_high_impact_rollback"; + private SystemConfig mSysConfig; @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); @@ -96,12 +118,34 @@ public class RollbackPackageHealthObserverTest { .initMocks(this) .strictness(Strictness.LENIENT) .spyStatic(PackageWatchdog.class) + .spyStatic(SystemProperties.class) .startMocking(); + mSystemSettingsMap = new HashMap<>(); // Mock PackageWatchdog doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog) .when(() -> PackageWatchdog.getInstance(mMockContext)); + // Mock SystemProperties setter and various getters + doAnswer((Answer<Void>) invocationOnMock -> { + String key = invocationOnMock.getArgument(0); + String value = invocationOnMock.getArgument(1); + + mSystemSettingsMap.put(key, value); + return null; + } + ).when(() -> SystemProperties.set(anyString(), anyString())); + + doAnswer((Answer<Boolean>) invocationOnMock -> { + String key = invocationOnMock.getArgument(0); + boolean defaultValue = invocationOnMock.getArgument(1); + + String storedValue = mSystemSettingsMap.get(key); + return storedValue == null ? defaultValue : Boolean.parseBoolean(storedValue); + } + ).when(() -> SystemProperties.getBoolean(anyString(), anyBoolean())); + + SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(false)); } @After @@ -121,7 +165,7 @@ public class RollbackPackageHealthObserverTest { @Test public void testHealthCheckLevels() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); VersionedPackage testFailedPackage = new VersionedPackage(APP_A, VERSION_CODE); VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); @@ -165,14 +209,14 @@ public class RollbackPackageHealthObserverTest { @Test public void testIsPersistent() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); assertTrue(observer.isPersistent()); } @Test public void testMayObservePackage_withoutAnyRollback() { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of()); assertFalse(observer.mayObservePackage(APP_A)); @@ -182,7 +226,7 @@ public class RollbackPackageHealthObserverTest { public void testMayObservePackage_forPersistentApp() throws PackageManager.NameNotFoundException { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); ApplicationInfo info = new ApplicationInfo(); info.flags = ApplicationInfo.FLAG_PERSISTENT | ApplicationInfo.FLAG_SYSTEM; when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); @@ -197,7 +241,7 @@ public class RollbackPackageHealthObserverTest { public void testMayObservePackage_forNonPersistentApp() throws PackageManager.NameNotFoundException { RollbackPackageHealthObserver observer = - spy(new RollbackPackageHealthObserver(mMockContext)); + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(mRollbackInfo)); when(mRollbackInfo.getPackages()).thenReturn(List.of(mPackageRollbackInfo)); @@ -208,96 +252,790 @@ public class RollbackPackageHealthObserverTest { } /** - * Test that isAutomaticRollbackDenied works correctly when packages that are not - * denied are sent. + * Test that when impactLevel is low returns user impact level 70 */ @Test - public void isRollbackAllowedTest_false() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.test.package", 1))).isEqualTo(false); + public void healthCheckFailed_impactLevelLow_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Test that isAutomaticRollbackDenied works correctly when packages that are - * denied are sent. + * HealthCheckFailed should only return low impact rollbacks. High impact rollbacks are only + * for bootloop. */ @Test - public void isRollbackAllowedTest_true() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(true); + public void healthCheckFailed_impactLevelHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Test that isAutomaticRollbackDenied works correctly when no config is present + * When the rollback impact level is manual only return user impact level 0. (User impact level + * 0 is ignored by package watchdog) */ @Test - public void isRollbackAllowedTest_noConfig() throws IOException { - final File folder = createTempSubfolder("folder"); + public void healthCheckFailed_impactLevelManualOnly_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); - readPermissions(folder, /* Grant all permission flags */ ~0); + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); - assertThat(RollbackPackageHealthObserver.isAutomaticRollbackDenied(mSysConfig, - new VersionedPackage("com.android.vending", 1))).isEqualTo(false); + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onHealthCheckFailed(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } /** - * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. - * - * @param folder pre-existing subdirectory of mTemporaryFolder to put the file - * @param fileName name of the file (e.g. filename.xml) to create - * @param contents contents to write to the file - * @return the newly created file + * When both low impact and high impact are present, return 70. */ - private File createTempFile(File folder, String fileName, String contents) - throws IOException { - File file = new File(folder, fileName); - BufferedWriter bw = new BufferedWriter(new FileWriter(file)); - bw.write(contents); - bw.close(); - - // Print to logcat for test debugging. - Log.d(LOG_TAG, "Contents of file " + file.getAbsolutePath()); - Scanner input = new Scanner(file); - while (input.hasNextLine()) { - Log.d(LOG_TAG, input.nextLine()); - } + @Test + public void healthCheckFailed_impactLevelLowAndHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null, false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); - return file; + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onHealthCheckFailed(failedPackage, + PackageWatchdog.FAILURE_REASON_APP_CRASH, 1)); } - private void readPermissions(File libraryDir, int permissionFlag) { - final XmlPullParser parser = Xml.newPullParser(); - mSysConfig.readPermissions(parser, libraryDir, permissionFlag); + /** + * When low impact rollback is available roll it back. + */ + @Test + public void execute_impactLevelLow_nativeCrash_rollback() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage secondFailedPackage = new VersionedPackage(APP_B, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(secondFailedPackage, + PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager).getAvailableRollbacks(); + verify(mRollbackManager).commitRollback(eq(rollbackId), any(), any()); } /** - * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents. - * - * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed - * @return the folder + * Rollback the failing package if rollback is available for it */ - private File createTempSubfolder(String folderName) - throws IOException { - File folder = new File(mTemporaryFolder.getRoot(), folderName); - folder.mkdirs(); - return folder; + @Test + public void execute_impactLevelLow_rollbackFailedPackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(appBFrom, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager).commitRollback(argument.capture(), any(), any()); + // Rollback package App B as the failing package is B + assertThat(argument.getValue()).isEqualTo(rollbackId2); + } + + /** + * Rollback all available rollbacks if the rollback is not available for failing package. + */ + @Test + public void execute_impactLevelLow_rollbackAll() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(2)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); + } + + /** + * rollback low impact package if both low and high impact packages are available + */ + @Test + public void execute_impactLevelLowAndHigh_rollbackLow() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Don't roll back high impact package if only high impact package is available. high impact + * rollback to be rolled back only on bootloop. + */ + @Test + public void execute_impactLevelHigh_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); + + } + + /** + * Test that when impactLevel is low returns user impact level 70 + */ + @Test + public void onBootLoop_impactLevelLow_onePackage() throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onBootLoop(1)); + } + + @Test + public void onBootLoop_impactLevelHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_90, + observer.onBootLoop(1)); + } + + @Test + public void onBootLoop_impactLevelHighDisableHighImpactRollback_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onBootLoop(1)); + } + + /** + * When the rollback impact level is manual only return user impact level 0. (User impact level + * 0 is ignored by package watchdog) + */ + @Test + public void onBootLoop_impactLevelManualOnly_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_0, + observer.onBootLoop(1)); + } + + /** + * When both low impact and high impact are present, return 70. + */ + @Test + public void onBootLoop_impactLevelLowAndHigh_onePackage() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(appAFrom, appATo, + null, null, false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(1, List.of(packageRollbackInfo), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null, false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_70, + observer.onBootLoop(1)); + } + + /** + * Rollback all available rollbacks if the rollback is not available for failing package. + */ + @Test + public void executeBootLoopMitigation_impactLevelLow_rollbackAll() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(2)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1, rollbackId2)); + } + + /** + * rollback low impact package if both low and high impact packages are available + */ + @Test + public void executeBootLoopMitigation_impactLevelLowAndHigh_rollbackLow() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback A and B when the failing package doesn't have a rollback + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Rollback high impact package if only high impact package is available + */ + @Test + public void executeBootLoopMitigation_impactLevelHigh_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback high impact packages when no other rollback available + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); + } + + /** + * Rollback only low impact available rollbacks if both low and manual only are available. + */ + @Test + public void execute_impactLevelLowAndManual_rollbackLowImpactOnly() + throws PackageManager.NameNotFoundException, InterruptedException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_LOW); + int rollbackId2 = 2; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoB), + false, null, 222, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId1)); + } + + /** + * Do not roll back if only manual rollback is available. + */ + @Test + public void execute_impactLevelManual_rollbackLowImpactOnly() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_ONLY_MANUAL); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(rollbackInfo1)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.execute(failedPackage, PackageWatchdog.FAILURE_REASON_APP_CRASH, 1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, never()).commitRollback(argument.capture(), any(), any()); + } + + /** + * Rollback alphabetically first package if multiple high impact rollbacks are available. + */ + @Test + public void executeBootLoopMitigation_impactLevelHighMultiplePackage_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + int rollbackId1 = 1; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + int rollbackId2 = 2; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + VersionedPackage failedPackage = new VersionedPackage(APP_C, VERSION_CODE); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, times(1)).commitRollback( + argument.capture(), any(), any()); + // Rollback APP_A because it is first alphabetically + assertThat(argument.getAllValues()).isEqualTo(List.of(rollbackId2)); + } + + /** + * Don't roll back if kill switch is enabled. + */ + @Test + public void executeBootLoopMitigation_impactLevelHighKillSwitchTrue_rollbackHigh() + throws PackageManager.NameNotFoundException { + mSetFlagsRule.enableFlags(Flags.FLAG_RECOVERABILITY_DETECTION); + SystemProperties.set(PROP_DISABLE_HIGH_IMPACT_ROLLBACK_FLAG, Boolean.toString(true)); + int rollbackId1 = 1; + VersionedPackage appBFrom = new VersionedPackage(APP_B, VERSION_CODE_2); + VersionedPackage appBTo = new VersionedPackage(APP_B, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoB = new PackageRollbackInfo(appBFrom, appBTo, + null, null , false, false, + null); + RollbackInfo rollbackInfo1 = new RollbackInfo(rollbackId1, List.of(packageRollbackInfoB), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + int rollbackId2 = 2; + VersionedPackage appAFrom = new VersionedPackage(APP_A, VERSION_CODE_2); + VersionedPackage appATo = new VersionedPackage(APP_A, VERSION_CODE); + PackageRollbackInfo packageRollbackInfoA = new PackageRollbackInfo(appAFrom, appATo, + null, null , false, false, + null); + RollbackInfo rollbackInfo2 = new RollbackInfo(rollbackId2, List.of(packageRollbackInfoA), + false, null, 111, + PackageManager.ROLLBACK_USER_IMPACT_HIGH); + RollbackPackageHealthObserver observer = + spy(new RollbackPackageHealthObserver(mMockContext, mApexManager)); + ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass(Integer.class); + + when(mMockContext.getSystemService(RollbackManager.class)).thenReturn(mRollbackManager); + // Make the rollbacks available + when(mRollbackManager.getAvailableRollbacks()).thenReturn( + List.of(rollbackInfo1, rollbackInfo2)); + when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); + when(mMockPackageManager.getModuleInfo(any(), eq(0))).thenReturn(null); + + observer.executeBootLoopMitigation(1); + waitForIdleHandler(observer.getHandler(), Duration.ofSeconds(10)); + + verify(mRollbackManager, never()).commitRollback( + argument.capture(), any(), any()); + } + + private void waitForIdleHandler(Handler handler, Duration timeout) { + final MessageQueue queue = handler.getLooper().getQueue(); + final CountDownLatch latch = new CountDownLatch(1); + queue.addIdleHandler(() -> { + latch.countDown(); + // Remove idle handler + return false; + }); + try { + latch.await(timeout.toMillis(), TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + fail("Interrupted unexpectedly: " + e); + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING new file mode 100644 index 000000000000..e42bdad97730 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/rollback/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "postsubmit": [ + { + "name": "FrameworksMockingServicesTests", + "options": [ + { + "include-filter": "com.android.server.rollback" + } + ] + } + ] +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt index 84af9dd75571..218b7c07b3dc 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt @@ -30,6 +30,7 @@ private fun createKeyboard( deviceId: Int, vendorId: Int, productId: Int, + deviceBus: Int, languageTag: String?, layoutType: String? ): InputDevice = @@ -42,6 +43,7 @@ private fun createKeyboard( .setExternal(true) .setVendorId(vendorId) .setProductId(productId) + .setDeviceBus(deviceBus) .setKeyboardLanguageTag(languageTag) .setKeyboardLayoutType(layoutType) .build() @@ -67,6 +69,7 @@ class KeyboardMetricsCollectorTests { const val DEVICE_ID = 1 const val DEFAULT_VENDOR_ID = 123 const val DEFAULT_PRODUCT_ID = 456 + const val DEFAULT_DEVICE_BUS = 789 } @Test @@ -77,6 +80,7 @@ class KeyboardMetricsCollectorTests { DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, + DEFAULT_DEVICE_BUS, null, null ) @@ -92,6 +96,7 @@ class KeyboardMetricsCollectorTests { DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, + DEFAULT_DEVICE_BUS, null, null ) @@ -107,6 +112,7 @@ class KeyboardMetricsCollectorTests { DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, + DEFAULT_DEVICE_BUS, "de-CH", "qwertz" ) @@ -135,6 +141,11 @@ class KeyboardMetricsCollectorTests { DEFAULT_PRODUCT_ID, event.productId ) + assertEquals( + "KeyboardConfigurationEvent should pick device bus from provided InputDevice", + DEFAULT_DEVICE_BUS, + event.deviceBus + ) assertTrue(event.isFirstConfiguration) assertEquals( @@ -178,6 +189,7 @@ class KeyboardMetricsCollectorTests { DEVICE_ID, DEFAULT_VENDOR_ID, DEFAULT_PRODUCT_ID, + DEFAULT_DEVICE_BUS, "und", // Undefined language tag "azerty" ) diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index a529382bfb26..15cd5115a49e 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -149,6 +149,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkStateSnapshot; import android.net.NetworkTemplate; import android.net.TelephonyNetworkSpecifier; @@ -205,7 +206,6 @@ import libcore.io.Streams; import org.junit.After; import org.junit.Assume; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; @@ -1620,6 +1620,12 @@ public class NetworkPolicyManagerServiceTest { verify(mActivityManagerInternal).notifyNetworkPolicyRulesUpdated(UID_A, procStateSeq); } + private void callAndWaitOnUidGone(int uid) throws Exception { + // The disabled argument is used only for ephemeral apps and does not matter here. + mUidObserver.onUidGone(uid, false /* disabled */); + waitForUidEventHandlerIdle(); + } + private void callAndWaitOnUidStateChanged(int uid, int procState, long procStateSeq) throws Exception { callAndWaitOnUidStateChanged(uid, procState, procStateSeq, @@ -2144,14 +2150,12 @@ public class NetworkPolicyManagerServiceTest { assertFalse(mService.isUidNetworkingBlocked(UID_E, false)); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainEnabled() throws Exception { verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnProcStateChange() throws Exception { @@ -2181,7 +2185,6 @@ public class NetworkPolicyManagerServiceTest { assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnAllowlistChange() throws Exception { @@ -2220,7 +2223,6 @@ public class NetworkPolicyManagerServiceTest { assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnTempAllowlistChange() throws Exception { @@ -2250,7 +2252,15 @@ public class NetworkPolicyManagerServiceTest { assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); } - @Ignore("Temporarily disabled until the feature is enabled") + private boolean isUidState(int uid, int procState, int procStateSeq, int capability) { + final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid); + if (uidState == null) { + return false; + } + return uidState.uid == uid && uidState.procStateSeq == procStateSeq + && uidState.procState == procState && uidState.capability == capability; + } + @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersProcStateChanges() throws Exception { @@ -2313,7 +2323,6 @@ public class NetworkPolicyManagerServiceTest { waitForUidEventHandlerIdle(); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersStaleChanges() throws Exception { @@ -2334,7 +2343,6 @@ public class NetworkPolicyManagerServiceTest { waitForUidEventHandlerIdle(); } - @Ignore("Temporarily disabled until the feature is enabled") @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testUidObserverFiltersCapabilityChanges() throws Exception { @@ -2371,6 +2379,31 @@ public class NetworkPolicyManagerServiceTest { } @Test + public void testUidStateChangeAfterUidGone() throws Exception { + final int testProcStateSeq = 51; + final int testProcState = PROCESS_STATE_IMPORTANT_FOREGROUND; + + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // First callback for uid. + callOnUidStatechanged(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + assertTrue(isUidState(UID_B, testProcState, testProcStateSeq, PROCESS_CAPABILITY_NONE)); + + callAndWaitOnUidGone(UID_B); + try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) { + // Even though the procState is the same, the uid had exited - so this should be + // processed as a fresh callback. + callOnUidStatechanged(UID_B, testProcState, testProcStateSeq + 1, + PROCESS_CAPABILITY_NONE); + assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED)); + } + waitForUidEventHandlerIdle(); + assertTrue(isUidState(UID_B, testProcState, testProcStateSeq + 1, PROCESS_CAPABILITY_NONE)); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java index e075379284f0..c0ea1571d8c7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java @@ -106,13 +106,13 @@ public class PackageDynamicCodeLoadingTests { } @Test - public void testRecord_changeUserForFile_throws() { + public void testRecord_changeUserForFile_ignored() { Entry entry1 = new Entry("owning.package1", "/path/file1", 'D', 10, "loading.package1"); Entry entry2 = new Entry("owning.package1", "/path/file1", 'D', 20, "loading.package1"); PackageDynamicCodeLoading info = makePackageDcl(entry1); - assertThrows(() -> record(info, entry2)); + assertThat(record(info, entry2)).isFalse(); assertHasEntries(info, entry1); } diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index aca96ad20385..d073f5bfebe4 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -595,56 +595,6 @@ public class SystemConfigTest { } /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app}. - */ - @Test - public void automaticRollbackDeny_vending() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app package=\"com.android.vending\" />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()) - .containsExactly("com.android.vending"); - } - - /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app} without any packages. - */ - @Test - public void automaticRollbackDeny_empty() throws IOException { - final String contents = - "<config>\n" - + " <automatic-rollback-denylisted-app />\n" - + "</config>"; - final File folder = createTempSubfolder("folder"); - createTempFile(folder, "automatic-rollback-denylisted-app.xml", contents); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty(); - } - - /** - * Test that getRollbackDenylistedPackages works correctly for the tag: - * {@code automatic-rollback-denylisted-app} without the corresponding config. - */ - @Test - public void automaticRollbackDeny_noConfig() throws IOException { - final File folder = createTempSubfolder("folder"); - - readPermissions(folder, /* Grant all permission flags */ ~0); - - assertThat(mSysConfig.getAutomaticRollbackDenylistedPackages()).isEmpty(); - } - - /** * Tests that readPermissions works correctly for the tag: {@code update-ownership}. */ @Test diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java index feca3268a95c..84925f933c5f 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java @@ -38,6 +38,7 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { private static final int VENDOR_ID = 0x123; private static final int PRODUCT_ID = 0x456; + private static final int DEVICE_BUS = 0x789; private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT; private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT); private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT; @@ -224,7 +225,7 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { @Override public void setUp() { super.setUp(); - mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID); + mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID, DEVICE_BUS); mPhoneWindowManager.overrideLaunchHome(); mPhoneWindowManager.overrideSearchKeyBehavior( PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY); @@ -240,6 +241,7 @@ public class ShortcutLoggingTests extends ShortcutKeyTestBase { int expectedKey, int expectedModifierState) { sendKeyCombination(testKeys, 0); mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent, - expectedKey, expectedModifierState, "Failed while executing " + testName); + expectedKey, expectedModifierState, DEVICE_BUS, + "Failed while executing " + testName); } } diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 186676772a6a..9aaf291a82ba 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -423,10 +423,15 @@ class TestPhoneWindowManager { doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing(); } - void overrideKeyEventSource(int vendorId, int productId) { - InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId( - productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType( - InputDevice.KEYBOARD_TYPE_ALPHABETIC).build(); + void overrideKeyEventSource(int vendorId, int productId, int deviceBus) { + InputDevice device = new InputDevice.Builder() + .setId(1) + .setVendorId(vendorId) + .setProductId(productId) + .setDeviceBus(deviceBus) + .setSources(InputDevice.SOURCE_KEYBOARD) + .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC) + .build(); doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class)); doReturn(device).when(mInputManager).getInputDevice(anyInt()); } @@ -604,10 +609,10 @@ class TestPhoneWindowManager { } void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent, - int expectedKey, int expectedModifierState, String errorMsg) { + int expectedKey, int expectedModifierState, int deviceBus, String errorMsg) { waitForIdle(); verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey}, - expectedModifierState), description(errorMsg)); + expectedModifierState, deviceBus), description(errorMsg)); } } diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java index d075b5f01ef9..5434c82b07bd 100644 --- a/tests/Input/src/com/android/test/input/InputDeviceTest.java +++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java @@ -51,6 +51,7 @@ public class InputDeviceTest { assertEquals(device.getName(), outDevice.getName()); assertEquals(device.getVendorId(), outDevice.getVendorId()); assertEquals(device.getProductId(), outDevice.getProductId()); + assertEquals(device.getDeviceBus(), outDevice.getDeviceBus()); assertEquals(device.getDescriptor(), outDevice.getDescriptor()); assertEquals(device.isExternal(), outDevice.isExternal()); assertEquals(device.getSources(), outDevice.getSources()); @@ -79,6 +80,7 @@ public class InputDeviceTest { .setName("Test Device " + DEVICE_ID) .setVendorId(44) .setProductId(45) + .setDeviceBus(3) .setDescriptor("descriptor") .setExternal(true) .setSources(InputDevice.SOURCE_HDMI) diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 228520e8545b..bc1df75bb142 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -3,6 +3,7 @@ //######################################################################## package { + default_team: "trendy_team_enigma", // 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" |