diff options
212 files changed, 4705 insertions, 2453 deletions
diff --git a/Android.bp b/Android.bp index 933d1aff842b..e344eaa49e27 100644 --- a/Android.bp +++ b/Android.bp @@ -357,7 +357,6 @@ java_library { visibility: [ "//frameworks/base", // TODO(b/147128803) remove the below lines - "//frameworks/base/apex/appsearch/framework", "//frameworks/base/apex/blobstore/framework", "//frameworks/base/apex/jobscheduler/framework", "//frameworks/base/packages/Tethering/tests/unit", diff --git a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java index 764a3a8b1ed9..4aaa9f449b4f 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/PowerSaveModeModifier.java @@ -17,27 +17,48 @@ package com.android.server.tare; import android.annotation.NonNull; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.PowerManager; +import android.os.SystemClock; import android.util.IndentingPrintWriter; +import android.util.Log; +import android.util.Slog; /** Modifier that makes things more expensive in adaptive and full battery saver are active. */ class PowerSaveModeModifier extends Modifier { + private static final String TAG = "TARE-" + PowerSaveModeModifier.class.getSimpleName(); + private static final boolean DEBUG = InternalResourceService.DEBUG + || Log.isLoggable(TAG, Log.DEBUG); + private final InternalResourceService mIrs; - private final PowerManager mPowerManager; + private final PowerSaveModeTracker mPowerSaveModeTracker; PowerSaveModeModifier(@NonNull InternalResourceService irs) { super(); mIrs = irs; - mPowerManager = irs.getContext().getSystemService(PowerManager.class); + mPowerSaveModeTracker = new PowerSaveModeTracker(); + } + + @Override + public void setup() { + mPowerSaveModeTracker.startTracking(mIrs.getContext()); + } + + @Override + public void tearDown() { + mPowerSaveModeTracker.stopTracking(mIrs.getContext()); } @Override long getModifiedCostToProduce(long ctp) { - if (mPowerManager.isPowerSaveMode()) { + if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { return (long) (1.5 * ctp); } // TODO: get adaptive power save mode - if (mPowerManager.isPowerSaveMode()) { + if (mPowerSaveModeTracker.mPowerSaveModeEnabled) { return (long) (1.25 * ctp); } return ctp; @@ -46,6 +67,45 @@ class PowerSaveModeModifier extends Modifier { @Override void dump(IndentingPrintWriter pw) { pw.print("power save="); - pw.println(mPowerManager.isPowerSaveMode()); + pw.println(mPowerSaveModeTracker.mPowerSaveModeEnabled); + } + + // TODO: migrate to relying on PowerSaveState and ServiceType.TARE + private final class PowerSaveModeTracker extends BroadcastReceiver { + private final PowerManager mPowerManager; + private volatile boolean mPowerSaveModeEnabled; + + private PowerSaveModeTracker() { + mPowerManager = mIrs.getContext().getSystemService(PowerManager.class); + } + + public void startTracking(@NonNull Context context) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED); + context.registerReceiver(this, filter); + + // Initialise tracker state. + mPowerSaveModeEnabled = mPowerManager.isPowerSaveMode(); + } + + public void stopTracking(@NonNull Context context) { + context.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) { + final boolean enabled = mPowerManager.isPowerSaveMode(); + if (DEBUG) { + Slog.d(TAG, "Power save mode changed to " + enabled + + ", fired @ " + SystemClock.elapsedRealtime()); + } + if (mPowerSaveModeEnabled != enabled) { + mPowerSaveModeEnabled = enabled; + mIrs.onDeviceStateChanged(); + } + } + } } } diff --git a/api/Android.bp b/api/Android.bp index 40472b7e2d69..8370c1069710 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -186,8 +186,10 @@ genrule { cmd: metalava_cmd + "--check-compatibility:api:released $(location :android.api.module-lib.latest) " + // Note: having "public" be the base of module-lib is not perfect -- it should - // ideally be a merged public+system), but this will help when migrating from - // MODULE_LIBS -> public. + // ideally be a merged public+system (which metalava is not currently able to generate). + // This "base" will help when migrating from MODULE_LIBS -> public, but not when + // migrating from MODULE_LIBS -> system (where it needs to instead be listed as + // an incompatibility). "--check-compatibility:base $(location :frameworks-base-api-current.txt) " + "--baseline:compatibility:released $(location :android-incompatibilities.api.module-lib.latest) " + "--update-baseline:compatibility:released $(genDir)/updated-baseline.txt " + diff --git a/core/api/current.txt b/core/api/current.txt index 77efa4d39c44..445de4890094 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7390,12 +7390,12 @@ package android.app.admin { method @NonNull public java.util.List<java.lang.String> getDelegatedScopes(@Nullable android.content.ComponentName, @NonNull String); method @Nullable public String getDeviceManagerRoleHolderPackageName(); method public CharSequence getDeviceOwnerLockScreenInfo(); - method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); - method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>); method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon); method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon); - method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); - method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>); + method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Supplier<android.graphics.drawable.Drawable>); method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName); method @NonNull public String getEnrollmentSpecificId(); method @Nullable public android.app.admin.FactoryResetProtectionPolicy getFactoryResetProtectionPolicy(@Nullable android.content.ComponentName); @@ -7704,9 +7704,10 @@ package android.app.admin { field public static final String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE"; field public static final String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID"; field public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE"; - field public static final String EXTRA_RESOURCE_ID = "android.app.extra.RESOURCE_ID"; - field public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = "android.app.extra.RESOURCE_TYPE_DRAWABLE"; - field public static final String EXTRA_RESOURCE_TYPE_STRING = "android.app.extra.RESOURCE_TYPE_STRING"; + field public static final String EXTRA_RESOURCE_IDS = "android.app.extra.RESOURCE_IDS"; + field public static final String EXTRA_RESOURCE_TYPE = "android.app.extra.RESOURCE_TYPE"; + field public static final int EXTRA_RESOURCE_TYPE_DRAWABLE = 1; // 0x1 + field public static final int EXTRA_RESOURCE_TYPE_STRING = 2; // 0x2 field public static final String EXTRA_RESULT_LAUNCH_INTENT = "android.app.extra.RESULT_LAUNCH_INTENT"; field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1 field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2 @@ -17398,7 +17399,7 @@ package android.hardware.camera2 { field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_PARTIAL_RESULT_COUNT; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> REQUEST_PIPELINE_MAX_DEPTH; - field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE; + field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES; field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES; @@ -18118,22 +18119,23 @@ package android.hardware.camera2.params { } public final class DynamicRangeProfiles { - ctor public DynamicRangeProfiles(@NonNull int[]); - method @NonNull public java.util.Set<java.lang.Integer> getProfileCaptureRequestConstraints(int); - method @NonNull public java.util.Set<java.lang.Integer> getSupportedProfiles(); - field public static final int DOLBY_VISION_10B_HDR_OEM = 64; // 0x40 - field public static final int DOLBY_VISION_10B_HDR_OEM_PO = 128; // 0x80 - field public static final int DOLBY_VISION_10B_HDR_REF = 16; // 0x10 - field public static final int DOLBY_VISION_10B_HDR_REF_PO = 32; // 0x20 - field public static final int DOLBY_VISION_8B_HDR_OEM = 1024; // 0x400 - field public static final int DOLBY_VISION_8B_HDR_OEM_PO = 2048; // 0x800 - field public static final int DOLBY_VISION_8B_HDR_REF = 256; // 0x100 - field public static final int DOLBY_VISION_8B_HDR_REF_PO = 512; // 0x200 - field public static final int HDR10 = 4; // 0x4 - field public static final int HDR10_PLUS = 8; // 0x8 - field public static final int HLG10 = 2; // 0x2 - field public static final int PUBLIC_MAX = 4096; // 0x1000 - field public static final int STANDARD = 1; // 0x1 + ctor public DynamicRangeProfiles(@NonNull long[]); + method @NonNull public java.util.Set<java.lang.Long> getProfileCaptureRequestConstraints(long); + method @NonNull public java.util.Set<java.lang.Long> getSupportedProfiles(); + method public boolean isExtraLatencyPresent(long); + field public static final long DOLBY_VISION_10B_HDR_OEM = 64L; // 0x40L + field public static final long DOLBY_VISION_10B_HDR_OEM_PO = 128L; // 0x80L + field public static final long DOLBY_VISION_10B_HDR_REF = 16L; // 0x10L + field public static final long DOLBY_VISION_10B_HDR_REF_PO = 32L; // 0x20L + field public static final long DOLBY_VISION_8B_HDR_OEM = 1024L; // 0x400L + field public static final long DOLBY_VISION_8B_HDR_OEM_PO = 2048L; // 0x800L + field public static final long DOLBY_VISION_8B_HDR_REF = 256L; // 0x100L + field public static final long DOLBY_VISION_8B_HDR_REF_PO = 512L; // 0x200L + field public static final long HDR10 = 4L; // 0x4L + field public static final long HDR10_PLUS = 8L; // 0x8L + field public static final long HLG10 = 2L; // 0x2L + field public static final long PUBLIC_MAX = 4096L; // 0x1000L + field public static final long STANDARD = 1L; // 0x1L } public final class ExtensionSessionConfiguration { @@ -18240,7 +18242,7 @@ package android.hardware.camera2.params { method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader); method public int describeContents(); method public void enableSurfaceSharing(); - method public int getDynamicRangeProfile(); + method public long getDynamicRangeProfile(); method public int getMaxSharedSurfaceCount(); method public int getMirrorMode(); method public int getStreamUseCase(); @@ -18250,7 +18252,7 @@ package android.hardware.camera2.params { method public int getTimestampBase(); method public void removeSensorPixelModeUsed(int); method public void removeSurface(@NonNull android.view.Surface); - method public void setDynamicRangeProfile(int); + method public void setDynamicRangeProfile(long); method public void setMirrorMode(int); method public void setPhysicalCameraId(@Nullable String); method public void setStreamUseCase(int); @@ -35449,8 +35451,8 @@ package android.provider { field public static final String EXTRA_WIFI_NETWORK_RESULT_LIST = "android.provider.extra.WIFI_NETWORK_RESULT_LIST"; field public static final String INTENT_CATEGORY_USAGE_ACCESS_CONFIG = "android.intent.category.USAGE_ACCESS_CONFIG"; field public static final String METADATA_USAGE_ACCESS_REASON = "android.settings.metadata.USAGE_ACCESS_REASON"; - field public static final String SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = "supervisor_restricted_biometrics_controller"; - field public static final String SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = ""; + field public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1; // 0x1 + field public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0; // 0x0 } public static final class Settings.Global extends android.provider.Settings.NameValueTable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2c0e6416b455..83464f0ca1df 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -915,10 +915,10 @@ package android.app { public class StatusBarManager { method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); - method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride(); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarMode(); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void registerNearbyMediaDevicesProvider(@NonNull android.media.NearbyMediaDevicesProvider); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); - method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int); + method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarMode(int); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void unregisterNearbyMediaDevicesProvider(@NonNull android.media.NearbyMediaDevicesProvider); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable android.graphics.drawable.Icon, @Nullable CharSequence); method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable); @@ -933,8 +933,8 @@ package android.app { field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; // 0x7 field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; // 0x5 field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; // 0x3 - field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1 - field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0 + field public static final int NAV_BAR_MODE_DEFAULT = 0; // 0x0 + field public static final int NAV_BAR_MODE_KIDS = 1; // 0x1 } public static final class StatusBarManager.DisableInfo { @@ -1059,10 +1059,10 @@ package android.app.admin { ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, @NonNull String, @NonNull String, @NonNull String, @DrawableRes int); ctor public DevicePolicyDrawableResource(@NonNull android.content.Context, @NonNull String, @NonNull String, @DrawableRes int); method public int describeContents(); - method @DrawableRes public int getCallingPackageResourceId(); method @NonNull public String getDrawableId(); method @NonNull public String getDrawableSource(); method @NonNull public String getDrawableStyle(); + method @DrawableRes public int getResourceIdInCallingPackage(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyDrawableResource> CREATOR; } @@ -1088,8 +1088,8 @@ package android.app.admin { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser(); method @Nullable public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException; method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException; - method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>); - method @Nullable public String getString(@NonNull String, @NonNull java.util.concurrent.Callable<java.lang.String>, @NonNull java.lang.Object...); + method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>); + method @Nullable public String getString(@NonNull String, @NonNull java.util.function.Supplier<java.lang.String>, @NonNull java.lang.Object...); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState(); method public boolean isDeviceManaged(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned(); @@ -1239,7 +1239,7 @@ package android.app.admin { public final class DevicePolicyStringResource implements android.os.Parcelable { ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int); method public int describeContents(); - method public int getCallingPackageResourceId(); + method public int getResourceIdInCallingPackage(); method @NonNull public String getStringId(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.DevicePolicyStringResource> CREATOR; @@ -2172,23 +2172,41 @@ package android.app.smartspace { field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.SmartspaceTarget> CREATOR; field public static final int FEATURE_ALARM = 7; // 0x7 field public static final int FEATURE_BEDTIME_ROUTINE = 16; // 0x10 + field public static final int FEATURE_BLAZE_BUILD_PROGRESS = 40; // 0x28 field public static final int FEATURE_CALENDAR = 2; // 0x2 field public static final int FEATURE_COMMUTE_TIME = 3; // 0x3 field public static final int FEATURE_CONSENT = 11; // 0xb + field public static final int FEATURE_CROSS_DEVICE_TIMER = 32; // 0x20 + field public static final int FEATURE_DOORBELL = 30; // 0x1e + field public static final int FEATURE_DRIVING_MODE = 26; // 0x1a + field public static final int FEATURE_EARTHQUAKE_ALERT = 38; // 0x26 + field public static final int FEATURE_EARTHQUAKE_OCCURRED = 41; // 0x29 field public static final int FEATURE_ETA_MONITORING = 18; // 0x12 field public static final int FEATURE_FITNESS_TRACKING = 17; // 0x11 + field public static final int FEATURE_FLASHLIGHT = 28; // 0x1c field public static final int FEATURE_FLIGHT = 4; // 0x4 + field public static final int FEATURE_GAS_STATION_PAYMENT = 24; // 0x18 + field public static final int FEATURE_HOLIDAY_ALARM = 34; // 0x22 field public static final int FEATURE_LOYALTY_CARD = 14; // 0xe field public static final int FEATURE_MEDIA = 15; // 0xf + field public static final int FEATURE_MEDIA_HEADS_UP = 36; // 0x24 + field public static final int FEATURE_MEDIA_RESUME = 31; // 0x1f field public static final int FEATURE_MISSED_CALL = 19; // 0x13 field public static final int FEATURE_ONBOARDING = 8; // 0x8 field public static final int FEATURE_PACKAGE_TRACKING = 20; // 0x14 + field public static final int FEATURE_PAIRED_DEVICE_STATE = 25; // 0x19 field public static final int FEATURE_REMINDER = 6; // 0x6 + field public static final int FEATURE_SAFETY_CHECK = 35; // 0x23 + field public static final int FEATURE_SEVERE_WEATHER_ALERT = 33; // 0x21 field public static final int FEATURE_SHOPPING_LIST = 13; // 0xd + field public static final int FEATURE_SLEEP_SUMMARY = 27; // 0x1b field public static final int FEATURE_SPORTS = 9; // 0x9 + field public static final int FEATURE_STEP_COUNTING = 37; // 0x25 + field public static final int FEATURE_STEP_DATE = 39; // 0x27 field public static final int FEATURE_STOCK_PRICE_CHANGE = 12; // 0xc field public static final int FEATURE_STOPWATCH = 22; // 0x16 field public static final int FEATURE_TIMER = 21; // 0x15 + field public static final int FEATURE_TIME_TO_LEAVE = 29; // 0x1d field public static final int FEATURE_TIPS = 5; // 0x5 field public static final int FEATURE_UNDEFINED = 0; // 0x0 field public static final int FEATURE_UPCOMING_ALARM = 23; // 0x17 @@ -2254,22 +2272,12 @@ package android.app.smartspace.uitemplatedata { public class BaseTemplateData implements android.os.Parcelable { method public int describeContents(); method public int getLayoutWeight(); - method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getPrimaryLoggingInfo(); - method @Nullable public android.app.smartspace.uitemplatedata.TapAction getPrimaryTapAction(); - method @Nullable public android.app.smartspace.uitemplatedata.Icon getSubtitleIcon(); - method @Nullable public android.app.smartspace.uitemplatedata.Text getSubtitleText(); - method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalAlarmText(); - method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalIcon(); - method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalLoggingInfo(); - method @Nullable public android.app.smartspace.uitemplatedata.Icon getSupplementalSubtitleIcon(); - method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getSupplementalSubtitleLoggingInfo(); - method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalSubtitleTapAction(); - method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalSubtitleText(); - method @Nullable public android.app.smartspace.uitemplatedata.TapAction getSupplementalTapAction(); - method @Nullable public android.app.smartspace.uitemplatedata.Text getSupplementalText(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getPrimaryItem(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSubtitleItem(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSubtitleSupplementalItem(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSupplementalAlarmItem(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo getSupplementalLineItem(); method public int getTemplateType(); - method @Nullable public android.app.smartspace.uitemplatedata.Icon getTitleIcon(); - method @Nullable public android.app.smartspace.uitemplatedata.Text getTitleText(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData> CREATOR; } @@ -2278,27 +2286,37 @@ package android.app.smartspace.uitemplatedata { ctor public BaseTemplateData.Builder(int); method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData build(); method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setLayoutWeight(int); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.Text); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalIcon(@NonNull android.app.smartspace.uitemplatedata.Icon); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.Text); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalText(@NonNull android.app.smartspace.uitemplatedata.Text); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.Icon); - method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.Text); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setPrimaryItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSubtitleSupplementalItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalAlarmItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.Builder setSupplementalLineItem(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo); + } + + public static final class BaseTemplateData.SubItemInfo implements android.os.Parcelable { + method public int describeContents(); + method @Nullable public android.app.smartspace.uitemplatedata.Icon getIcon(); + method @Nullable public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo getLoggingInfo(); + method @Nullable public android.app.smartspace.uitemplatedata.TapAction getTapAction(); + method @Nullable public android.app.smartspace.uitemplatedata.Text getText(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo> CREATOR; + } + + public static final class BaseTemplateData.SubItemInfo.Builder { + ctor public BaseTemplateData.SubItemInfo.Builder(); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo build(); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setIcon(@NonNull android.app.smartspace.uitemplatedata.Icon); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setLoggingInfo(@NonNull android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setTapAction(@NonNull android.app.smartspace.uitemplatedata.TapAction); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemInfo.Builder setText(@NonNull android.app.smartspace.uitemplatedata.Text); } public static final class BaseTemplateData.SubItemLoggingInfo implements android.os.Parcelable { method public int describeContents(); method public int getFeatureType(); method public int getInstanceId(); + method @Nullable public CharSequence getPackageName(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo> CREATOR; } @@ -2306,6 +2324,7 @@ package android.app.smartspace.uitemplatedata { public static final class BaseTemplateData.SubItemLoggingInfo.Builder { ctor public BaseTemplateData.SubItemLoggingInfo.Builder(int, int); method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo build(); + method @NonNull public android.app.smartspace.uitemplatedata.BaseTemplateData.SubItemLoggingInfo.Builder setPackageName(@NonNull CharSequence); } public final class CarouselTemplateData extends android.app.smartspace.uitemplatedata.BaseTemplateData { @@ -2793,7 +2812,7 @@ package android.companion.virtual { method public void addActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener, @NonNull java.util.concurrent.Executor); method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @NonNull java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index e31a56690408..8b3c9fa73798 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -523,6 +523,8 @@ public class BroadcastOptions extends ComponentOptions { * Sets whether events (such as posting a notification) originating from an app after it * receives the broadcast while in background should be recorded as responses to the broadcast. * + * <p> Note that this will only be considered when sending explicit broadcast intents. + * * @param id ID to be used for the response events corresponding to this broadcast. If the * value is {@code 0} (default), then response events will not be recorded. Otherwise, * they will be recorded with the ID provided. diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 77c7c6f80f0e..99a523a0e18a 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1377,18 +1377,16 @@ public final class LoadedApk { Slog.wtf(TAG, "App instance already created for package=" + mPackageName + " instance=" + cached); } - // TODO Return the cached one, unless it's for the wrong user? + // TODO Return the cached one, unles it's for the wrong user? // For now, we just add WTF checks. } } Application app = null; - // Temporarily disable per-process app class to investigate b/185177290 -// final String myProcessName = Process.myProcessName(); -// String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess( -// myProcessName); - String appClass = mApplicationInfo.className; + final String myProcessName = Process.myProcessName(); + String appClass = mApplicationInfo.getCustomApplicationClassNameForProcess( + myProcessName); if (forceDefaultAppClass || (appClass == null)) { appClass = "android.app.Application"; } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 0326e11b643e..e460638d4420 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -324,15 +324,15 @@ public class StatusBarManager { public @interface RequestResult {} /** - * Constant for {@link #setNavBarModeOverride(int)} indicating the default navbar mode. + * Constant for {@link #setNavBarMode(int)} indicating the default navbar mode. * * @hide */ @SystemApi - public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; + public static final int NAV_BAR_MODE_DEFAULT = 0; /** - * Constant for {@link #setNavBarModeOverride(int)} indicating kids navbar mode. + * Constant for {@link #setNavBarMode(int)} indicating kids navbar mode. * * <p>When used, back and home icons will change drawables and layout, recents will be hidden, * and the navbar will remain visible when apps are in immersive mode. @@ -340,15 +340,15 @@ public class StatusBarManager { * @hide */ @SystemApi - public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; + public static final int NAV_BAR_MODE_KIDS = 1; /** @hide */ - @IntDef(prefix = {"NAV_BAR_MODE_OVERRIDE_"}, value = { - NAV_BAR_MODE_OVERRIDE_NONE, - NAV_BAR_MODE_OVERRIDE_KIDS + @IntDef(prefix = {"NAV_BAR_MODE_"}, value = { + NAV_BAR_MODE_DEFAULT, + NAV_BAR_MODE_KIDS }) @Retention(RetentionPolicy.SOURCE) - public @interface NavBarModeOverride {} + public @interface NavBarMode {} /** * State indicating that this sender device is close to a receiver device, so the user can @@ -926,25 +926,23 @@ public class StatusBarManager { } /** - * Sets or removes the navigation bar mode override. + * Sets or removes the navigation bar mode. * - * @param navBarModeOverride the mode of the navigation bar override to be set. + * @param navBarMode the mode of the navigation bar to be set. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.STATUS_BAR) - public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) { - if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE - && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) { - throw new IllegalArgumentException( - "Supplied navBarModeOverride not supported: " + navBarModeOverride); + public void setNavBarMode(@NavBarMode int navBarMode) { + if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) { + throw new IllegalArgumentException("Supplied navBarMode not supported: " + navBarMode); } try { final IStatusBarService svc = getService(); if (svc != null) { - svc.setNavBarModeOverride(navBarModeOverride); + svc.setNavBarMode(navBarMode); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -952,23 +950,23 @@ public class StatusBarManager { } /** - * Gets the navigation bar mode override. Returns default value if no override is set. + * Gets the navigation bar mode. Returns default value if no mode is set. * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.STATUS_BAR) - public @NavBarModeOverride int getNavBarModeOverride() { - int navBarModeOverride = NAV_BAR_MODE_OVERRIDE_NONE; + public @NavBarMode int getNavBarMode() { + int navBarMode = NAV_BAR_MODE_DEFAULT; try { final IStatusBarService svc = getService(); if (svc != null) { - navBarModeOverride = svc.getNavBarModeOverride(); + navBarMode = svc.getNavBarMode(); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return navBarModeOverride; + return navBarMode; } /** diff --git a/core/java/android/app/admin/DevicePolicyDrawableResource.java b/core/java/android/app/admin/DevicePolicyDrawableResource.java index 61ff11be170a..7fd8e896bac2 100644 --- a/core/java/android/app/admin/DevicePolicyDrawableResource.java +++ b/core/java/android/app/admin/DevicePolicyDrawableResource.java @@ -38,7 +38,7 @@ public final class DevicePolicyDrawableResource implements Parcelable { @NonNull private final @DevicePolicyResources.UpdatableDrawableId String mDrawableId; @NonNull private final @DevicePolicyResources.UpdatableDrawableStyle String mDrawableStyle; @NonNull private final @DevicePolicyResources.UpdatableDrawableSource String mDrawableSource; - private final @DrawableRes int mCallingPackageResourceId; + private final @DrawableRes int mResourceIdInCallingPackage; @NonNull private ParcelableResource mResource; /** @@ -47,25 +47,25 @@ public final class DevicePolicyDrawableResource implements Parcelable { * * <p>It will be used to update the drawable defined by {@code drawableId} with style * {@code drawableStyle} located in source {@code drawableSource} to the drawable with ID - * {@code callingPackageResourceId} in the calling package</p> + * {@code resourceIdInCallingPackage} in the calling package</p> * * @param drawableId The ID of the drawable to update. * @param drawableStyle The style of the drawable to update. * @param drawableSource The source of the drawable to update. - * @param callingPackageResourceId The ID of the drawable resource in the calling package to + * @param resourceIdInCallingPackage The ID of the drawable resource in the calling package to * use as an updated resource. * * @throws IllegalStateException if the resource with ID - * {@code callingPackageResourceId} doesn't exist in the {@code context} package. + * {@code resourceIdInCallingPackage} doesn't exist in the {@code context} package. */ public DevicePolicyDrawableResource( @NonNull Context context, @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource, - @DrawableRes int callingPackageResourceId) { - this(drawableId, drawableStyle, drawableSource, callingPackageResourceId, - new ParcelableResource(context, callingPackageResourceId, + @DrawableRes int resourceIdInCallingPackage) { + this(drawableId, drawableStyle, drawableSource, resourceIdInCallingPackage, + new ParcelableResource(context, resourceIdInCallingPackage, ParcelableResource.RESOURCE_TYPE_DRAWABLE)); } @@ -73,7 +73,7 @@ public final class DevicePolicyDrawableResource implements Parcelable { @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource, - @DrawableRes int callingPackageResourceId, + @DrawableRes int resourceIdInCallingPackage, @NonNull ParcelableResource resource) { Objects.requireNonNull(drawableId); @@ -84,7 +84,7 @@ public final class DevicePolicyDrawableResource implements Parcelable { this.mDrawableId = drawableId; this.mDrawableStyle = drawableStyle; this.mDrawableSource = drawableSource; - this.mCallingPackageResourceId = callingPackageResourceId; + this.mResourceIdInCallingPackage = resourceIdInCallingPackage; this.mResource = resource; } @@ -92,24 +92,24 @@ public final class DevicePolicyDrawableResource implements Parcelable { * Creates an object containing the required information for updating an enterprise drawable * resource using {@link DevicePolicyManager#setDrawables}. * <p>It will be used to update the drawable defined by {@code drawableId} with style - * {@code drawableStyle} to the drawable with ID {@code callingPackageResourceId} in the + * {@code drawableStyle} to the drawable with ID {@code resourceIdInCallingPackage} in the * calling package</p> * * @param drawableId The ID of the drawable to update. * @param drawableStyle The style of the drawable to update. - * @param callingPackageResourceId The ID of the drawable resource in the calling package to + * @param resourceIdInCallingPackage The ID of the drawable resource in the calling package to * use as an updated resource. * * @throws IllegalStateException if the resource with ID - * {@code callingPackageResourceId} doesn't exist in the calling package. + * {@code resourceIdInCallingPackage} doesn't exist in the calling package. */ public DevicePolicyDrawableResource( @NonNull Context context, @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, - @DrawableRes int callingPackageResourceId) { + @DrawableRes int resourceIdInCallingPackage) { this(context, drawableId, drawableStyle, Drawables.Source.UNDEFINED, - callingPackageResourceId); + resourceIdInCallingPackage); } /** @@ -144,8 +144,8 @@ public final class DevicePolicyDrawableResource implements Parcelable { * resource. */ @DrawableRes - public int getCallingPackageResourceId() { - return mCallingPackageResourceId; + public int getResourceIdInCallingPackage() { + return mResourceIdInCallingPackage; } /** @@ -166,14 +166,14 @@ public final class DevicePolicyDrawableResource implements Parcelable { return mDrawableId.equals(other.mDrawableId) && mDrawableStyle.equals(other.mDrawableStyle) && mDrawableSource.equals(other.mDrawableSource) - && mCallingPackageResourceId == other.mCallingPackageResourceId + && mResourceIdInCallingPackage == other.mResourceIdInCallingPackage && mResource.equals(other.mResource); } @Override public int hashCode() { - return Objects.hash( - mDrawableId, mDrawableStyle, mDrawableSource, mCallingPackageResourceId, mResource); + return Objects.hash(mDrawableId, mDrawableStyle, mDrawableSource, + mResourceIdInCallingPackage, mResource); } @Override @@ -186,7 +186,7 @@ public final class DevicePolicyDrawableResource implements Parcelable { dest.writeString(mDrawableId); dest.writeString(mDrawableStyle); dest.writeString(mDrawableSource); - dest.writeInt(mCallingPackageResourceId); + dest.writeInt(mResourceIdInCallingPackage); dest.writeTypedObject(mResource, flags); } @@ -197,11 +197,11 @@ public final class DevicePolicyDrawableResource implements Parcelable { String drawableId = in.readString(); String drawableStyle = in.readString(); String drawableSource = in.readString(); - int callingPackageResourceId = in.readInt(); + int resourceIdInCallingPackage = in.readInt(); ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR); return new DevicePolicyDrawableResource( - drawableId, drawableStyle, drawableSource, callingPackageResourceId, + drawableId, drawableStyle, drawableSource, resourceIdInCallingPackage, resource); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 753df3d66b3a..dcd9558e108b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -132,11 +132,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.function.Supplier; // TODO(b/172376923) - add CarDevicePolicyManager examples below (or remove reference to it). /** @@ -3611,42 +3611,45 @@ public class DevicePolicyManager { /** * Broadcast action: notify system apps (e.g. settings, SysUI, etc) that the device management - * resources with IDs {@link #EXTRA_RESOURCE_ID} has been updated, the updated resources can be + * resources with IDs {@link #EXTRA_RESOURCE_IDS} has been updated, the updated resources can be * retrieved using {@link #getDrawable} and {@code #getString}. * * <p>This broadcast is sent to registered receivers only. * - * <p> The following extras will be included to identify the type of resource being updated: - * <ul> - * <li>{@link #EXTRA_RESOURCE_TYPE_DRAWABLE} for drawable resources</li> - * <li>{@link #EXTRA_RESOURCE_TYPE_STRING} for string resources</li> - * </ul> + * <p> {@link #EXTRA_RESOURCE_TYPE} will be included to identify the type of resource being + * updated. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_DEVICE_POLICY_RESOURCE_UPDATED = "android.app.action.DEVICE_POLICY_RESOURCE_UPDATED"; /** - * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a - * resource of type {@link Drawable} is being updated. + * An {@code int} extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate the type + * of the resource being updated, the type can be {@link #EXTRA_RESOURCE_TYPE_DRAWABLE} or + * {@link #EXTRA_RESOURCE_TYPE_STRING} + */ + public static final String EXTRA_RESOURCE_TYPE = + "android.app.extra.RESOURCE_TYPE"; + + /** + * A {@code int} value for {@link #EXTRA_RESOURCE_TYPE} to indicate that a resource of type + * {@link Drawable} is being updated. */ - public static final String EXTRA_RESOURCE_TYPE_DRAWABLE = - "android.app.extra.RESOURCE_TYPE_DRAWABLE"; + public static final int EXTRA_RESOURCE_TYPE_DRAWABLE = 1; /** - * A boolean extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate that a - * resource of type {@link String} is being updated. + * A {@code int} value for {@link #EXTRA_RESOURCE_TYPE} to indicate that a resource of type + * {@link String} is being updated. */ - public static final String EXTRA_RESOURCE_TYPE_STRING = - "android.app.extra.RESOURCE_TYPE_STRING"; + public static final int EXTRA_RESOURCE_TYPE_STRING = 2; /** * An integer array extra for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to indicate which - * resource IDs (see {@link DevicePolicyResources.UpdatableDrawableId} and - * {@link DevicePolicyResources.UpdatableStringId}) have been updated. + * resource IDs (see {@link DevicePolicyResources.Drawables} and + * {@link DevicePolicyResources.Strings}) have been updated. */ - public static final String EXTRA_RESOURCE_ID = - "android.app.extra.RESOURCE_ID"; + public static final String EXTRA_RESOURCE_IDS = + "android.app.extra.RESOURCE_IDS"; /** @hide */ @NonNull @@ -7913,6 +7916,10 @@ public class DevicePolicyManager { /** * Returns the current runtime nearby notification streaming policy set by the device or profile * owner. + * <p> + * The caller must be the target user's device owner/profile owner or hold the + * {@link android.Manifest.permission#READ_NEARBY_STREAMING_POLICY READ_NEARBY_STREAMING_POLICY} + * permission. */ @RequiresPermission( value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, @@ -7956,6 +7963,10 @@ public class DevicePolicyManager { /** * Returns the current runtime nearby app streaming policy set by the device or profile owner. + * <p> + * The caller must be the target user's device owner/profile owner or hold the + * {@link android.Manifest.permission#READ_NEARBY_STREAMING_POLICY READ_NEARBY_STREAMING_POLICY} + * permission. */ @RequiresPermission( value = android.Manifest.permission.READ_NEARBY_STREAMING_POLICY, @@ -15123,7 +15134,7 @@ public class DevicePolicyManager { * the combination of {@link DevicePolicyDrawableResource#getDrawableId()} and * {@link DevicePolicyDrawableResource#getDrawableStyle()}, (see * {@link DevicePolicyResources.Drawables} and {@link DevicePolicyResources.Drawables.Style}) to - * the drawable with ID {@link DevicePolicyDrawableResource#getCallingPackageResourceId()}, + * the drawable with ID {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()}, * meaning any system UI surface calling {@link #getDrawable} * with {@code drawableId} and {@code drawableStyle} will get the new resource after this API * is called. @@ -15138,12 +15149,12 @@ public class DevicePolicyManager { * <p>Important notes to consider when using this API: * <ul> * <li>{@link #getDrawable} references the resource - * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} in the + * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always * available in the calling package as long as it is used as an updated resource. * <li>You still have to re-call {@code setDrawables} even if you only make changes to the * content of the resource with ID - * {@link DevicePolicyDrawableResource#getCallingPackageResourceId()} as the content might be + * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} as the content might be * cached and would need updating. * </ul> * @@ -15204,7 +15215,7 @@ public class DevicePolicyManager { * * <p>This API uses the screen density returned from {@link Resources#getConfiguration()}, to * set a different value use - * {@link #getDrawableForDensity(String, String, int, Callable)}. + * {@link #getDrawableForDensity(String, String, int, Supplier)}. * * <p>Callers should register for {@link #ACTION_DEVICE_POLICY_RESOURCE_UPDATED} to get * notified when a resource has been updated. @@ -15221,16 +15232,16 @@ public class DevicePolicyManager { public Drawable getDrawable( @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, - @NonNull Callable<Drawable> defaultDrawableLoader) { + @NonNull Supplier<Drawable> defaultDrawableLoader) { return getDrawable( drawableId, drawableStyle, Drawables.Source.UNDEFINED, defaultDrawableLoader); } /** - * Similar to {@link #getDrawable(String, String, Callable)}, but also accepts + * Similar to {@link #getDrawable(String, String, Supplier)}, but also accepts * a {@code drawableSource} (see {@link DevicePolicyResources.Drawables.Source}) which * could result in returning a different drawable than - * {@link #getDrawable(String, String, Callable)} + * {@link #getDrawable(String, String, Supplier)} * if an override was set for that specific source. * * <p>Calls to this API will not return {@code null} unless no updated drawable was found @@ -15250,7 +15261,7 @@ public class DevicePolicyManager { @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource, - @NonNull Callable<Drawable> defaultDrawableLoader) { + @NonNull Supplier<Drawable> defaultDrawableLoader) { Objects.requireNonNull(drawableId, "drawableId can't be null"); Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); @@ -15285,7 +15296,7 @@ public class DevicePolicyManager { } /** - * Similar to {@link #getDrawable(String, String, Callable)}, but also accepts + * Similar to {@link #getDrawable(String, String, Supplier)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * * <p>Calls to this API will not return {@code null} unless no updated drawable was found @@ -15307,7 +15318,7 @@ public class DevicePolicyManager { @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId, @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, int density, - @NonNull Callable<Drawable> defaultDrawableLoader) { + @NonNull Supplier<Drawable> defaultDrawableLoader) { return getDrawableForDensity( drawableId, drawableStyle, @@ -15317,7 +15328,7 @@ public class DevicePolicyManager { } /** - * Similar to {@link #getDrawable(String, String, String, Callable)}, but also accepts + * Similar to {@link #getDrawable(String, String, String, Supplier)}, but also accepts * {@code density}. See {@link Resources#getDrawableForDensity(int, int, Resources.Theme)}. * * <p>Calls to this API will not return {@code null} unless no updated drawable was found @@ -15341,7 +15352,7 @@ public class DevicePolicyManager { @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle, @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource, int density, - @NonNull Callable<Drawable> defaultDrawableLoader) { + @NonNull Supplier<Drawable> defaultDrawableLoader) { Objects.requireNonNull(drawableId, "drawableId can't be null"); Objects.requireNonNull(drawableStyle, "drawableStyle can't be null"); @@ -15372,7 +15383,7 @@ public class DevicePolicyManager { } /** - * Similar to {@link #getDrawable(String, String, String, Callable)} but returns an + * Similar to {@link #getDrawable(String, String, String, Supplier)} but returns an * {@link Icon} instead of a {@link Drawable}. * * @param drawableId The drawable ID to get the updated resource for. @@ -15414,7 +15425,7 @@ public class DevicePolicyManager { } /** - * Similar to {@link #getDrawable(String, String, Callable)} but returns an {@link Icon} + * Similar to {@link #getDrawable(String, String, Supplier)} but returns an {@link Icon} * instead of a {@link Drawable}. * * @param drawableId The drawable ID to get the updated resource for. @@ -15520,7 +15531,7 @@ public class DevicePolicyManager { @Nullable public String getString( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, - @NonNull Callable<String> defaultStringLoader) { + @NonNull Supplier<String> defaultStringLoader) { Objects.requireNonNull(stringId, "stringId can't be null"); Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); @@ -15547,7 +15558,7 @@ public class DevicePolicyManager { } /** - * Similar to {@link #getString(String, Callable)} but accepts {@code formatArgs} and returns a + * Similar to {@link #getString(String, Supplier)} but accepts {@code formatArgs} and returns a * localized formatted string, substituting the format arguments as defined in * {@link java.util.Formatter} and {@link java.lang.String#format}, (see * {@link Resources#getString(int, Object...)}). @@ -15567,7 +15578,7 @@ public class DevicePolicyManager { @SuppressLint("SamShouldBeLast") public String getString( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, - @NonNull Callable<String> defaultStringLoader, + @NonNull Supplier<String> defaultStringLoader, @NonNull Object... formatArgs) { Objects.requireNonNull(stringId, "stringId can't be null"); diff --git a/core/java/android/app/admin/DevicePolicyStringResource.java b/core/java/android/app/admin/DevicePolicyStringResource.java index 5f09bfdcf331..b36f140807aa 100644 --- a/core/java/android/app/admin/DevicePolicyStringResource.java +++ b/core/java/android/app/admin/DevicePolicyStringResource.java @@ -35,7 +35,7 @@ import java.util.Objects; @SystemApi public final class DevicePolicyStringResource implements Parcelable { @NonNull private final @DevicePolicyResources.UpdatableStringId String mStringId; - private final @StringRes int mCallingPackageResourceId; + private final @StringRes int mResourceIdInCallingPackage; @NonNull private ParcelableResource mResource; /** @@ -43,32 +43,32 @@ public final class DevicePolicyStringResource implements Parcelable { * resource using {@link DevicePolicyManager#setStrings}. * * <p>It will be used to update the string defined by {@code stringId} to the string with ID - * {@code callingPackageResourceId} in the calling package</p> + * {@code resourceIdInCallingPackage} in the calling package</p> * * @param stringId The ID of the string to update. - * @param callingPackageResourceId The ID of the {@link StringRes} in the calling package to + * @param resourceIdInCallingPackage The ID of the {@link StringRes} in the calling package to * use as an updated resource. * - * @throws IllegalStateException if the resource with ID {@code callingPackageResourceId} + * @throws IllegalStateException if the resource with ID {@code resourceIdInCallingPackage} * doesn't exist in the {@code context} package. */ public DevicePolicyStringResource( @NonNull Context context, @NonNull @DevicePolicyResources.UpdatableStringId String stringId, - @StringRes int callingPackageResourceId) { - this(stringId, callingPackageResourceId, new ParcelableResource( - context, callingPackageResourceId, ParcelableResource.RESOURCE_TYPE_STRING)); + @StringRes int resourceIdInCallingPackage) { + this(stringId, resourceIdInCallingPackage, new ParcelableResource( + context, resourceIdInCallingPackage, ParcelableResource.RESOURCE_TYPE_STRING)); } private DevicePolicyStringResource( @NonNull @DevicePolicyResources.UpdatableStringId String stringId, - @StringRes int callingPackageResourceId, + @StringRes int resourceIdInCallingPackage, @NonNull ParcelableResource resource) { Objects.requireNonNull(stringId, "stringId must be provided."); Objects.requireNonNull(resource, "ParcelableResource must be provided."); this.mStringId = stringId; - this.mCallingPackageResourceId = callingPackageResourceId; + this.mResourceIdInCallingPackage = resourceIdInCallingPackage; this.mResource = resource; } @@ -85,8 +85,8 @@ public final class DevicePolicyStringResource implements Parcelable { * Returns the ID of the {@link StringRes} in the calling package to use as an updated * resource. */ - public int getCallingPackageResourceId() { - return mCallingPackageResourceId; + public int getResourceIdInCallingPackage() { + return mResourceIdInCallingPackage; } /** @@ -105,13 +105,13 @@ public final class DevicePolicyStringResource implements Parcelable { if (o == null || getClass() != o.getClass()) return false; DevicePolicyStringResource other = (DevicePolicyStringResource) o; return mStringId == other.mStringId - && mCallingPackageResourceId == other.mCallingPackageResourceId + && mResourceIdInCallingPackage == other.mResourceIdInCallingPackage && mResource.equals(other.mResource); } @Override public int hashCode() { - return Objects.hash(mStringId, mCallingPackageResourceId, mResource); + return Objects.hash(mStringId, mResourceIdInCallingPackage, mResource); } @Override @@ -122,7 +122,7 @@ public final class DevicePolicyStringResource implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mStringId); - dest.writeInt(mCallingPackageResourceId); + dest.writeInt(mResourceIdInCallingPackage); dest.writeTypedObject(mResource, flags); } @@ -131,10 +131,10 @@ public final class DevicePolicyStringResource implements Parcelable { @Override public DevicePolicyStringResource createFromParcel(Parcel in) { String stringId = in.readString(); - int callingPackageResourceId = in.readInt(); + int resourceIdInCallingPackage = in.readInt(); ParcelableResource resource = in.readTypedObject(ParcelableResource.CREATOR); - return new DevicePolicyStringResource(stringId, callingPackageResourceId, resource); + return new DevicePolicyStringResource(stringId, resourceIdInCallingPackage, resource); } @Override diff --git a/core/java/android/app/admin/ParcelableResource.java b/core/java/android/app/admin/ParcelableResource.java index 0b1b166add40..bcae2846ad42 100644 --- a/core/java/android/app/admin/ParcelableResource.java +++ b/core/java/android/app/admin/ParcelableResource.java @@ -39,7 +39,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; -import java.util.concurrent.Callable; +import java.util.function.Supplier; /** * Used to store the required information to load a resource that was updated using @@ -179,7 +179,7 @@ public final class ParcelableResource implements Parcelable { public Drawable getDrawable( Context context, int density, - @NonNull Callable<Drawable> defaultDrawableLoader) { + @NonNull Supplier<Drawable> defaultDrawableLoader) { // TODO(b/203548565): properly handle edge case when the device manager role holder is // unavailable because it's being updated. try { @@ -203,7 +203,7 @@ public final class ParcelableResource implements Parcelable { @Nullable public String getString( Context context, - @NonNull Callable<String> defaultStringLoader) { + @NonNull Supplier<String> defaultStringLoader) { // TODO(b/203548565): properly handle edge case when the device manager role holder is // unavailable because it's being updated. try { @@ -227,7 +227,7 @@ public final class ParcelableResource implements Parcelable { @Nullable public String getString( Context context, - @NonNull Callable<String> defaultStringLoader, + @NonNull Supplier<String> defaultStringLoader, @NonNull Object... formatArgs) { // TODO(b/203548565): properly handle edge case when the device manager role holder is // unavailable because it's being updated. @@ -268,26 +268,18 @@ public final class ParcelableResource implements Parcelable { * returns the {@link Drawable} loaded from calling {@code defaultDrawableLoader}. */ @Nullable - public static Drawable loadDefaultDrawable(@NonNull Callable<Drawable> defaultDrawableLoader) { - try { - Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null"); - return defaultDrawableLoader.call(); - } catch (Exception e) { - throw new RuntimeException("Couldn't load default drawable: ", e); - } + public static Drawable loadDefaultDrawable(@NonNull Supplier<Drawable> defaultDrawableLoader) { + Objects.requireNonNull(defaultDrawableLoader, "defaultDrawableLoader can't be null"); + return defaultDrawableLoader.get(); } /** * returns the {@link String} loaded from calling {@code defaultStringLoader}. */ @Nullable - public static String loadDefaultString(@NonNull Callable<String> defaultStringLoader) { - try { - Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); - return defaultStringLoader.call(); - } catch (Exception e) { - throw new RuntimeException("Couldn't load default string: ", e); - } + public static String loadDefaultString(@NonNull Supplier<String> defaultStringLoader) { + Objects.requireNonNull(defaultStringLoader, "defaultStringLoader can't be null"); + return defaultStringLoader.get(); } /** diff --git a/core/java/android/app/smartspace/SmartspaceTarget.java b/core/java/android/app/smartspace/SmartspaceTarget.java index fd7088f48aed..be077435b080 100644 --- a/core/java/android/app/smartspace/SmartspaceTarget.java +++ b/core/java/android/app/smartspace/SmartspaceTarget.java @@ -159,6 +159,24 @@ public final class SmartspaceTarget implements Parcelable { public static final int FEATURE_TIMER = 21; public static final int FEATURE_STOPWATCH = 22; public static final int FEATURE_UPCOMING_ALARM = 23; + public static final int FEATURE_GAS_STATION_PAYMENT = 24; + public static final int FEATURE_PAIRED_DEVICE_STATE = 25; + public static final int FEATURE_DRIVING_MODE = 26; + public static final int FEATURE_SLEEP_SUMMARY = 27; + public static final int FEATURE_FLASHLIGHT = 28; + public static final int FEATURE_TIME_TO_LEAVE = 29; + public static final int FEATURE_DOORBELL = 30; + public static final int FEATURE_MEDIA_RESUME = 31; + public static final int FEATURE_CROSS_DEVICE_TIMER = 32; + public static final int FEATURE_SEVERE_WEATHER_ALERT = 33; + public static final int FEATURE_HOLIDAY_ALARM = 34; + public static final int FEATURE_SAFETY_CHECK = 35; + public static final int FEATURE_MEDIA_HEADS_UP = 36; + public static final int FEATURE_STEP_COUNTING = 37; + public static final int FEATURE_EARTHQUAKE_ALERT = 38; + public static final int FEATURE_STEP_DATE = 39; + public static final int FEATURE_BLAZE_BUILD_PROGRESS = 40; + public static final int FEATURE_EARTHQUAKE_OCCURRED = 41; /** * @hide @@ -187,7 +205,25 @@ public final class SmartspaceTarget implements Parcelable { FEATURE_PACKAGE_TRACKING, FEATURE_TIMER, FEATURE_STOPWATCH, - FEATURE_UPCOMING_ALARM + FEATURE_UPCOMING_ALARM, + FEATURE_GAS_STATION_PAYMENT, + FEATURE_PAIRED_DEVICE_STATE, + FEATURE_DRIVING_MODE, + FEATURE_SLEEP_SUMMARY, + FEATURE_FLASHLIGHT, + FEATURE_TIME_TO_LEAVE, + FEATURE_DOORBELL, + FEATURE_MEDIA_RESUME, + FEATURE_CROSS_DEVICE_TIMER, + FEATURE_SEVERE_WEATHER_ALERT, + FEATURE_HOLIDAY_ALARM, + FEATURE_SAFETY_CHECK, + FEATURE_MEDIA_HEADS_UP, + FEATURE_STEP_COUNTING, + FEATURE_EARTHQUAKE_ALERT, + FEATURE_STEP_DATE, + FEATURE_BLAZE_BUILD_PROGRESS, + FEATURE_EARTHQUAKE_OCCURRED }) @Retention(RetentionPolicy.SOURCE) public @interface FeatureType { diff --git a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java index 584b17605a87..e3cb67a8284c 100644 --- a/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/BaseTemplateData.java @@ -20,10 +20,12 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.app.smartspace.SmartspaceTarget.FeatureType; import android.app.smartspace.SmartspaceTarget.UiTemplateType; import android.app.smartspace.SmartspaceUtils; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import java.util.Objects; @@ -57,81 +59,39 @@ public class BaseTemplateData implements Parcelable { /** * Title text and title icon are shown at the first row. When both are absent, the date view * will be used, which has its own tap action applied to the title area. + * + * Primary tap action for the entire card, including the blank spaces, except: 1. When title is + * absent, the date view's default tap action is used; 2. Subtitle/Supplemental subtitle uses + * its own tap action if being set; 3. Secondary card uses its own tap action if being set. */ @Nullable - private final Text mTitleText; + private final SubItemInfo mPrimaryItem; - @Nullable - private final Icon mTitleIcon; /** Subtitle text and icon are shown at the second row. */ @Nullable - private final Text mSubtitleText; - - @Nullable - private final Icon mSubtitleIcon; - - /** - * Primary tap action for the entire card, including the blank spaces, except: 1. When title is - * absent, the date view's default tap action is used; 2. Supplemental subtitle uses its own tap - * action if being set; 3. Secondary card uses its own tap action if being set. - */ - @Nullable - private final TapAction mPrimaryTapAction; - - /** - * Primary logging info for the entire card. This will only be used when rendering a sub card - * within the base card. For the base card itself, BcSmartspaceCardLoggingInfo should be used, - * which has the display-specific info (e.g. display surface). - */ - @Nullable - private final SubItemLoggingInfo mPrimaryLoggingInfo; + private final SubItemInfo mSubtitleItem; /** * Supplemental subtitle text and icon are shown at the second row following the subtitle text. * Mainly used for weather info on non-weather card. */ @Nullable - private final Text mSupplementalSubtitleText; - - @Nullable - private final Icon mSupplementalSubtitleIcon; + private final SubItemInfo mSubtitleSupplementalItem; /** - * Tap action for the supplemental subtitle's text and icon. Uses the primary tap action if - * not being set. + * Supplemental line is shown at the third row. */ @Nullable - private final TapAction mSupplementalSubtitleTapAction; + private final SubItemInfo mSupplementalLineItem; /** - * Logging info for the supplemental subtitle's are. Uses the primary logging info if not being - * set. + * Supplemental alarm item is specifically used for holiday alarm, which is appended to "next + * alarm". This is also shown at the third row, but won't be shown the same time with + * mSupplementalLineItem. */ @Nullable - private final SubItemLoggingInfo mSupplementalSubtitleLoggingInfo; - - @Nullable - private final Text mSupplementalText; - - @Nullable - private final Icon mSupplementalIcon; - - @Nullable - private final TapAction mSupplementalTapAction; - - /** - * Logging info for the supplemental line. Uses the primary logging info if not being set. - */ - @Nullable - private final SubItemLoggingInfo mSupplementalLoggingInfo; - - /** - * Supplemental alarm text is specifically used for holiday alarm, which is appended to "next - * alarm". - */ - @Nullable - private final Text mSupplementalAlarmText; + private final SubItemInfo mSupplementalAlarmItem; /** * The layout weight info for the card, which indicates how much space it should occupy on the @@ -141,21 +101,11 @@ public class BaseTemplateData implements Parcelable { BaseTemplateData(@NonNull Parcel in) { mTemplateType = in.readInt(); - mTitleText = in.readTypedObject(Text.CREATOR); - mTitleIcon = in.readTypedObject(Icon.CREATOR); - mSubtitleText = in.readTypedObject(Text.CREATOR); - mSubtitleIcon = in.readTypedObject(Icon.CREATOR); - mPrimaryTapAction = in.readTypedObject(TapAction.CREATOR); - mPrimaryLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR); - mSupplementalSubtitleText = in.readTypedObject(Text.CREATOR); - mSupplementalSubtitleIcon = in.readTypedObject(Icon.CREATOR); - mSupplementalSubtitleTapAction = in.readTypedObject(TapAction.CREATOR); - mSupplementalSubtitleLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR); - mSupplementalText = in.readTypedObject(Text.CREATOR); - mSupplementalIcon = in.readTypedObject(Icon.CREATOR); - mSupplementalTapAction = in.readTypedObject(TapAction.CREATOR); - mSupplementalLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR); - mSupplementalAlarmText = in.readTypedObject(Text.CREATOR); + mPrimaryItem = in.readTypedObject(SubItemInfo.CREATOR); + mSubtitleItem = in.readTypedObject(SubItemInfo.CREATOR); + mSubtitleSupplementalItem = in.readTypedObject(SubItemInfo.CREATOR); + mSupplementalLineItem = in.readTypedObject(SubItemInfo.CREATOR); + mSupplementalAlarmItem = in.readTypedObject(SubItemInfo.CREATOR); mLayoutWeight = in.readInt(); } @@ -164,38 +114,18 @@ public class BaseTemplateData implements Parcelable { * SmartspaceDefaultUiTemplateData.Builder. */ BaseTemplateData(@UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight) { mTemplateType = templateType; - mTitleText = titleText; - mTitleIcon = titleIcon; - mSubtitleText = subtitleText; - mSubtitleIcon = subtitleIcon; - mPrimaryTapAction = primaryTapAction; - mPrimaryLoggingInfo = primaryLoggingInfo; - mSupplementalSubtitleText = supplementalSubtitleText; - mSupplementalSubtitleIcon = supplementalSubtitleIcon; - mSupplementalSubtitleTapAction = supplementalSubtitleTapAction; - mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo; - mSupplementalText = supplementalText; - mSupplementalIcon = supplementalIcon; - mSupplementalTapAction = supplementalTapAction; - mSupplementalLoggingInfo = supplementalLoggingInfo; - mSupplementalAlarmText = supplementalAlarmText; + mPrimaryItem = primaryItem; + mSubtitleItem = subtitleItem; + mSubtitleSupplementalItem = subtitleSupplementalItem; + mSupplementalLineItem = supplementalLineItem; + mSupplementalAlarmItem = supplementalAlarmItem; mLayoutWeight = layoutWeight; } @@ -205,94 +135,34 @@ public class BaseTemplateData implements Parcelable { return mTemplateType; } - /** Returns the title's text. */ + /** Returns the primary item (the first line). */ @Nullable - public Text getTitleText() { - return mTitleText; + public SubItemInfo getPrimaryItem() { + return mPrimaryItem; } - /** Returns the title's icon. */ + /** Returns the subtitle item (the second line). */ @Nullable - public Icon getTitleIcon() { - return mTitleIcon; + public SubItemInfo getSubtitleItem() { + return mSubtitleItem; } - /** Returns the subtitle's text. */ + /** Returns the subtitle's supplemental item (the second line following the subtitle). */ @Nullable - public Text getSubtitleText() { - return mSubtitleText; + public SubItemInfo getSubtitleSupplementalItem() { + return mSubtitleSupplementalItem; } - /** Returns the subtitle's icon. */ + /** Returns the supplemental line item (the 3rd line). */ @Nullable - public Icon getSubtitleIcon() { - return mSubtitleIcon; + public SubItemInfo getSupplementalLineItem() { + return mSupplementalLineItem; } - /** Returns the card's primary tap action. */ + /** Returns the supplemental alarm item (the 3rd line). */ @Nullable - public TapAction getPrimaryTapAction() { - return mPrimaryTapAction; - } - - /** Returns the card's primary logging info. */ - @Nullable - public SubItemLoggingInfo getPrimaryLoggingInfo() { - return mPrimaryLoggingInfo; - } - - /** Returns the supplemental subtitle's text. */ - @Nullable - public Text getSupplementalSubtitleText() { - return mSupplementalSubtitleText; - } - - /** Returns the supplemental subtitle's icon. */ - @Nullable - public Icon getSupplementalSubtitleIcon() { - return mSupplementalSubtitleIcon; - } - - /** Returns the supplemental subtitle's tap action. Can be null if not being set. */ - @Nullable - public TapAction getSupplementalSubtitleTapAction() { - return mSupplementalSubtitleTapAction; - } - - /** Returns the card's supplemental title's logging info. */ - @Nullable - public SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() { - return mSupplementalSubtitleLoggingInfo; - } - - /** Returns the supplemental text. */ - @Nullable - public Text getSupplementalText() { - return mSupplementalText; - } - - /** Returns the supplemental icon. */ - @Nullable - public Icon getSupplementalIcon() { - return mSupplementalIcon; - } - - /** Returns the supplemental line's tap action. Can be null if not being set. */ - @Nullable - public TapAction getSupplementalTapAction() { - return mSupplementalTapAction; - } - - /** Returns the card's supplemental line logging info. */ - @Nullable - public SubItemLoggingInfo getSupplementalLoggingInfo() { - return mSupplementalLoggingInfo; - } - - /** Returns the supplemental alarm text. */ - @Nullable - public Text getSupplementalAlarmText() { - return mSupplementalAlarmText; + public SubItemInfo getSupplementalAlarmItem() { + return mSupplementalAlarmItem; } /** Returns the card layout weight info. Default weight is 0. */ @@ -325,21 +195,11 @@ public class BaseTemplateData implements Parcelable { @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(mTemplateType); - out.writeTypedObject(mTitleText, flags); - out.writeTypedObject(mTitleIcon, flags); - out.writeTypedObject(mSubtitleText, flags); - out.writeTypedObject(mSubtitleIcon, flags); - out.writeTypedObject(mPrimaryTapAction, flags); - out.writeTypedObject(mPrimaryLoggingInfo, flags); - out.writeTypedObject(mSupplementalSubtitleText, flags); - out.writeTypedObject(mSupplementalSubtitleIcon, flags); - out.writeTypedObject(mSupplementalSubtitleTapAction, flags); - out.writeTypedObject(mSupplementalSubtitleLoggingInfo, flags); - out.writeTypedObject(mSupplementalText, flags); - out.writeTypedObject(mSupplementalIcon, flags); - out.writeTypedObject(mSupplementalTapAction, flags); - out.writeTypedObject(mSupplementalLoggingInfo, flags); - out.writeTypedObject(mSupplementalAlarmText, flags); + out.writeTypedObject(mPrimaryItem, flags); + out.writeTypedObject(mSubtitleItem, flags); + out.writeTypedObject(mSubtitleSupplementalItem, flags); + out.writeTypedObject(mSupplementalLineItem, flags); + out.writeTypedObject(mSupplementalAlarmItem, flags); out.writeInt(mLayoutWeight); } @@ -348,58 +208,29 @@ public class BaseTemplateData implements Parcelable { if (this == o) return true; if (!(o instanceof BaseTemplateData)) return false; BaseTemplateData that = (BaseTemplateData) o; - return mTemplateType == that.mTemplateType && SmartspaceUtils.isEqual(mTitleText, - that.mTitleText) - && Objects.equals(mTitleIcon, that.mTitleIcon) - && SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText) - && Objects.equals(mSubtitleIcon, that.mSubtitleIcon) - && Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction) - && Objects.equals(mPrimaryLoggingInfo, that.mPrimaryLoggingInfo) - && SmartspaceUtils.isEqual(mSupplementalSubtitleText, - that.mSupplementalSubtitleText) - && Objects.equals(mSupplementalSubtitleIcon, that.mSupplementalSubtitleIcon) - && Objects.equals(mSupplementalSubtitleTapAction, - that.mSupplementalSubtitleTapAction) - && Objects.equals(mSupplementalSubtitleLoggingInfo, - that.mSupplementalSubtitleLoggingInfo) - && SmartspaceUtils.isEqual(mSupplementalText, - that.mSupplementalText) - && Objects.equals(mSupplementalIcon, that.mSupplementalIcon) - && Objects.equals(mSupplementalTapAction, that.mSupplementalTapAction) - && Objects.equals(mSupplementalLoggingInfo, that.mSupplementalLoggingInfo) - && SmartspaceUtils.isEqual(mSupplementalAlarmText, that.mSupplementalAlarmText) - && mLayoutWeight == that.mLayoutWeight; + return mTemplateType == that.mTemplateType && mLayoutWeight == that.mLayoutWeight + && Objects.equals(mPrimaryItem, that.mPrimaryItem) + && Objects.equals(mSubtitleItem, that.mSubtitleItem) + && Objects.equals(mSubtitleSupplementalItem, that.mSubtitleSupplementalItem) + && Objects.equals(mSupplementalLineItem, that.mSupplementalLineItem) + && Objects.equals(mSupplementalAlarmItem, that.mSupplementalAlarmItem); } @Override public int hashCode() { - return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon, - mPrimaryTapAction, mPrimaryLoggingInfo, mSupplementalSubtitleText, - mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction, - mSupplementalSubtitleLoggingInfo, - mSupplementalText, mSupplementalIcon, mSupplementalTapAction, - mSupplementalLoggingInfo, mSupplementalAlarmText, mLayoutWeight); + return Objects.hash(mTemplateType, mPrimaryItem, mSubtitleItem, mSubtitleSupplementalItem, + mSupplementalLineItem, mSupplementalAlarmItem, mLayoutWeight); } @Override public String toString() { - return "SmartspaceDefaultUiTemplateData{" + return "BaseTemplateData{" + "mTemplateType=" + mTemplateType - + ", mTitleText=" + mTitleText - + ", mTitleIcon=" + mTitleIcon - + ", mSubtitleText=" + mSubtitleText - + ", mSubTitleIcon=" + mSubtitleIcon - + ", mPrimaryTapAction=" + mPrimaryTapAction - + ", mPrimaryLoggingInfo=" + mPrimaryLoggingInfo - + ", mSupplementalSubtitleText=" + mSupplementalSubtitleText - + ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon - + ", mSupplementalSubtitleTapAction=" + mSupplementalSubtitleTapAction - + ", mSupplementalSubtitleLoggingInfo=" + mSupplementalSubtitleLoggingInfo - + ", mSupplementalText=" + mSupplementalText - + ", mSupplementalIcon=" + mSupplementalIcon - + ", mSupplementalTapAction=" + mSupplementalTapAction - + ", mSupplementalLoggingInfo=" + mSupplementalLoggingInfo - + ", mSupplementalAlarmText=" + mSupplementalAlarmText + + ", mPrimaryItem=" + mPrimaryItem + + ", mSubtitleItem=" + mSubtitleItem + + ", mSubtitleSupplementalItem=" + mSubtitleSupplementalItem + + ", mSupplementalLineItem=" + mSupplementalLineItem + + ", mSupplementalAlarmItem=" + mSupplementalAlarmItem + ", mLayoutWeight=" + mLayoutWeight + '}'; } @@ -414,21 +245,12 @@ public class BaseTemplateData implements Parcelable { public static class Builder { @UiTemplateType private final int mTemplateType; - private Text mTitleText; - private Icon mTitleIcon; - private Text mSubtitleText; - private Icon mSubtitleIcon; - private TapAction mPrimaryTapAction; - private SubItemLoggingInfo mPrimaryLoggingInfo; - private Text mSupplementalSubtitleText; - private Icon mSupplementalSubtitleIcon; - private TapAction mSupplementalSubtitleTapAction; - private SubItemLoggingInfo mSupplementalSubtitleLoggingInfo; - private Text mSupplementalText; - private Icon mSupplementalIcon; - private TapAction mSupplementalTapAction; - private SubItemLoggingInfo mSupplementalLoggingInfo; - private Text mSupplementalAlarmText; + + private SubItemInfo mPrimaryItem; + private SubItemInfo mSubtitleItem; + private SubItemInfo mSubtitleSupplementalItem; + private SubItemInfo mSupplementalLineItem; + private SubItemInfo mSupplementalAlarmItem; private int mLayoutWeight; /** @@ -451,106 +273,36 @@ public class BaseTemplateData implements Parcelable { /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - Text getTitleText() { - return mTitleText; + SubItemInfo getPrimaryItem() { + return mPrimaryItem; } /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - Icon getTitleIcon() { - return mTitleIcon; + SubItemInfo getSubtitleItem() { + return mSubtitleItem; } /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - Text getSubtitleText() { - return mSubtitleText; + SubItemInfo getSubtitleSupplemtnalItem() { + return mSubtitleSupplementalItem; } /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - Icon getSubtitleIcon() { - return mSubtitleIcon; + SubItemInfo getSupplementalLineItem() { + return mSupplementalLineItem; } /** Should ONLY be used by the subclasses */ @Nullable @SuppressLint("GetterOnBuilder") - TapAction getPrimaryTapAction() { - return mPrimaryTapAction; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - SubItemLoggingInfo getPrimaryLoggingInfo() { - return mPrimaryLoggingInfo; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - Text getSupplementalSubtitleText() { - return mSupplementalSubtitleText; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - Icon getSupplementalSubtitleIcon() { - return mSupplementalSubtitleIcon; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - TapAction getSupplementalSubtitleTapAction() { - return mSupplementalSubtitleTapAction; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - SubItemLoggingInfo getSupplementalSubtitleLoggingInfo() { - return mSupplementalSubtitleLoggingInfo; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - Text getSupplementalText() { - return mSupplementalText; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - Icon getSupplementalIcon() { - return mSupplementalIcon; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - TapAction getSupplementalTapAction() { - return mSupplementalTapAction; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - SubItemLoggingInfo getSupplementalLoggingInfo() { - return mSupplementalLoggingInfo; - } - - /** Should ONLY be used by the subclasses */ - @Nullable - @SuppressLint("GetterOnBuilder") - Text getSupplementalAlarmText() { - return mSupplementalAlarmText; + SubItemInfo getSupplementalAlarmItem() { + return mSupplementalAlarmItem; } /** Should ONLY be used by the subclasses */ @@ -560,169 +312,255 @@ public class BaseTemplateData implements Parcelable { } /** - * Sets the card title. + * Sets the card primary item. */ @NonNull - public Builder setTitleText(@NonNull Text titleText) { - mTitleText = titleText; + public Builder setPrimaryItem(@NonNull SubItemInfo primaryItem) { + mPrimaryItem = primaryItem; return this; } /** - * Sets the card title icon. + * Sets the card subtitle item. */ @NonNull - public Builder setTitleIcon(@NonNull Icon titleIcon) { - mTitleIcon = titleIcon; + public Builder setSubtitleItem(@NonNull SubItemInfo subtitleItem) { + mSubtitleItem = subtitleItem; return this; } /** - * Sets the card subtitle. + * Sets the card subtitle's supplemental item. */ @NonNull - public Builder setSubtitleText(@NonNull Text subtitleText) { - mSubtitleText = subtitleText; + public Builder setSubtitleSupplementalItem(@NonNull SubItemInfo subtitleSupplementalItem) { + mSubtitleSupplementalItem = subtitleSupplementalItem; return this; } /** - * Sets the card subtitle icon. + * Sets the card supplemental line item. */ @NonNull - public Builder setSubtitleIcon(@NonNull Icon subtitleIcon) { - mSubtitleIcon = subtitleIcon; + public Builder setSupplementalLineItem(@NonNull SubItemInfo supplementalLineItem) { + mSupplementalLineItem = supplementalLineItem; return this; } /** - * Sets the card primary tap action. + * Sets the card supplemental alarm item. */ @NonNull - public Builder setPrimaryTapAction(@NonNull TapAction primaryTapAction) { - mPrimaryTapAction = primaryTapAction; + public Builder setSupplementalAlarmItem(@NonNull SubItemInfo supplementalAlarmItem) { + mSupplementalAlarmItem = supplementalAlarmItem; return this; } /** - * Sets the card primary logging info. + * Sets the layout weight. */ @NonNull - public Builder setPrimaryLoggingInfo(@NonNull SubItemLoggingInfo primaryLoggingInfo) { - mPrimaryLoggingInfo = primaryLoggingInfo; + public Builder setLayoutWeight(int layoutWeight) { + mLayoutWeight = layoutWeight; return this; } /** - * Sets the supplemental subtitle text. + * Builds a new SmartspaceDefaultUiTemplateData instance. */ @NonNull - public Builder setSupplementalSubtitleText( - @NonNull Text supplementalSubtitleText) { - mSupplementalSubtitleText = supplementalSubtitleText; - return this; + public BaseTemplateData build() { + return new BaseTemplateData( + mTemplateType, + mPrimaryItem, + mSubtitleItem, + mSubtitleSupplementalItem, + mSupplementalLineItem, + mSupplementalAlarmItem, + mLayoutWeight); } + } - /** - * Sets the supplemental subtitle icon. - */ - @NonNull - public Builder setSupplementalSubtitleIcon( - @NonNull Icon supplementalSubtitleIcon) { - mSupplementalSubtitleIcon = supplementalSubtitleIcon; - return this; + /** + * Holds all the rendering and logging info needed for a sub item within the base card. + */ + public static final class SubItemInfo implements Parcelable { + + /** The text information for the subitem, which will be rendered as it's text content. */ + @Nullable + private final Text mText; + + /** The icon for the subitem, which will be rendered as a drawable in front of the text. */ + @Nullable + private final Icon mIcon; + + /** The tap action for the subitem. */ + @Nullable + private final TapAction mTapAction; + + /** The logging info for the subitem. */ + @Nullable + private final SubItemLoggingInfo mLoggingInfo; + + SubItemInfo(@NonNull Parcel in) { + mText = in.readTypedObject(Text.CREATOR); + mIcon = in.readTypedObject(Icon.CREATOR); + mTapAction = in.readTypedObject(TapAction.CREATOR); + mLoggingInfo = in.readTypedObject(SubItemLoggingInfo.CREATOR); } - /** - * Sets the supplemental subtitle tap action. {@code mPrimaryTapAction} will be used if not - * being set. - */ - @NonNull - public Builder setSupplementalSubtitleTapAction( - @NonNull TapAction supplementalSubtitleTapAction) { - mSupplementalSubtitleTapAction = supplementalSubtitleTapAction; - return this; + private SubItemInfo(@Nullable Text text, + @Nullable Icon icon, + @Nullable TapAction tapAction, + @Nullable SubItemLoggingInfo loggingInfo) { + mText = text; + mIcon = icon; + mTapAction = tapAction; + mLoggingInfo = loggingInfo; } - /** - * Sets the card supplemental title's logging info. - */ - @NonNull - public Builder setSupplementalSubtitleLoggingInfo( - @NonNull SubItemLoggingInfo supplementalSubtitleLoggingInfo) { - mSupplementalSubtitleLoggingInfo = supplementalSubtitleLoggingInfo; - return this; + /** Returns the subitem's text. */ + @Nullable + public Text getText() { + return mText; } - /** - * Sets the supplemental text. - */ - @NonNull - public Builder setSupplementalText(@NonNull Text supplementalText) { - mSupplementalText = supplementalText; - return this; + /** Returns the subitem's icon. */ + @Nullable + public Icon getIcon() { + return mIcon; } - /** - * Sets the supplemental icon. - */ - @NonNull - public Builder setSupplementalIcon(@NonNull Icon supplementalIcon) { - mSupplementalIcon = supplementalIcon; - return this; + /** Returns the subitem's tap action. */ + @Nullable + public TapAction getTapAction() { + return mTapAction; } - /** - * Sets the supplemental line tap action. {@code mPrimaryTapAction} will be used if not - * being set. - */ - @NonNull - public Builder setSupplementalTapAction(@NonNull TapAction supplementalTapAction) { - mSupplementalTapAction = supplementalTapAction; - return this; + /** Returns the subitem's logging info. */ + @Nullable + public SubItemLoggingInfo getLoggingInfo() { + return mLoggingInfo; } /** - * Sets the card supplemental line's logging info. + * @see Parcelable.Creator */ @NonNull - public Builder setSupplementalLoggingInfo( - @NonNull SubItemLoggingInfo supplementalLoggingInfo) { - mSupplementalLoggingInfo = supplementalLoggingInfo; - return this; + public static final Creator<SubItemInfo> CREATOR = + new Creator<SubItemInfo>() { + @Override + public SubItemInfo createFromParcel(Parcel in) { + return new SubItemInfo(in); + } + + @Override + public SubItemInfo[] newArray(int size) { + return new SubItemInfo[size]; + } + }; + + @Override + public int describeContents() { + return 0; } - /** - * Sets the supplemental alarm text. - */ - @NonNull - public Builder setSupplementalAlarmText(@NonNull Text supplementalAlarmText) { - mSupplementalAlarmText = supplementalAlarmText; - return this; + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeTypedObject(mText, flags); + out.writeTypedObject(mIcon, flags); + out.writeTypedObject(mTapAction, flags); + out.writeTypedObject(mLoggingInfo, flags); } - /** - * Sets the layout weight. - */ - @NonNull - public Builder setLayoutWeight(int layoutWeight) { - mLayoutWeight = layoutWeight; - return this; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SubItemInfo)) return false; + SubItemInfo that = (SubItemInfo) o; + return SmartspaceUtils.isEqual(mText, that.mText) && Objects.equals(mIcon, + that.mIcon) && Objects.equals(mTapAction, that.mTapAction) + && Objects.equals(mLoggingInfo, that.mLoggingInfo); + } + + @Override + public int hashCode() { + return Objects.hash(mText, mIcon, mTapAction, mLoggingInfo); + } + + @Override + public String toString() { + return "SubItemInfo{" + + "mText=" + mText + + ", mIcon=" + mIcon + + ", mTapAction=" + mTapAction + + ", mLoggingInfo=" + mLoggingInfo + + '}'; } /** - * Builds a new SmartspaceDefaultUiTemplateData instance. + * A builder for {@link SubItemInfo} object. + * + * @hide */ - @NonNull - public BaseTemplateData build() { - return new BaseTemplateData(mTemplateType, mTitleText, mTitleIcon, - mSubtitleText, mSubtitleIcon, mPrimaryTapAction, - mPrimaryLoggingInfo, - mSupplementalSubtitleText, mSupplementalSubtitleIcon, - mSupplementalSubtitleTapAction, mSupplementalSubtitleLoggingInfo, - mSupplementalText, mSupplementalIcon, - mSupplementalTapAction, mSupplementalLoggingInfo, - mSupplementalAlarmText, mLayoutWeight); + @SystemApi + public static final class Builder { + + private Text mText; + private Icon mIcon; + private TapAction mTapAction; + private SubItemLoggingInfo mLoggingInfo; + + /** + * Sets the sub item's text. + */ + @NonNull + public Builder setText(@NonNull Text text) { + mText = text; + return this; + } + + /** + * Sets the sub item's icon. + */ + @NonNull + public Builder setIcon(@NonNull Icon icon) { + mIcon = icon; + return this; + } + + /** + * Sets the sub item's tap action. + */ + @NonNull + public Builder setTapAction(@NonNull TapAction tapAction) { + mTapAction = tapAction; + return this; + } + + /** + * Sets the sub item's logging info. + */ + @NonNull + public Builder setLoggingInfo(@NonNull SubItemLoggingInfo loggingInfo) { + mLoggingInfo = loggingInfo; + return this; + } + + /** + * Builds a new {@link SubItemInfo} instance. + * + * @throws IllegalStateException if all the data field is empty. + */ + @NonNull + public SubItemInfo build() { + if (SmartspaceUtils.isEmpty(mText) && mIcon == null && mTapAction == null + && mLoggingInfo == null) { + throw new IllegalStateException("SubItem data is empty"); + } + + return new SubItemInfo(mText, mIcon, mTapAction, mLoggingInfo); + } } } @@ -735,27 +573,45 @@ public class BaseTemplateData implements Parcelable { /** A unique instance id for the sub item. */ private final int mInstanceId; - /** The feature type for this sub item. */ + /** + * {@link FeatureType} indicating the feature type of this subitem. + * + * @see FeatureType + */ + @FeatureType private final int mFeatureType; + /** The data source's package name for this sub item. */ + @Nullable + private final CharSequence mPackageName; + SubItemLoggingInfo(@NonNull Parcel in) { mInstanceId = in.readInt(); mFeatureType = in.readInt(); + mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); } - private SubItemLoggingInfo(int instanceId, int featureType) { + private SubItemLoggingInfo(int instanceId, @FeatureType int featureType, + @Nullable CharSequence packageName) { mInstanceId = instanceId; mFeatureType = featureType; + mPackageName = packageName; } public int getInstanceId() { return mInstanceId; } + @FeatureType public int getFeatureType() { return mFeatureType; } + @Nullable + public CharSequence getPackageName() { + return mPackageName; + } + /** * @see Parcelable.Creator */ @@ -782,6 +638,7 @@ public class BaseTemplateData implements Parcelable { public void writeToParcel(@NonNull Parcel out, int flags) { out.writeInt(mInstanceId); out.writeInt(mFeatureType); + TextUtils.writeToParcel(mPackageName, out, flags); } @Override @@ -789,12 +646,13 @@ public class BaseTemplateData implements Parcelable { if (this == o) return true; if (!(o instanceof SubItemLoggingInfo)) return false; SubItemLoggingInfo that = (SubItemLoggingInfo) o; - return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType; + return mInstanceId == that.mInstanceId && mFeatureType == that.mFeatureType + && SmartspaceUtils.isEqual(mPackageName, that.mPackageName); } @Override public int hashCode() { - return Objects.hash(mInstanceId, mFeatureType); + return Objects.hash(mInstanceId, mFeatureType, mPackageName); } @Override @@ -802,6 +660,7 @@ public class BaseTemplateData implements Parcelable { return "SubItemLoggingInfo{" + "mInstanceId=" + mInstanceId + ", mFeatureType=" + mFeatureType + + ", mPackageName=" + mPackageName + '}'; } @@ -815,22 +674,32 @@ public class BaseTemplateData implements Parcelable { private final int mInstanceId; private final int mFeatureType; + private CharSequence mPackageName; /** * A builder for {@link SubItemLoggingInfo}. * * @param instanceId A unique instance id for the sub item - * @param featureType The feature type for this sub item + * @param featureType The feature type id for this sub item */ - public Builder(int instanceId, int featureType) { + public Builder(int instanceId, @FeatureType int featureType) { mInstanceId = instanceId; mFeatureType = featureType; } + /** + * Sets the sub item's data source package name. + */ + @NonNull + public Builder setPackageName(@NonNull CharSequence packageName) { + mPackageName = packageName; + return this; + } + /** Builds a new {@link SubItemLoggingInfo} instance. */ @NonNull public SubItemLoggingInfo build() { - return new SubItemLoggingInfo(mInstanceId, mFeatureType); + return new SubItemLoggingInfo(mInstanceId, mFeatureType, mPackageName); } } } diff --git a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java index fbdb7be3de7d..c1378f13543a 100644 --- a/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/CarouselTemplateData.java @@ -56,31 +56,16 @@ public final class CarouselTemplateData extends BaseTemplateData { } private CarouselTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @NonNull List<CarouselItem> carouselItems, @Nullable TapAction carouselAction) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); mCarouselItems = carouselItems; mCarouselAction = carouselAction; @@ -190,14 +175,9 @@ public final class CarouselTemplateData extends BaseTemplateData { throw new IllegalStateException("Carousel data is empty"); } - return new CarouselTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new CarouselTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mCarouselItems, mCarouselAction); } } diff --git a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java index 1d1306619d11..836f41412c01 100644 --- a/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/CombinedCardsTemplateData.java @@ -51,30 +51,16 @@ public final class CombinedCardsTemplateData extends BaseTemplateData { } private CombinedCardsTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @NonNull List<BaseTemplateData> combinedCardDataList) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); + mCombinedCardDataList = combinedCardDataList; } @@ -161,14 +147,9 @@ public final class CombinedCardsTemplateData extends BaseTemplateData { if (mCombinedCardDataList == null) { throw new IllegalStateException("Please assign a value to all @NonNull args."); } - return new CombinedCardsTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new CombinedCardsTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mCombinedCardDataList); } } diff --git a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java index 19177dfc7649..29df0186b192 100644 --- a/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/HeadToHeadTemplateData.java @@ -66,21 +66,11 @@ public final class HeadToHeadTemplateData extends BaseTemplateData { } private HeadToHeadTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @Nullable Text headToHeadTitle, @Nullable Icon headToHeadFirstCompetitorIcon, @@ -88,13 +78,8 @@ public final class HeadToHeadTemplateData extends BaseTemplateData { @Nullable Text headToHeadFirstCompetitorText, @Nullable Text headToHeadSecondCompetitorText, @Nullable TapAction headToHeadAction) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); mHeadToHeadTitle = headToHeadTitle; mHeadToHeadFirstCompetitorIcon = headToHeadFirstCompetitorIcon; @@ -296,14 +281,9 @@ public final class HeadToHeadTemplateData extends BaseTemplateData { */ @NonNull public HeadToHeadTemplateData build() { - return new HeadToHeadTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new HeadToHeadTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mHeadToHeadTitle, mHeadToHeadFirstCompetitorIcon, mHeadToHeadSecondCompetitorIcon, mHeadToHeadFirstCompetitorText, diff --git a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java index 48af9c173f1d..b87e5b32fcc5 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SubCardTemplateData.java @@ -59,32 +59,17 @@ public final class SubCardTemplateData extends BaseTemplateData { } private SubCardTemplateData(int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @NonNull Icon subCardIcon, @Nullable Text subCardText, @Nullable TapAction subCardAction) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); mSubCardIcon = subCardIcon; mSubCardText = subCardText; @@ -207,14 +192,9 @@ public final class SubCardTemplateData extends BaseTemplateData { */ @NonNull public SubCardTemplateData build() { - return new SubCardTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new SubCardTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mSubCardIcon, mSubCardText, mSubCardAction); diff --git a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java index 38692cd19df4..430d79c481ed 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SubImageTemplateData.java @@ -60,32 +60,17 @@ public final class SubImageTemplateData extends BaseTemplateData { } private SubImageTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @NonNull List<Text> subImageTexts, @NonNull List<Icon> subImages, @Nullable TapAction subImageAction) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); mSubImageTexts = subImageTexts; mSubImages = subImages; @@ -204,14 +189,9 @@ public final class SubImageTemplateData extends BaseTemplateData { */ @NonNull public SubImageTemplateData build() { - return new SubImageTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new SubImageTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mSubImageTexts, mSubImages, mSubImageAction); diff --git a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java index b1535f1e3924..ae43fc48c907 100644 --- a/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java +++ b/core/java/android/app/smartspace/uitemplatedata/SubListTemplateData.java @@ -59,32 +59,17 @@ public final class SubListTemplateData extends BaseTemplateData { } private SubListTemplateData(@SmartspaceTarget.UiTemplateType int templateType, - @Nullable Text titleText, - @Nullable Icon titleIcon, - @Nullable Text subtitleText, - @Nullable Icon subtitleIcon, - @Nullable TapAction primaryTapAction, - @Nullable SubItemLoggingInfo primaryLoggingInfo, - @Nullable Text supplementalSubtitleText, - @Nullable Icon supplementalSubtitleIcon, - @Nullable TapAction supplementalSubtitleTapAction, - @Nullable SubItemLoggingInfo supplementalSubtitleLoggingInfo, - @Nullable Text supplementalText, - @Nullable Icon supplementalIcon, - @Nullable TapAction supplementalTapAction, - @Nullable SubItemLoggingInfo supplementalLoggingInfo, - @Nullable Text supplementalAlarmText, + @Nullable SubItemInfo primaryItem, + @Nullable SubItemInfo subtitleItem, + @Nullable SubItemInfo subtitleSupplementalItem, + @Nullable SubItemInfo supplementalLineItem, + @Nullable SubItemInfo supplementalAlarmItem, int layoutWeight, @Nullable Icon subListIcon, @NonNull List<Text> subListTexts, @Nullable TapAction subListAction) { - super(templateType, titleText, titleIcon, subtitleText, subtitleIcon, - primaryTapAction, primaryLoggingInfo, - supplementalSubtitleText, supplementalSubtitleIcon, - supplementalSubtitleTapAction, supplementalSubtitleLoggingInfo, - supplementalText, supplementalIcon, - supplementalTapAction, supplementalLoggingInfo, - supplementalAlarmText, layoutWeight); + super(templateType, primaryItem, subtitleItem, subtitleSupplementalItem, + supplementalLineItem, supplementalAlarmItem, layoutWeight); mSubListIcon = subListIcon; mSubListTexts = subListTexts; @@ -207,14 +192,9 @@ public final class SubListTemplateData extends BaseTemplateData { */ @NonNull public SubListTemplateData build() { - return new SubListTemplateData(getTemplateType(), getTitleText(), - getTitleIcon(), getSubtitleText(), getSubtitleIcon(), - getPrimaryTapAction(), getPrimaryLoggingInfo(), - getSupplementalSubtitleText(), getSupplementalSubtitleIcon(), - getSupplementalSubtitleTapAction(), getSupplementalSubtitleLoggingInfo(), - getSupplementalText(), getSupplementalIcon(), - getSupplementalTapAction(), getSupplementalLoggingInfo(), - getSupplementalAlarmText(), getLayoutWeight(), + return new SubListTemplateData(getTemplateType(), getPrimaryItem(), + getSubtitleItem(), getSubtitleSupplemtnalItem(), + getSupplementalLineItem(), getSupplementalAlarmItem(), getLayoutWeight(), mSubListIcon, mSubListTexts, mSubListAction); diff --git a/core/java/android/app/smartspace/uitemplatedata/Text.java b/core/java/android/app/smartspace/uitemplatedata/Text.java index e1afce7148ae..cb2f43202e90 100644 --- a/core/java/android/app/smartspace/uitemplatedata/Text.java +++ b/core/java/android/app/smartspace/uitemplatedata/Text.java @@ -109,6 +109,15 @@ public final class Text implements Parcelable { out.writeInt(mMaxLines); } + @Override + public String toString() { + return "Text{" + + "mText=" + mText + + ", mTruncateAtType=" + mTruncateAtType + + ", mMaxLines=" + mMaxLines + + '}'; + } + /** * A builder for {@link Text} object. * diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 99ce14743a6f..02d140f047cc 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -48,7 +48,6 @@ import android.os.ResultReceiver; import android.util.ArrayMap; import android.view.Surface; -import java.util.Objects; import java.util.concurrent.Executor; /** @@ -223,7 +222,8 @@ public final class VirtualDeviceManager { * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. * @param executor The executor on which {@code callback} will be invoked. This is ignored - * if {@code callback} is {@code null}. + * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must + * not be null. * @param callback Callback to call when the state of the {@link VirtualDisplay} changes * @return The newly created virtual display, or {@code null} if the application could * not create the virtual display. @@ -237,7 +237,7 @@ public final class VirtualDeviceManager { @IntRange(from = 1) int densityDpi, @Nullable Surface surface, @VirtualDisplayFlag int flags, - @NonNull @CallbackExecutor Executor executor, + @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { // TODO(b/205343547): Handle display groups properly instead of creating a new display // group for every new virtual display created using this API. @@ -253,7 +253,7 @@ public final class VirtualDeviceManager { .setFlags(getVirtualDisplayFlags(flags)) .build(), callback, - Objects.requireNonNull(executor)); + executor); } /** diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java index 85890c1912c5..823d454ee16b 100644 --- a/core/java/android/hardware/CameraStreamStats.java +++ b/core/java/android/hardware/CameraStreamStats.java @@ -47,7 +47,7 @@ public class CameraStreamStats implements Parcelable { private int mHistogramType; private float[] mHistogramBins; private long[] mHistogramCounts; - private int mDynamicRangeProfile; + private long mDynamicRangeProfile; private int mStreamUseCase; private static final String TAG = "CameraStreamStats"; @@ -70,7 +70,7 @@ public class CameraStreamStats implements Parcelable { public CameraStreamStats(int width, int height, int format, int dataSpace, long usage, long requestCount, long errorCount, - int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile, + int startLatencyMs, int maxHalBuffers, int maxAppBuffers, long dynamicRangeProfile, int streamUseCase) { mWidth = width; mHeight = height; @@ -130,7 +130,7 @@ public class CameraStreamStats implements Parcelable { dest.writeInt(mHistogramType); dest.writeFloatArray(mHistogramBins); dest.writeLongArray(mHistogramCounts); - dest.writeInt(mDynamicRangeProfile); + dest.writeLong(mDynamicRangeProfile); dest.writeInt(mStreamUseCase); } @@ -148,7 +148,7 @@ public class CameraStreamStats implements Parcelable { mHistogramType = in.readInt(); mHistogramBins = in.createFloatArray(); mHistogramCounts = in.createLongArray(); - mDynamicRangeProfile = in.readInt(); + mDynamicRangeProfile = in.readLong(); mStreamUseCase = in.readInt(); } @@ -204,7 +204,7 @@ public class CameraStreamStats implements Parcelable { return mHistogramCounts; } - public int getDynamicRangeProfile() { + public long getDynamicRangeProfile() { return mDynamicRangeProfile; } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 524fe795cb75..7bebe1ff14c3 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -2448,8 +2448,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * @see #REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX * @hide */ - public static final Key<int[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP = - new Key<int[]>("android.request.availableDynamicRangeProfilesMap", int[].class); + public static final Key<long[]> REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP = + new Key<long[]>("android.request.availableDynamicRangeProfilesMap", long[].class); /** * <p>Recommended 10-bit dynamic range profile.</p> @@ -2464,8 +2464,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri */ @PublicKey @NonNull - public static final Key<Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE = - new Key<Integer>("android.request.recommendedTenBitDynamicRangeProfile", int.class); + public static final Key<Long> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE = + new Key<Long>("android.request.recommendedTenBitDynamicRangeProfile", long.class); /** * <p>The list of image formats that are supported by this diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 8f42b1f6f355..73735edbb2ba 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -990,7 +990,7 @@ public abstract class CameraDevice implements AutoCloseable { * <p>Reprocessing with 10-bit output targets on 10-bit capable * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT} devices is * not supported. Trying to initialize a repreocessable capture session with one ore more - * output configurations set {@link OutputConfiguration#setDynamicRangeProfile(int)} to use + * output configurations set {@link OutputConfiguration#setDynamicRangeProfile} to use * a 10-bit dynamic range profile {@link android.hardware.camera2.params.DynamicRangeProfiles} * will trigger {@link IllegalArgumentException}.</p> * @@ -1179,7 +1179,7 @@ public abstract class CameraDevice implements AutoCloseable { * @see #createCaptureSessionByOutputConfigurations * @see #createReprocessableCaptureSession * @see #createConstrainedHighSpeedCaptureSession - * @see OutputConfiguration#setDynamicRangeProfile(int) + * @see OutputConfiguration#setDynamicRangeProfile * @see android.hardware.camera2.params.DynamicRangeProfiles */ public void createCaptureSession( diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 4fb496d66fbd..468e6041eb73 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1058,7 +1058,7 @@ public class CameraMetadataNative implements Parcelable { } private DynamicRangeProfiles getDynamicRangeProfiles() { - int[] profileArray = getBase( + long[] profileArray = getBase( CameraCharacteristics.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP); if (profileArray == null) { diff --git a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java index 5c1a4aa120ea..cbd84e77e72a 100644 --- a/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java +++ b/core/java/android/hardware/camera2/params/DynamicRangeProfiles.java @@ -16,7 +16,7 @@ package android.hardware.camera2.params; -import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.hardware.camera2.CameraMetadata; @@ -27,7 +27,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Objects; import java.util.Set; /** @@ -44,20 +43,20 @@ import java.util.Set; * * <p>Some devices may not be able to support 8-bit and/or 10-bit output with different dynamic * range profiles within the same capture request. Such device specific constraints can be queried - * by calling {@link #getProfileCaptureRequestConstraints(int)}. Do note that unsupported + * by calling {@link #getProfileCaptureRequestConstraints}. Do note that unsupported * combinations will result in {@link IllegalArgumentException} when trying to submit a capture * request. Capture requests that only reference outputs configured using the same dynamic range * profile value will never fail due to such constraints.</p> * - * @see OutputConfiguration#setDynamicRangeProfile(int) + * @see OutputConfiguration#setDynamicRangeProfile */ public final class DynamicRangeProfiles { /** * This the default 8-bit standard profile that will be used in case where camera clients do not * explicitly configure a supported dynamic range profile by calling - * {@link OutputConfiguration#setDynamicRangeProfile(int)}. + * {@link OutputConfiguration#setDynamicRangeProfile}. */ - public static final int STANDARD = + public static final long STANDARD = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_STANDARD; /** @@ -65,7 +64,7 @@ public final class DynamicRangeProfiles { * * <p>All 10-bit output capable devices are required to support this profile.</p> */ - public static final int HLG10 = + public static final long HLG10 = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HLG10; /** @@ -74,7 +73,7 @@ public final class DynamicRangeProfiles { * <p>This profile utilizes internal static metadata to increase the quality * of the capture.</p> */ - public static final int HDR10 = + public static final long HDR10 = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10; /** @@ -83,7 +82,7 @@ public final class DynamicRangeProfiles { * <p>In contrast to HDR10, this profile uses internal per-frame metadata * to further enhance the quality of the capture.</p> */ - public static final int HDR10_PLUS = + public static final long HDR10_PLUS = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_HDR10_PLUS; /** @@ -91,13 +90,13 @@ public final class DynamicRangeProfiles { * accurate capture. This would typically differ from what a specific device * might want to tune for a consumer optimized Dolby Vision general capture.</p> */ - public static final int DOLBY_VISION_10B_HDR_REF = + public static final long DOLBY_VISION_10B_HDR_REF = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF; /** * <p>This is the power optimized mode for 10-bit Dolby Vision HDR Reference Mode.</p> */ - public static final int DOLBY_VISION_10B_HDR_REF_PO = + public static final long DOLBY_VISION_10B_HDR_REF_PO = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_REF_PO; /** @@ -107,52 +106,52 @@ public final class DynamicRangeProfiles { * that each specific device would have a different look for their default * Dolby Vision capture.</p> */ - public static final int DOLBY_VISION_10B_HDR_OEM = + public static final long DOLBY_VISION_10B_HDR_OEM = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM; /** * <p>This is the power optimized mode for 10-bit Dolby Vision HDR device specific capture * Mode.</p> */ - public static final int DOLBY_VISION_10B_HDR_OEM_PO = + public static final long DOLBY_VISION_10B_HDR_OEM_PO = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_10B_HDR_OEM_PO; /** * <p>This is the 8-bit version of the Dolby Vision reference capture mode optimized * for scene accuracy.</p> */ - public static final int DOLBY_VISION_8B_HDR_REF = + public static final long DOLBY_VISION_8B_HDR_REF = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF; /** * <p>This is the power optimized mode for 8-bit Dolby Vision HDR Reference Mode.</p> */ - public static final int DOLBY_VISION_8B_HDR_REF_PO = + public static final long DOLBY_VISION_8B_HDR_REF_PO = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_REF_PO; /** * <p>This is the 8-bit version of device specific tuned and optimized Dolby Vision * capture mode.</p> */ - public static final int DOLBY_VISION_8B_HDR_OEM = + public static final long DOLBY_VISION_8B_HDR_OEM = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM; /** * <p>This is the power optimized mode for 8-bit Dolby Vision HDR device specific * capture Mode.</p> */ - public static final int DOLBY_VISION_8B_HDR_OEM_PO = + public static final long DOLBY_VISION_8B_HDR_OEM_PO = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_DOLBY_VISION_8B_HDR_OEM_PO; /* * @hide */ - public static final int PUBLIC_MAX = + public static final long PUBLIC_MAX = CameraMetadata.REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP_MAX; /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"PROFILE_"}, value = + @LongDef(prefix = {"PROFILE_"}, value = {STANDARD, HLG10, HDR10, @@ -168,7 +167,8 @@ public final class DynamicRangeProfiles { public @interface Profile { } - private final HashMap<Integer, Set<Integer>> mProfileMap = new HashMap<>(); + private final HashMap<Long, Set<Long>> mProfileMap = new HashMap<>(); + private final HashMap<Long, Boolean> mLookahedLatencyMap = new HashMap<>(); /** * Create a new immutable DynamicRangeProfiles instance. @@ -193,23 +193,23 @@ public final class DynamicRangeProfiles { * if {@code elements} is {@code null} * */ - public DynamicRangeProfiles(@NonNull final int[] elements) { - if ((elements.length % 2) != 0) { + public DynamicRangeProfiles(@NonNull final long[] elements) { + if ((elements.length % 3) != 0) { throw new IllegalArgumentException("Dynamic range profile map length " + elements.length + " is not even!"); } - for (int i = 0; i < elements.length; i += 2) { + for (int i = 0; i < elements.length; i += 3) { checkProfileValue(elements[i]); // STANDARD is not expected to be included if (elements[i] == STANDARD) { throw new IllegalArgumentException("Dynamic range profile map must not include a" + " STANDARD profile entry!"); } - HashSet<Integer> profiles = new HashSet<>(); + HashSet<Long> profiles = new HashSet<>(); if (elements[i+1] != 0) { - for (int profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) { + for (long profile = STANDARD; profile < PUBLIC_MAX; profile <<= 1) { if ((elements[i+1] & profile) != 0) { profiles.add(profile); } @@ -217,12 +217,13 @@ public final class DynamicRangeProfiles { } mProfileMap.put(elements[i], profiles); + mLookahedLatencyMap.put(elements[i], elements[i+2] != 0L); } // Build the STANDARD constraints depending on the advertised 10-bit limitations - HashSet<Integer> standardConstraints = new HashSet<>(); + HashSet<Long> standardConstraints = new HashSet<>(); standardConstraints.add(STANDARD); - for(Integer profile : mProfileMap.keySet()) { + for(Long profile : mProfileMap.keySet()) { if (mProfileMap.get(profile).isEmpty() || mProfileMap.get(profile).contains(STANDARD)) { standardConstraints.add(profile); } @@ -235,24 +236,15 @@ public final class DynamicRangeProfiles { /** * @hide */ - public static void checkProfileValue(int profile) { - switch (profile) { - case STANDARD: - case HLG10: - case HDR10: - case HDR10_PLUS: - case DOLBY_VISION_10B_HDR_REF: - case DOLBY_VISION_10B_HDR_REF_PO: - case DOLBY_VISION_10B_HDR_OEM: - case DOLBY_VISION_10B_HDR_OEM_PO: - case DOLBY_VISION_8B_HDR_REF: - case DOLBY_VISION_8B_HDR_REF_PO: - case DOLBY_VISION_8B_HDR_OEM: - case DOLBY_VISION_8B_HDR_OEM_PO: - //No-op - break; - default: - throw new IllegalArgumentException("Unknown profile " + profile); + public static void checkProfileValue(long profile) { + if (profile == STANDARD || profile == HLG10 || profile == HDR10 || profile == HDR10_PLUS + || profile == DOLBY_VISION_10B_HDR_REF || profile == DOLBY_VISION_10B_HDR_REF_PO + || profile == DOLBY_VISION_10B_HDR_OEM || profile == DOLBY_VISION_10B_HDR_OEM_PO + || profile == DOLBY_VISION_8B_HDR_REF || profile == DOLBY_VISION_8B_HDR_REF_PO + || profile == DOLBY_VISION_8B_HDR_OEM + || profile == DOLBY_VISION_8B_HDR_OEM_PO) {//No-op + } else { + throw new IllegalArgumentException("Unknown profile " + profile); } } @@ -261,7 +253,7 @@ public final class DynamicRangeProfiles { * * @return non-modifiable set of dynamic range profiles */ - public @NonNull Set<Integer> getSupportedProfiles() { + public @NonNull Set<Long> getSupportedProfiles() { return Collections.unmodifiableSet(mProfileMap.keySet()); } @@ -272,7 +264,7 @@ public final class DynamicRangeProfiles { * * <p>For example if assume that a particular 10-bit output capable device * returns ({@link #STANDARD}, {@link #HLG10}, {@link #HDR10}) as result from calling - * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints(int)} + * {@link #getSupportedProfiles()} and {@link #getProfileCaptureRequestConstraints} * returns ({@link #STANDARD}, {@link #HLG10}) when given an argument of {@link #STANDARD}. * This means that the corresponding camera device will only accept and process capture requests * that reference outputs configured using {@link #HDR10} dynamic profile or alternatively @@ -288,14 +280,40 @@ public final class DynamicRangeProfiles { * within the list returned by * getSupportedProfiles() * - * @see OutputConfiguration#setDynamicRangeProfile(int) + * @see OutputConfiguration#setDynamicRangeProfile */ - public @NonNull Set<Integer> getProfileCaptureRequestConstraints(@Profile int profile) { - Set<Integer> ret = mProfileMap.get(profile); + public @NonNull Set<Long> getProfileCaptureRequestConstraints(@Profile long profile) { + Set<Long> ret = mProfileMap.get(profile); if (ret == null) { throw new IllegalArgumentException("Unsupported profile!"); } return Collections.unmodifiableSet(ret); } + + /** + * Check whether a given dynamic range profile is suitable for latency sensitive use cases. + * + * <p>Due to internal lookahead logic, camera outputs configured with some dynamic range + * profiles may experience additional latency greater than 3 buffers. Using camera outputs + * with such profiles for latency sensitive use cases such as camera preview is not + * recommended. Profiles that have such extra streaming delay are typically utilized for + * scenarios such as offscreen video recording.</p> + * + * @return true if the given profile is not suitable for latency sensitive use cases, false + * otherwise + * @throws IllegalArgumentException - If the profile argument is not + * within the list returned by + * getSupportedProfiles() + * + * @see OutputConfiguration#setDynamicRangeProfile + */ + public boolean isExtraLatencyPresent(@Profile long profile) { + Boolean ret = mLookahedLatencyMap.get(profile); + if (ret == null) { + throw new IllegalArgumentException("Unsupported profile!"); + } + + return ret; + } } diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java index 8c0dcfcf0693..465abfb55540 100644 --- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java +++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java @@ -246,7 +246,7 @@ public final class MandatoryStreamCombination { * @return true if stream is able to output 10-bit pixels * * @see android.hardware.camera2.CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT - * @see OutputConfiguration#setDynamicRangeProfile(int) + * @see OutputConfiguration#setDynamicRangeProfile */ public boolean is10BitCapable() { return mIs10BitCapable; diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 80937640e094..2350b7c7a481 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -421,7 +421,7 @@ public final class OutputConfiguration implements Parcelable { * {@link android.media.MediaCodec} etc.) * or {@link ImageFormat#YCBCR_P010}.</p> */ - public void setDynamicRangeProfile(@Profile int profile) { + public void setDynamicRangeProfile(@Profile long profile) { mDynamicRangeProfile = profile; } @@ -430,7 +430,7 @@ public final class OutputConfiguration implements Parcelable { * * @return the currently set dynamic range profile */ - public @Profile int getDynamicRangeProfile() { + public @Profile long getDynamicRangeProfile() { return mDynamicRangeProfile; } @@ -1070,7 +1070,7 @@ public final class OutputConfiguration implements Parcelable { int streamUseCase = source.readInt(); checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant"); - int dynamicRangeProfile = source.readInt(); + long dynamicRangeProfile = source.readLong(); DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile); int timestampBase = source.readInt(); @@ -1217,7 +1217,7 @@ public final class OutputConfiguration implements Parcelable { dest.writeInt(mIsMultiResolution ? 1 : 0); // writeList doesn't seem to work well with Integer list. dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed)); - dest.writeInt(mDynamicRangeProfile); + dest.writeLong(mDynamicRangeProfile); dest.writeInt(mStreamUseCase); dest.writeInt(mTimestampBase); dest.writeInt(mMirrorMode); @@ -1335,7 +1335,7 @@ public final class OutputConfiguration implements Parcelable { // The sensor pixel modes that this OutputConfiguration will use private ArrayList<Integer> mSensorPixelModesUsed; // Dynamic range profile - private int mDynamicRangeProfile; + private long mDynamicRangeProfile; // Stream use case private int mStreamUseCase; // Timestamp base diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index c053c9287fac..c3417310dd40 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -957,7 +957,7 @@ public final class DisplayManager { public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig, @Nullable VirtualDisplay.Callback callback, - @NonNull Executor executor) { + @Nullable Executor executor) { return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice, virtualDisplayConfig, callback, executor, null); } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 889100d0f242..a62bbf623000 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -1054,6 +1054,14 @@ public final class DisplayManagerGlobal { @Nullable private final VirtualDisplay.Callback mCallback; @Nullable private final Executor mExecutor; + /** + * Creates a virtual display callback. + * + * @param callback The callback to call for virtual display events, or {@code null} if the + * caller does not wish to receive callback events. + * @param executor The executor to call the {@code callback} on. Must not be {@code null} if + * the callback is not {@code null}. + */ VirtualDisplayCallback(VirtualDisplay.Callback callback, Executor executor) { mCallback = callback; mExecutor = mCallback != null ? Objects.requireNonNull(executor) : null; diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 944b71700450..a3b836adfc8b 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -674,10 +674,12 @@ public class RecoverySystem { } try { if (!rs.allocateSpaceForUpdate(packageFile)) { + rs.clearBcb(); throw new IOException("Failed to allocate space for update " + packageFile.getAbsolutePath()); } } catch (RemoteException e) { + rs.clearBcb(); e.rethrowAsRuntimeException(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 23e02e9d9968..528156fb8f4e 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -24,7 +24,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -2128,7 +2127,7 @@ public final class Settings { /** * Intent extra: The id of a setting restricted by supervisors. * <p> - * Type: String with a value from the SupervisorVerificationSetting annotation below. + * Type: Integer with a value from the SupervisorVerificationSetting annotation below. * <ul> * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN} * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS} @@ -2141,20 +2140,19 @@ public final class Settings { /** * Unknown setting. */ - public static final String SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = ""; + public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0; /** * Biometric settings for supervisors. */ - public static final String SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = - "supervisor_restricted_biometrics_controller"; + public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1; /** * Keys for {@link #EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY}. * @hide */ @Retention(RetentionPolicy.SOURCE) - @StringDef(prefix = { "SUPERVISOR_VERIFICATION_SETTING_" }, value = { + @IntDef(prefix = { "SUPERVISOR_VERIFICATION_SETTING_" }, value = { SUPERVISOR_VERIFICATION_SETTING_UNKNOWN, SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS, }) diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 542de3fad8b0..c1fcd664f6fa 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -239,8 +239,10 @@ public class TelephonyRegistryManager { * @param events Events * @param notifyNow Whether to notify instantly */ - public void listenFromListener(int subId, @NonNull String pkg, @NonNull String featureId, - @NonNull PhoneStateListener listener, @NonNull int events, boolean notifyNow) { + public void listenFromListener(int subId, @NonNull boolean renounceFineLocationAccess, + @NonNull boolean renounceCoarseLocationAccess, @NonNull String pkg, + @NonNull String featureId, @NonNull PhoneStateListener listener, + @NonNull int events, boolean notifyNow) { if (listener == null) { throw new IllegalStateException("telephony service is null."); } @@ -257,8 +259,8 @@ public class TelephonyRegistryManager { } else if (listener.mSubId != null) { subId = listener.mSubId; } - sRegistry.listenWithEventList(false, false, subId, pkg, featureId, - listener.callback, eventsList, notifyNow); + sRegistry.listenWithEventList(renounceFineLocationAccess, renounceCoarseLocationAccess, + subId, pkg, featureId, listener.callback, eventsList, notifyNow); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 632af2315bcd..6b81667e9074 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -248,6 +248,7 @@ public final class SurfaceControl implements Parcelable { private static native void nativeSetFixedTransformHint(long transactionObj, long nativeObject, int transformHint); + private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId); private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken, String windowName, IBinder focusedToken, String focusedWindowName, int displayId); private static native void nativeSetFrameTimelineVsync(long transactionObj, @@ -3657,6 +3658,17 @@ public final class SurfaceControl implements Parcelable { } /** + * Removes the input focus from the current window which is having the input focus. Should + * only be called when the current focused app is not responding and the current focused + * window is not beloged to the current focused app. + * @hide + */ + public Transaction removeCurrentInputFocus(int displayId) { + nativeRemoveCurrentInputFocus(mNativeObject, displayId); + return this; + } + + /** * Adds or removes the flag SKIP_SCREENSHOT of the surface. Setting the flag is equivalent * to creating the Surface with the {@link #SKIP_SCREENSHOT} flag. * diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 844403298cc9..feb17f54acd6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -217,6 +217,7 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.ref.WeakReference; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; @@ -2721,6 +2722,10 @@ public final class ViewRootImpl implements ViewParent, // Execute enqueued actions on every traversal in case a detached view enqueued an action getRunQueue().executeActions(mAttachInfo.mHandler); + if (mApplyInsetsRequested && !(mWillMove || mWillResize)) { + dispatchApplyInsets(host); + } + boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); if (layoutRequested) { @@ -2785,18 +2790,6 @@ public final class ViewRootImpl implements ViewParent, } } - if (mApplyInsetsRequested && !(mWillMove || mWillResize)) { - dispatchApplyInsets(host); - if (mLayoutRequested) { - // Short-circuit catching a new layout request here, so - // we don't need to go through two layout passes when things - // change due to fitting system windows, which can happen a lot. - windowSizeMayChange |= measureHierarchy(host, lp, - mView.getContext().getResources(), - desiredWindowWidth, desiredWindowHeight); - } - } - if (layoutRequested) { // Clear this now, so that if anything requests a layout in the // rest of this function we will catch it and re-run a full @@ -3693,7 +3686,7 @@ public final class ViewRootImpl implements ViewParent, return current; } - final Queue<AccessibilityNodeInfo> fringe = new LinkedList<>(); + final Queue<AccessibilityNodeInfo> fringe = new ArrayDeque<>(); fringe.offer(current); while (!fringe.isEmpty()) { diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 07b7a18b6795..68532781f08b 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -46,11 +46,11 @@ import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; @@ -1166,7 +1166,7 @@ public final class AccessibilityInteractionClient + connectionIdWaitingForPrefetchResultCopy + ";Result: " + infos, Binder.getCallingUid(), Arrays.asList(Thread.currentThread().getStackTrace()), - new HashSet<String>(Arrays.asList("getStackTrace")), + new HashSet<>(Collections.singletonList("getStackTrace")), FLAGS_ACCESSIBILITY_INTERACTION_CONNECTION_CALLBACK); } } else if (DEBUG) { @@ -1348,7 +1348,7 @@ public final class AccessibilityInteractionClient } // Check for duplicates. HashSet<AccessibilityNodeInfo> seen = new HashSet<>(); - Queue<AccessibilityNodeInfo> fringe = new LinkedList<>(); + Queue<AccessibilityNodeInfo> fringe = new ArrayDeque<>(); fringe.add(root); while (!fringe.isEmpty()) { AccessibilityNodeInfo current = fringe.poll(); diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 6cfb9381f130..5d9f4f630fc0 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -39,7 +39,7 @@ import com.android.internal.textservice.ITextServicesSessionListener; import dalvik.system.CloseGuard; -import java.util.LinkedList; +import java.util.ArrayDeque; import java.util.Locale; import java.util.Queue; import java.util.concurrent.Executor; @@ -245,7 +245,7 @@ public class SpellCheckerSession { } } - private final Queue<SpellCheckerParams> mPendingTasks = new LinkedList<>(); + private final Queue<SpellCheckerParams> mPendingTasks = new ArrayDeque<>(); @GuardedBy("SpellCheckerSessionListenerImpl.this") private SpellCheckerSession mSpellCheckerSession; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index cdb69e546b8f..e336e96d577e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -33,7 +33,6 @@ import android.annotation.StringRes; import android.annotation.UiThread; import android.app.Activity; import android.app.ActivityManager; -import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.VoiceInteractor.PickOptionRequest; import android.app.VoiceInteractor.PickOptionRequest.Option; @@ -54,13 +53,11 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Insets; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.PatternMatcher; import android.os.RemoteException; import android.os.StrictMode; @@ -1464,36 +1461,9 @@ public class ResolverActivity extends Activity implements public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity, int userId) { - // Pass intent to delegate chooser activity with permission token. - // TODO: This should move to a trampoline Activity in the system when the ChooserActivity - // moves into systemui - try { - // TODO: Once this is a small springboard activity, it can move off the UI process - // and we can move the request method to ActivityManagerInternal. - final Intent chooserIntent = new Intent(); - final ComponentName delegateActivity = ComponentName.unflattenFromString( - Resources.getSystem().getString(R.string.config_chooserActivity)); - IBinder permissionToken = ActivityTaskManager.getService() - .requestStartActivityPermissionToken(delegateActivity); - chooserIntent.setClassName(delegateActivity.getPackageName(), - delegateActivity.getClassName()); - chooserIntent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, permissionToken); - - // TODO: These extras will change as chooser activity moves into systemui - chooserIntent.putExtra(Intent.EXTRA_INTENT, intent); - chooserIntent.putExtra(ActivityTaskManager.EXTRA_OPTIONS, options); - chooserIntent.putExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY, - ignoreTargetSecurity); - chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId); - chooserIntent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - - // Don't close until the delegate finishes, or the token will be invalidated. - mAwaitingDelegateResponse = true; - - startActivityForResult(chooserIntent, REQUEST_CODE_RETURN_FROM_DELEGATE_CHOOSER); - } catch (RemoteException e) { - Log.e(TAG, e.toString()); - } + // Note: this method will be overridden in the delegate implementation to use the passed-in + // permission token. + startActivityAsCaller(intent, options, null, false, userId); return true; } diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index 301de2d3529e..dc53e77466ad 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -30,11 +30,9 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; -import android.provider.DeviceConfig; import com.android.internal.app.ResolverActivity; import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter; -import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import java.util.ArrayList; import java.util.Arrays; @@ -45,11 +43,6 @@ import java.util.List; * resolve it to an activity. */ public class DisplayResolveInfo implements TargetInfo, Parcelable { - private final boolean mEnableChooserDelegate = - DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.USE_DELEGATE_CHOOSER, - false); - private final ResolveInfo mResolveInfo; private CharSequence mDisplayLabel; private Drawable mDisplayIcon; @@ -180,12 +173,9 @@ public class DisplayResolveInfo implements TargetInfo, Parcelable { @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - if (mEnableChooserDelegate) { - return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); - } else { - activity.startActivityAsCaller(mResolvedIntent, options, null, false, userId); - return true; - } + // TODO: if the start-as-caller API no longer requires a permission token, this can go back + // to inlining the real activity-start call, and we can remove startAsCallerImpl. + return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); } @Override diff --git a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java index 615e4b793752..a03bac45d14f 100644 --- a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java +++ b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java @@ -29,22 +29,66 @@ public final class SystemServerClassLoaderFactory { private static final ArrayMap<String, PathClassLoader> sLoadedPaths = new ArrayMap<>(); /** - * Creates and caches a ClassLoader for the jar at the given path, or returns a cached - * ClassLoader if it exists. + * Creates and caches a ClassLoader for the jar at the given path. + * + * This method should only be called by ZygoteInit to prefetch jars. For other users, use + * {@link getOrCreateClassLoader} instead. * * The parent class loader should always be the system server class loader. Changing it has * implications that require discussion with the mainline team. * * @hide for internal use only */ - public static PathClassLoader getOrCreateClassLoader(String path, ClassLoader parent) { - PathClassLoader pathClassLoader = sLoadedPaths.get(path); - if (pathClassLoader == null) { - pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( - path, /*librarySearchPath=*/null, /*libraryPermittedPath=*/null, parent, - Build.VERSION.SDK_INT, /*isNamespaceShared=*/true , /*classLoaderName=*/null); - sLoadedPaths.put(path, pathClassLoader); + /* package */ static PathClassLoader createClassLoader(String path, ClassLoader parent) { + if (sLoadedPaths.containsKey(path)) { + throw new IllegalStateException("A ClassLoader for " + path + " already exists"); } + PathClassLoader pathClassLoader = (PathClassLoader) ClassLoaderFactory.createClassLoader( + path, /*librarySearchPath=*/null, /*libraryPermittedPath=*/null, parent, + Build.VERSION.SDK_INT, /*isNamespaceShared=*/true , /*classLoaderName=*/null); + sLoadedPaths.put(path, pathClassLoader); return pathClassLoader; } + + /** + * Returns a cached ClassLoader to be used at runtime for the jar at the given path. Or, creates + * one if it is not prefetched and is allowed to be created at runtime. + * + * The parent class loader should always be the system server class loader. Changing it has + * implications that require discussion with the mainline team. + * + * @hide for internal use only + */ + public static PathClassLoader getOrCreateClassLoader( + String path, ClassLoader parent, boolean isTestOnly) { + PathClassLoader pathClassLoader = sLoadedPaths.get(path); + if (pathClassLoader != null) { + return pathClassLoader; + } + if (!allowClassLoaderCreation(path, isTestOnly)) { + throw new RuntimeException("Creating a ClassLoader from " + path + " is not allowed. " + + "Please make sure that the jar is listed in " + + "`PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS` in the Makefile and added as a " + + "`standalone_contents` of a `systemserverclasspath_fragment` in " + + "`Android.bp`."); + } + return createClassLoader(path, parent); + } + + /** + * Returns whether a class loader for the jar is allowed to be created at runtime. + */ + private static boolean allowClassLoaderCreation(String path, boolean isTestOnly) { + // Currently, we only enforce prefetching for APEX jars. + if (!path.startsWith("/apex/")) { + return true; + } + // APEXes for testing only are okay to ignore. + if (isTestOnly) { + return true; + } + return false; + } + + } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 3d24aa2db247..ca1ae194cb12 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -586,7 +586,7 @@ public class ZygoteInit { } for (String jar : envStr.split(":")) { try { - SystemServerClassLoaderFactory.getOrCreateClassLoader( + SystemServerClassLoaderFactory.createClassLoader( jar, getOrCreateSystemServerClassLoader()); } catch (Error e) { // We don't want the process to crash for this error because prefetching is just an diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 9e70392fb7a0..dcc1a7626a1b 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -175,20 +175,20 @@ interface IStatusBarService void cancelRequestAddTile(in String packageName); /** - * Overrides the navigation bar mode. + * Sets the navigation bar mode. * - * @param navBarModeOverride the mode of the navigation bar override to be set. + * @param navBarMode the mode of the navigation bar to be set. * * @hide */ - void setNavBarModeOverride(int navBarModeOverride); + void setNavBarMode(int navBarMode); /** - * Gets the navigation bar mode override. + * Gets the navigation bar mode. * * @hide */ - int getNavBarModeOverride(); + int getNavBarMode(); /** * Register a listener for certain sessions. Each session may be guarded by its own permission. diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index fb5b5ffaac48..64571adac2ef 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1833,6 +1833,15 @@ static jlong nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) { return reinterpret_cast<jlong>(surfaceControl->getHandle().get()); } +static void nativeRemoveCurrentInputFocus(JNIEnv* env, jclass clazz, jlong transactionObj, + jint displayId) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + FocusRequest request; + request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); + request.displayId = displayId; + transaction->setFocusedWindow(request); +} + static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj, jobject toTokenObj, jstring windowNameJstr, jobject focusedTokenObj, jstring focusedWindowNameJstr, @@ -2167,6 +2176,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetFixedTransformHint}, {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V", (void*)nativeSetFocusedWindow}, + {"nativeRemoveCurrentInputFocus", "(JI)V", + (void*)nativeRemoveCurrentInputFocus}, {"nativeSetFrameTimelineVsync", "(JJ)V", (void*)nativeSetFrameTimelineVsync }, {"nativeAddJankDataListener", "(JJ)V", diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml index 0db08fb751d8..88bf18cb8857 100644 --- a/core/res/res/values-television/config.xml +++ b/core/res/res/values-television/config.xml @@ -24,19 +24,6 @@ <!-- Flags enabling default window features. See Window.java --> <bool name="config_defaultWindowFeatureOptionsPanel">false</bool> - <!-- The percentage of the screen width to use for the default width or height of - picture-in-picture windows. Regardless of the percent set here, calculated size will never - be smaller than @dimen/default_minimal_size_pip_resizable_task. --> - <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item> - - <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. - These values are in DPs and will be converted to pixel sizes internally. --> - <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">24x24</string> - - <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x55</integer> - <!-- The maximum height of the expanded horizontal picture-in-picture window --> <item name="config_pictureInPictureExpandedHorizontalHeight" format="dimension" type="dimen">110dp</item> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 05894d55358e..5ac30de631c5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3704,26 +3704,6 @@ snapped to any position between the first target and the last target. --> <bool name="config_dockedStackDividerFreeSnapMode">false</bool> - <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. - These values are in DPs and will be converted to pixel sizes internally. --> - <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">16x16</string> - - <!-- The percentage of the screen width to use for the default width or height of - picture-in-picture windows. Regardless of the percent set here, calculated size will never - be smaller than @dimen/default_minimal_size_pip_resizable_task. --> - <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> - - <!-- The default aspect ratio for picture-in-picture windows. --> - <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen">1.777778</item> - - <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size - will be used instead of an adaptive size based loosely on area. --> - <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen">1.777778</item> - - <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x55</integer> - <!-- The minimum aspect ratio (width/height) that is supported for picture-in-picture. Any ratio smaller than this is considered too tall and thin to be usable. Currently, this is the inverse of the max landscape aspect ratio (1:2.39), but this is an extremely diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index adf8f8e99d35..032d0b954ef1 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -717,16 +717,6 @@ <!-- The default minimal size of a resizable task, in both dimensions. --> <dimen name="default_minimal_size_resizable_task">220dp</dimen> - <!-- The default minimal size of a PiP task, in both dimensions. --> - <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen> - - <!-- - The overridable minimal size of a PiP task, in both dimensions. - Different from default_minimal_size_pip_resizable_task, this is to limit the dimension - when the pinned stack size is overridden by app via minWidth/minHeight. - --> - <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> - <!-- Height of a task when in minimized mode from the top when launcher is resizable. --> <dimen name="task_height_of_minimized_mode">80dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 558e3c3a0222..60a3398c93e6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -408,11 +408,6 @@ <java-symbol type="array" name="config_localPrivateDisplayPorts" /> <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> <java-symbol type="bool" name="config_enableAppWidgetService" /> - <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" /> - <java-symbol type="dimen" name="config_pictureInPictureDefaultSizePercent" /> - <java-symbol type="dimen" name="config_pictureInPictureDefaultAspectRatio" /> - <java-symbol type="dimen" name="config_pictureInPictureAspectRatioLimitForMinSize" /> - <java-symbol type="integer" name="config_defaultPictureInPictureGravity" /> <java-symbol type="dimen" name="config_pictureInPictureMinAspectRatio" /> <java-symbol type="dimen" name="config_pictureInPictureMaxAspectRatio" /> <java-symbol type="integer" name="config_pictureInPictureMaxNumberOfActions" /> @@ -1295,6 +1290,7 @@ <java-symbol type="array" name="vendor_required_apps_managed_user" /> <java-symbol type="array" name="vendor_required_apps_managed_profile" /> <java-symbol type="array" name="vendor_required_apps_managed_device" /> + <java-symbol type="array" name="vendor_required_attestation_certificates" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_user" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" /> <java-symbol type="array" name="vendor_disallowed_apps_managed_device" /> @@ -2016,8 +2012,6 @@ <java-symbol type="id" name="replace_message" /> <java-symbol type="fraction" name="config_dimBehindFadeDuration" /> <java-symbol type="dimen" name="default_minimal_size_resizable_task" /> - <java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" /> - <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" /> <java-symbol type="dimen" name="task_height_of_minimized_mode" /> <java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" /> <java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" /> diff --git a/core/res/res/values/vendor_required_attestation_certificates.xml b/core/res/res/values/vendor_required_attestation_certificates.xml new file mode 100644 index 000000000000..ce5660f433ff --- /dev/null +++ b/core/res/res/values/vendor_required_attestation_certificates.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +--> +<resources> + <!-- The PEM-encoded certificates added here are used for verifying attestations. + The trustworthiness of the attestation depends on the root certificate of the chain. + + Certificates that can be used can be retrieved from: + https://developer.android.com/training/articles/security-key-attestation#root_certificate. + + If not already present in resource overlay, please add + vendor_required_attestation_certificates.xml (matching this file) in vendor overlay + with <item></item> of the PEM-encoded root certificates. + --> + <string-array translatable="false" name="vendor_required_attestation_certificates"> + </string-array> +</resources> diff --git a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java index 68d4cd4a97e7..1bc46a79f1c0 100644 --- a/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/ManagedUserContentResolverTest.java @@ -20,7 +20,6 @@ import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.platform.test.annotations.Presubmit; import androidx.test.filters.LargeTest; @@ -37,7 +36,6 @@ import androidx.test.filters.LargeTest; * Run: adb shell am instrument -e class android.content.ManagedUserContentResolverTest -w \ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ -@Presubmit @LargeTest public class ManagedUserContentResolverTest extends AbstractCrossUserContentResolverTest { @Override diff --git a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java index de4c5725c190..dbe027800e3f 100644 --- a/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java +++ b/core/tests/coretests/src/android/content/SecondaryUserContentResolverTest.java @@ -18,7 +18,6 @@ package android.content; import android.content.pm.UserInfo; import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; import androidx.test.filters.LargeTest; @@ -35,7 +34,6 @@ import androidx.test.filters.LargeTest; * Run: adb shell am instrument -e class android.content.SecondaryUserContentResolverTest -w \ * com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner */ -@Presubmit @LargeTest public class SecondaryUserContentResolverTest extends AbstractCrossUserContentResolverTest { @Override diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 177965534b18..ed8986993ab0 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3793,6 +3793,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/NonAppWindowAnimationAdapter.java" }, + "2001473656": { + "message": "App %s is focused, but the window is not ready. Start a transaction to remove focus from the window of non-focused apps.", + "level": "VERBOSE", + "group": "WM_DEBUG_FOCUS_LIGHT", + "at": "com\/android\/server\/wm\/InputMonitor.java" + }, "2018454757": { "message": "WS.removeImmediately: %s Already removed...", "level": "VERBOSE", diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml new file mode 100644 index 000000000000..552048e8be9a --- /dev/null +++ b/libs/WindowManager/Shell/res/values-television/config.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<!-- These resources are around just to allow their values to be customized + for TV products. Do not translate. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.2</item> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 24x24 + </string> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> +</resources> diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 1b8032b7077b..d416c060c86c 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -70,4 +70,30 @@ <!-- Animation duration when exit starting window: reveal app --> <integer name="starting_window_app_reveal_anim_duration">266</integer> + + <!-- Default insets [LEFT/RIGHTxTOP/BOTTOM] from the screen edge for picture-in-picture windows. + These values are in DPs and will be converted to pixel sizes internally. --> + <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets"> + 16x16 + </string> + + <!-- The percentage of the screen width to use for the default width or height of + picture-in-picture windows. Regardless of the percent set here, calculated size will never + be smaller than @dimen/default_minimal_size_pip_resizable_task. --> + <item name="config_pictureInPictureDefaultSizePercent" format="float" type="dimen">0.23</item> + + <!-- The default aspect ratio for picture-in-picture windows. --> + <item name="config_pictureInPictureDefaultAspectRatio" format="float" type="dimen"> + 1.777778 + </item> + + <!-- This is the limit for the max and min aspect ratio (1 / this value) at which the min size + will be used instead of an adaptive size based loosely on area. --> + <item name="config_pictureInPictureAspectRatioLimitForMinSize" format="float" type="dimen"> + 1.777778 + </item> + + <!-- The default gravity for the picture-in-picture window. + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> </resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index bb8a43d87cb8..2c96786bb521 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -254,4 +254,14 @@ <!-- The distance of the shift icon when early exit starting window. --> <dimen name="starting_surface_early_exit_icon_distance">32dp</dimen> + + <!-- The default minimal size of a PiP task, in both dimensions. --> + <dimen name="default_minimal_size_pip_resizable_task">108dp</dimen> + + <!-- + The overridable minimal size of a PiP task, in both dimensions. + Different from default_minimal_size_pip_resizable_task, this is to limit the dimension + when the pinned stack size is overridden by app via minWidth/minHeight. + --> + <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 241f1a7d4d1c..d0138a488295 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -47,7 +47,10 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -131,6 +134,10 @@ public class BubbleController { public static final String RIGHT_POSITION = "Right"; public static final String BOTTOM_POSITION = "Bottom"; + // Should match with PhoneWindowManager + private static final String SYSTEM_DIALOG_REASON_KEY = "reason"; + private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; + private final Context mContext; private final BubblesImpl mImpl = new BubblesImpl(); private Bubbles.BubbleExpandListener mExpandListener; @@ -675,6 +682,7 @@ public class BubbleController { try { mAddedToWindowManager = true; + registerBroadcastReceiver(); mBubbleData.getOverflow().initialize(this); mWindowManager.addView(mStackView, mWmLayoutParams); mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> { @@ -717,6 +725,7 @@ public class BubbleController { try { mAddedToWindowManager = false; + mContext.unregisterReceiver(mBroadcastReceiver); if (mStackView != null) { mWindowManager.removeView(mStackView); mBubbleData.getOverflow().cleanUpExpandedState(); @@ -730,11 +739,34 @@ public class BubbleController { } } + private void registerBroadcastReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + filter.addAction(Intent.ACTION_SCREEN_OFF); + mContext.registerReceiver(mBroadcastReceiver, filter); + } + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (!isStackExpanded()) return; // Nothing to do + + String action = intent.getAction(); + String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); + if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) + && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason)) + || Intent.ACTION_SCREEN_OFF.equals(action)) { + mMainExecutor.execute(() -> collapseStack()); + } + } + }; + /** * Called by the BubbleStackView and whenever all bubbles have animated out, and none have been * added in the meantime. */ - void onAllBubblesAnimatedOut() { + @VisibleForTesting + public void onAllBubblesAnimatedOut() { if (mStackView != null) { mStackView.setVisibility(INVISIBLE); removeFromWindowManagerMaybe(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index e29dde2726e3..797df413d262 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -32,6 +32,7 @@ import android.util.Size; import android.util.TypedValue; import android.view.Gravity; +import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; import java.io.PrintWriter; @@ -76,15 +77,15 @@ public class PipBoundsAlgorithm { protected void reloadResources(Context context) { final Resources res = context.getResources(); mDefaultAspectRatio = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); + R.dimen.config_pictureInPictureDefaultAspectRatio); mDefaultStackGravity = res.getInteger( - com.android.internal.R.integer.config_defaultPictureInPictureGravity); + R.integer.config_defaultPictureInPictureGravity); mDefaultMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task); + R.dimen.default_minimal_size_pip_resizable_task); mOverridableMinSize = res.getDimensionPixelSize( - com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task); + R.dimen.overridable_minimal_size_pip_resizable_task); final String screenEdgeInsetsDpString = res.getString( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets); + R.string.config_defaultPictureInPictureScreenEdgeInsets); final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() ? Size.parseSize(screenEdgeInsetsDpString) : null; @@ -96,9 +97,9 @@ public class PipBoundsAlgorithm { mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + R.dimen.config_pictureInPictureDefaultSizePercent); mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 5af15300d10b..4bc5850cd293 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -26,6 +26,7 @@ import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; +import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION; import static android.app.admin.DevicePolicyResources.Drawables.Style.OUTLINE; @@ -156,9 +157,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - boolean isDrawable = intent.getBooleanExtra( - EXTRA_RESOURCE_TYPE_DRAWABLE, /* default= */ false); - if (!isDrawable) { + if (intent.getIntExtra(EXTRA_RESOURCE_TYPE, /* default= */ -1) + != EXTRA_RESOURCE_TYPE_DRAWABLE) { return; } updateEnterpriseThumbnailDrawable(); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index 654fa4ec9446..8d542c8ec9e6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -56,13 +56,6 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { .sendBroadcast(createIntentWithAction(broadcastAction)) } - fun requestOrientationForPip(orientation: Int) { - instrumentation.context.sendBroadcast( - createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION) - .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString()) - ) - } - companion object { // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index d65388bd8cc5..f7384e742a04 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -25,11 +25,14 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group4 import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled +import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE +import com.android.wm.shell.flicker.testapp.Components import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -39,7 +42,7 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test Pip with orientation changes. + * Test exiting Pip with orientation changes. * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTest` */ @RequiresDevice @@ -61,30 +64,41 @@ open class SetRequestedOrientationWhilePinnedTest( override val transition: FlickerBuilder.() -> Unit get() = { - setupAndTeardown(this) - setup { + test { + removeAllTasksButHome() + device.wakeUpAndGoToHomeScreen() + } eachRun { - // Launch the PiP activity fixed as landscape + // Launch the PiP activity fixed as landscape. pipApp.launchViaIntent(wmHelper, stringExtras = mapOf( - EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString(), - EXTRA_ENTER_PIP to "true")) + EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())) + // Enter PiP. + broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP) + wmHelper.waitPipShown() + wmHelper.waitForRotation(Surface.ROTATION_0) + wmHelper.waitForAppTransitionIdle() + // System bar may fade out during fixed rotation. + wmHelper.waitForNavBarStatusBarVisible() } } teardown { eachRun { pipApp.exit(wmHelper) + setRotation(Surface.ROTATION_0) + } + test { + removeAllTasksButHome() } } transitions { - // Request that the orientation is set to landscape - broadcastActionTrigger.requestOrientationForPip(ORIENTATION_LANDSCAPE) - - // Launch the activity back into fullscreen and - // ensure that it is now in landscape + // Launch the activity back into fullscreen and ensure that it is now in landscape pipApp.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(pipApp.component) wmHelper.waitForRotation(Surface.ROTATION_90) + wmHelper.waitForAppTransitionIdle() + // System bar may fade out during fixed rotation. + wmHelper.waitForNavBarStatusBarVisible() } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt index 36e28049864f..8d764a8d0e69 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTestShellTransit.kt @@ -29,6 +29,10 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized +/** + * Test exiting Pip with orientation changes. + * To run this test: `atest WMShellFlickerTests:SetRequestedOrientationWhilePinnedTestShellTransit` + */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 90f898aa09da..0059846c6055 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -29,6 +29,7 @@ import android.view.Gravity; import androidx.test.filters.SmallTest; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; @@ -72,16 +73,16 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void initializeMockResources() { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, DEFAULT_ASPECT_RATIO); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + R.dimen.default_minimal_size_pip_resizable_task, DEFAULT_MIN_EDGE_SIZE); res.addOverride( - com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, + R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); res.addOverride( com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, @@ -107,7 +108,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { public void onConfigurationChanged_reloadResources() { final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; final TestableResources res = mContext.getOrCreateTestableResources(); - res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + res.addOverride(R.dimen.config_pictureInPictureDefaultAspectRatio, newDefaultAspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); @@ -463,7 +464,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultAspectRatio(float aspectRatio) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + R.dimen.config_pictureInPictureDefaultAspectRatio, aspectRatio); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } @@ -471,7 +472,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private void overrideDefaultStackGravity(int stackGravity) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( - com.android.internal.R.integer.config_defaultPictureInPictureGravity, + R.integer.config_defaultPictureInPictureGravity, stackGravity); mPipBoundsAlgorithm.onConfigurationChanged(mContext); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java index 06b6fc8ef73d..b2258e1d4208 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java @@ -80,8 +80,8 @@ public class BiometricActionDisabledByAdminControllerTest { assertEquals(Settings.ACTION_MANAGE_SUPERVISOR_RESTRICTED_SETTING, intentCaptor.getValue().getAction()); assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS, - intentCaptor.getValue().getStringExtra( - Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY)); + intentCaptor.getValue().getIntExtra( + Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY, -1)); assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage()); } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 121f9e58a62a..c0e2b2ef361f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -593,6 +593,9 @@ <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION"/> + <!-- Permission required for CTS test - MediaCodecResourceTest --> + <uses-permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" /> + <!-- Permission required for CTS test - ResourceObserverNativeTest --> <uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index ad6074a93c96..4b1d00bb18e3 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -311,6 +311,9 @@ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" /> + <!-- To change system captions state --> + <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> @@ -767,22 +770,6 @@ </intent-filter> </activity> - <activity android:name=".chooser.ChooserActivity" - android:theme="@*android:style/Theme.NoDisplay" - android:finishOnCloseSystemDialogs="true" - android:excludeFromRecents="true" - android:documentLaunchMode="never" - android:relinquishTaskIdentity="true" - android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" - android:process=":ui" - android:visibleToInstantApps="true" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.CHOOSER" /> - <category android:name="android.intent.category.VOICE" /> - </intent-filter> - </activity> - <activity android:name=".clipboardoverlay.EditTextActivity" android:theme="@style/EditTextActivity" android:exported="false" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java index 0a0530c056af..3d2f570bde87 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -60,7 +60,6 @@ public interface VolumeDialogController { boolean areCaptionsEnabled(); void setCaptionsEnabled(boolean isEnabled); - boolean isCaptionStreamOptedOut(); void getCaptionsComponentState(boolean fromTooltip); diff --git a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml index 59f87da79a11..9d801d2a0ecf 100644 --- a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml +++ b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml @@ -28,7 +28,7 @@ android:layout_height="match_parent" android:layout_marginEnd="@dimen/new_qs_footer_action_inset" android:background="@drawable/qs_security_footer_background" - android:layout_gravity="center" + android:layout_gravity="end" android:gravity="center" android:paddingHorizontal="@dimen/qs_footer_padding" > diff --git a/packages/SystemUI/res/color/caption_tint_color_selector.xml b/packages/SystemUI/res/color/caption_tint_color_selector.xml index 30843ec70f91..5239d269eb4a 100644 --- a/packages/SystemUI/res/color/caption_tint_color_selector.xml +++ b/packages/SystemUI/res/color/caption_tint_color_selector.xml @@ -16,8 +16,5 @@ --> <selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto"> - <item sysui:optedOut="true" - android:color="?android:attr/colorButtonNormal"/> - <item android:color="?android:attr/colorAccent"/> </selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog.xml b/packages/SystemUI/res/layout-land-television/volume_dialog.xml index 6b5629facd41..0fbc519ca8dd 100644 --- a/packages/SystemUI/res/layout-land-television/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land-television/volume_dialog.xml @@ -73,8 +73,7 @@ android:layout_height="match_parent" android:tint="@color/caption_tint_color_selector" android:layout_gravity="center" - android:soundEffectsEnabled="false" - sysui:optedOut="false"/> + android:soundEffectsEnabled="false"/> </FrameLayout> diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml index f1cda277f045..3b70dc060e84 100644 --- a/packages/SystemUI/res/layout-land/volume_dialog.xml +++ b/packages/SystemUI/res/layout-land/volume_dialog.xml @@ -136,8 +136,7 @@ android:layout_height="match_parent" android:tint="?android:attr/colorAccent" android:layout_gravity="center" - android:soundEffectsEnabled="false" - sysui:optedOut="false"/> + android:soundEffectsEnabled="false" /> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml new file mode 100644 index 000000000000..37b8365996fd --- /dev/null +++ b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/dream_preview_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="@dimen/dream_overlay_complication_preview_text_size" + android:textColor="@android:color/white" + android:shadowColor="@color/keyguard_shadow_color" + android:shadowRadius="?attr/shadowRadius" + android:gravity="center_vertical" + android:drawableStart="@drawable/ic_arrow_back" + android:drawablePadding="@dimen/dream_overlay_complication_preview_icon_padding" + android:drawableTint="@android:color/white"/> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml index 6bb6c2d9877c..0f2d372f7158 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml @@ -55,16 +55,17 @@ <TextView android:id="@+id/add" - android:visibility="gone" + style="@style/Widget.Dialog.Button.BorderButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" - app:layout_constraintHeight_min="48dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent" android:paddingHorizontal="@dimen/user_switcher_fullscreen_button_padding" - android:textSize="@dimen/user_switcher_fullscreen_button_text_size" + android:text="@string/add" android:textColor="?androidprv:attr/colorAccentPrimary" - android:text="@string/add" /> + android:textSize="@dimen/user_switcher_fullscreen_button_text_size" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHeight_min="48dp" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml index a3d9a69e73c5..60e840c0708b 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen_item.xml @@ -13,21 +13,30 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<LinearLayout +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> - <ImageView - android:id="@+id/user_switcher_icon" - android:layout_gravity="center" - android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin" - android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" /> - <TextView - style="@style/Bouncer.UserSwitcher.Spinner.Item" - android:id="@+id/user_switcher_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textColor="@*android:color/text_color_primary_device_default_dark" - android:layout_gravity="center" /> -</LinearLayout> + + <ImageView + android:id="@+id/user_switcher_icon" + android:layout_width="@dimen/bouncer_user_switcher_icon_size_plus_margin" + android:layout_height="@dimen/bouncer_user_switcher_icon_size_plus_margin" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <TextView + android:id="@+id/user_switcher_text" + style="@style/Bouncer.UserSwitcher.Spinner.Item" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="center" + android:textColor="@*android:color/text_color_primary_device_default_dark" + app:layout_constraintEnd_toEndOf="@id/user_switcher_icon" + app:layout_constraintStart_toStartOf="@id/user_switcher_icon" + app:layout_constraintTop_toBottomOf="@id/user_switcher_icon" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index 51718d9af054..6a192d4b7e05 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -135,8 +135,7 @@ android:layout_height="match_parent" android:tint="?android:attr/colorAccent" android:layout_gravity="center" - android:soundEffectsEnabled="false" - sysui:optedOut="false"/> + android:soundEffectsEnabled="false"/> </FrameLayout> </LinearLayout> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 01eb09b659f2..c386a3e684a9 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -22,7 +22,6 @@ <dimen name="docked_divider_handle_width">2dp</dimen> <dimen name="docked_divider_handle_height">16dp</dimen> - <dimen name="qs_tile_height">84dp</dimen> <dimen name="qs_brightness_margin_top">0dp</dimen> <dimen name="qs_brightness_margin_bottom">12dp</dimen> <dimen name="qqs_layout_margin_top">8dp</dimen> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 8a4516aaa49f..c37c804caaa8 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -42,8 +42,4 @@ the shade (in alpha) --> <dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen> - <!-- Distance that the full shade transition takes in order for media to fully transition to - the shade --> - <dimen name="lockscreen_shade_media_transition_distance">200dp</dimen> - </resources> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index c5e005c556b2..62903d5a7b35 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -154,10 +154,6 @@ <attr name="showAirplaneMode" format="boolean" /> </declare-styleable> - <declare-styleable name="CaptionsToggleImageButton"> - <attr name="optedOut" format="boolean" /> - </declare-styleable> - <declare-styleable name="IlluminationDrawable"> <attr name="highlight" format="integer" /> <attr name="cornerRadius" format="dimension" /> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 178f93ae3c93..73ef37ca02ed 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -74,7 +74,12 @@ <!-- The default tiles to display in QuickSettings --> <string name="quick_settings_tiles_default" translatable="false"> - internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle + internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService) + </string> + + <!-- The component name of the Safety Quick Settings Tile --> + <string name="safety_quick_settings_tile" translatable="false"> + custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService) </string> <!-- The minimum number of tiles to display in QuickSettings --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index fcf60bf43c62..92bc8642b75c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -470,7 +470,7 @@ <dimen name="pull_span_min">25dp</dimen> <dimen name="qs_corner_radius">28dp</dimen> - <dimen name="qs_tile_height">84dp</dimen> + <dimen name="qs_tile_height">80dp</dimen> <dimen name="qs_tile_margin_horizontal">8dp</dimen> <dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen> <dimen name="qs_tile_margin_top_bottom">4dp</dimen> @@ -1347,6 +1347,8 @@ <dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen> <dimen name="dream_overlay_complication_clock_date_text_size">18sp</dimen> <dimen name="dream_overlay_complication_weather_text_size">18sp</dimen> + <dimen name="dream_overlay_complication_preview_text_size">36sp</dimen> + <dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen> <!-- The position of the end guide, which dream overlay complications can align their start with if their end is aligned with the parent end. Represented as the percentage over from the diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index b611c9659db8..6ad91612f99b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -35,6 +35,11 @@ interface ResourceFlag<T> : Flag<T> { val resourceId: Int } +interface SysPropFlag<T> : Flag<T> { + val name: String + val default: T +} + // Consider using the "parcelize" kotlin library. data class BooleanFlag @JvmOverloads constructor( @@ -66,6 +71,12 @@ data class ResourceBooleanFlag constructor( @BoolRes override val resourceId: Int ) : ResourceFlag<Boolean> +data class SysPropBooleanFlag constructor( + override val id: Int, + override val name: String, + override val default: Boolean = false +) : SysPropFlag<Boolean> + data class StringFlag @JvmOverloads constructor( override val id: Int, override val default: String = "" diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt index ec619dd6eea3..149f6e8e7e9e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt @@ -54,7 +54,7 @@ class FlagManager constructor( * An action called on restart which takes as an argument whether the listeners requested * that the restart be suppressed */ - var restartAction: Consumer<Boolean>? = null + var onSettingsChangedAction: Consumer<Boolean>? = null var clearCacheAction: Consumer<Int>? = null private val listeners: MutableSet<PerFlagListener> = mutableSetOf() private val settingsObserver: ContentObserver = SettingsObserver() @@ -154,11 +154,11 @@ class FlagManager constructor( val idStr = parts[parts.size - 1] val id = try { idStr.toInt() } catch (e: NumberFormatException) { return } clearCacheAction?.accept(id) - dispatchListenersAndMaybeRestart(id) + dispatchListenersAndMaybeRestart(id, onSettingsChangedAction) } } - fun dispatchListenersAndMaybeRestart(id: Int) { + fun dispatchListenersAndMaybeRestart(id: Int, restartAction: Consumer<Boolean>?) { val filteredListeners: List<FlagListenable.Listener> = synchronized(listeners) { listeners.mapNotNull { if (it.id == id) it.listener else null } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt index f6fe1ede9ce0..a50d852e9b36 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt @@ -4,6 +4,7 @@ import android.hardware.Sensor import android.hardware.SensorEvent import android.hardware.SensorEventListener import android.hardware.SensorManager +import android.os.Trace import androidx.core.util.Consumer internal class HingeSensorAngleProvider(private val sensorManager: SensorManager) : @@ -13,8 +14,10 @@ internal class HingeSensorAngleProvider(private val sensorManager: SensorManager private val listeners: MutableList<Consumer<Float>> = arrayListOf() override fun start() { + Trace.beginSection("HingeSensorAngleProvider#start") val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE) sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST) + Trace.endSection() } override fun stop() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 848b8ab8ff84..1ede76fb1fa4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -26,6 +26,7 @@ import android.hardware.display.DisplayManager; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; import android.os.Bundle; +import android.os.Trace; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -67,11 +68,14 @@ public class KeyguardDisplayManager { @Override public void onDisplayAdded(int displayId) { + Trace.beginSection( + "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")"); final Display display = mDisplayService.getDisplay(displayId); if (mShowing) { updateNavigationBarVisibility(displayId, false /* navBarVisible */); showPresentation(display); } + Trace.endSection(); } @Override @@ -81,7 +85,10 @@ public class KeyguardDisplayManager { @Override public void onDisplayRemoved(int displayId) { + Trace.beginSection( + "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")"); hidePresentation(displayId); + Trace.endSection(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt index 6bec8aacd55f..56046d9c057d 100644 --- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt +++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt @@ -49,7 +49,7 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { private val shouldDrawCutout: Boolean = DisplayCutout.getFillBuiltInDisplayCutout( context.resources, context.display?.uniqueId) private var displayMode: Display.Mode? = null - private val location = IntArray(2) + protected val location = IntArray(2) protected var displayRotation = 0 @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) @@ -65,7 +65,8 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { @JvmField val protectionPath: Path = Path() private val protectionRectOrig: RectF = RectF() private val protectionPathOrig: Path = Path() - private var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE + @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) + var cameraProtectionProgress: Float = HIDDEN_CAMERA_PROTECTION_SCALE private var cameraProtectionAnimator: ValueAnimator? = null constructor(context: Context) : super(context) @@ -273,4 +274,4 @@ open class DisplayCutoutBaseView : View, RegionInterceptableView { } } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt index ee1d9a3aa5de..4b868622ce58 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt @@ -26,11 +26,22 @@ import android.graphics.PixelFormat import android.graphics.PorterDuff import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffXfermode +import android.graphics.Rect +import android.graphics.Region import android.graphics.drawable.Drawable import android.hardware.graphics.common.AlphaInterpretation import android.hardware.graphics.common.DisplayDecorationSupport +import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM +import android.view.DisplayCutout.BOUNDS_POSITION_LEFT +import android.view.DisplayCutout.BOUNDS_POSITION_LENGTH +import android.view.DisplayCutout.BOUNDS_POSITION_TOP +import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT import android.view.RoundedCorner import android.view.RoundedCorners +import android.view.Surface +import androidx.annotation.VisibleForTesting +import kotlin.math.ceil +import kotlin.math.floor /** * When the HWC of the device supports Composition.DISPLAY_DECORATON, we use this layer to draw @@ -38,13 +49,16 @@ import android.view.RoundedCorners */ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport) : DisplayCutoutBaseView(context) { - public val colorMode: Int + val colorMode: Int private val useInvertedAlphaColor: Boolean private val color: Int private val bgColor: Int private val cornerFilter: ColorFilter private val cornerBgFilter: ColorFilter private val clearPaint: Paint + @JvmField val transparentRect: Rect = Rect() + private val debugTransparentRegionPaint: Paint? + private val tempRect: Rect = Rect() private var roundedCornerTopSize = 0 private var roundedCornerBottomSize = 0 @@ -61,6 +75,10 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec bgColor = Color.TRANSPARENT colorMode = ActivityInfo.COLOR_MODE_DEFAULT useInvertedAlphaColor = false + debugTransparentRegionPaint = Paint().apply { + color = 0x2f00ff00 // semi-transparent green + style = Paint.Style.FILL + } } else { colorMode = ActivityInfo.COLOR_MODE_A8 useInvertedAlphaColor = displayDecorationSupport.alphaInterpretation == @@ -72,6 +90,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec color = Color.BLACK bgColor = Color.TRANSPARENT } + debugTransparentRegionPaint = null } cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN) cornerBgFilter = PorterDuffColorFilter(bgColor, PorterDuff.Mode.SRC_OUT) @@ -82,6 +101,9 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec override fun onAttachedToWindow() { super.onAttachedToWindow() + if (!DEBUG_COLOR) { + parent.requestTransparentRegion(this) + } viewRootImpl.setDisplayDecoration(true) if (useInvertedAlphaColor) { @@ -93,12 +115,172 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec } override fun onDraw(canvas: Canvas) { + // If updating onDraw, also update gatherTransparentRegion if (useInvertedAlphaColor) { canvas.drawColor(bgColor) } // Cutouts are drawn in DisplayCutoutBaseView.onDraw() super.onDraw(canvas) drawRoundedCorners(canvas) + + debugTransparentRegionPaint?.let { + calculateTransparentRect() + canvas.drawRect(transparentRect, it) + } + } + + override fun gatherTransparentRegion(region: Region?): Boolean { + region?.let { + calculateTransparentRect() + region.op(transparentRect, Region.Op.INTERSECT) + } + // Always return false - views underneath this should always be visible. + return false + } + + /** + * The transparent rect is calculated by subtracting the regions of cutouts, cutout protect and + * rounded corners from the region with fullscreen display size. + */ + @VisibleForTesting + fun calculateTransparentRect() { + transparentRect.set(0, 0, width, height) + + // Remove cutout region. + removeCutoutFromTransparentRegion() + + // Remove cutout protection region. + removeCutoutProtectionFromTransparentRegion() + + // Remove rounded corner region. + removeRoundedCornersFromTransparentRegion() + } + + private fun removeCutoutFromTransparentRegion() { + displayInfo.displayCutout?.let { + cutout -> + if (!cutout.boundingRectLeft.isEmpty) { + transparentRect.left = + cutout.boundingRectLeft.right.coerceAtLeast(transparentRect.left) + } + if (!cutout.boundingRectTop.isEmpty) { + transparentRect.top = + cutout.boundingRectTop.bottom.coerceAtLeast(transparentRect.top) + } + if (!cutout.boundingRectRight.isEmpty) { + transparentRect.right = + cutout.boundingRectRight.left.coerceAtMost(transparentRect.right) + } + if (!cutout.boundingRectBottom.isEmpty) { + transparentRect.bottom = + cutout.boundingRectBottom.top.coerceAtMost(transparentRect.bottom) + } + } + } + + private fun removeCutoutProtectionFromTransparentRegion() { + if (protectionRect.isEmpty) { + return + } + + val centerX = protectionRect.centerX() + val centerY = protectionRect.centerY() + val scaledDistanceX = (centerX - protectionRect.left) * cameraProtectionProgress + val scaledDistanceY = (centerY - protectionRect.top) * cameraProtectionProgress + tempRect.set( + floor(centerX - scaledDistanceX).toInt(), + floor(centerY - scaledDistanceY).toInt(), + ceil(centerX + scaledDistanceX).toInt(), + ceil(centerY + scaledDistanceY).toInt() + ) + + // Find out which edge the protectionRect belongs and remove that edge from the transparent + // region. + val leftDistance = tempRect.left + val topDistance = tempRect.top + val rightDistance = width - tempRect.right + val bottomDistance = height - tempRect.bottom + val minDistance = minOf(leftDistance, topDistance, rightDistance, bottomDistance) + when (minDistance) { + leftDistance -> { + transparentRect.left = tempRect.right.coerceAtLeast(transparentRect.left) + } + topDistance -> { + transparentRect.top = tempRect.bottom.coerceAtLeast(transparentRect.top) + } + rightDistance -> { + transparentRect.right = tempRect.left.coerceAtMost(transparentRect.right) + } + bottomDistance -> { + transparentRect.bottom = tempRect.top.coerceAtMost(transparentRect.bottom) + } + } + } + + private fun removeRoundedCornersFromTransparentRegion() { + var hasTopOrBottomCutouts = false + var hasLeftOrRightCutouts = false + displayInfo.displayCutout?.let { + cutout -> + hasTopOrBottomCutouts = !cutout.boundingRectTop.isEmpty || + !cutout.boundingRectBottom.isEmpty + hasLeftOrRightCutouts = !cutout.boundingRectLeft.isEmpty || + !cutout.boundingRectRight.isEmpty + } + // The goal is to remove the rounded corner areas as small as possible so that we can have a + // larger transparent region. Therefore, we should always remove from the short edge sides + // if possible. + val isShortEdgeTopBottom = width < height + if (isShortEdgeTopBottom) { + // Short edges on top & bottom. + if (!hasTopOrBottomCutouts && hasLeftOrRightCutouts) { + // If there are cutouts only on left or right edges, remove left and right sides + // for rounded corners. + transparentRect.left = getRoundedCornerSizeByPosition(BOUNDS_POSITION_LEFT) + .coerceAtLeast(transparentRect.left) + transparentRect.right = + (width - getRoundedCornerSizeByPosition(BOUNDS_POSITION_RIGHT)) + .coerceAtMost(transparentRect.right) + } else { + // If there are cutouts on top or bottom edges or no cutout at all, remove top + // and bottom sides for rounded corners. + transparentRect.top = getRoundedCornerSizeByPosition(BOUNDS_POSITION_TOP) + .coerceAtLeast(transparentRect.top) + transparentRect.bottom = + (height - getRoundedCornerSizeByPosition(BOUNDS_POSITION_BOTTOM)) + .coerceAtMost(transparentRect.bottom) + } + } else { + // Short edges on left & right. + if (hasTopOrBottomCutouts && !hasLeftOrRightCutouts) { + // If there are cutouts only on top or bottom edges, remove top and bottom sides + // for rounded corners. + transparentRect.top = getRoundedCornerSizeByPosition(BOUNDS_POSITION_TOP) + .coerceAtLeast(transparentRect.top) + transparentRect.bottom = + (height - getRoundedCornerSizeByPosition(BOUNDS_POSITION_BOTTOM)) + .coerceAtMost(transparentRect.bottom) + } else { + // If there are cutouts on left or right edges or no cutout at all, remove left + // and right sides for rounded corners. + transparentRect.left = getRoundedCornerSizeByPosition(BOUNDS_POSITION_LEFT) + .coerceAtLeast(transparentRect.left) + transparentRect.right = + (width - getRoundedCornerSizeByPosition(BOUNDS_POSITION_RIGHT)) + .coerceAtMost(transparentRect.right) + } + } + } + + private fun getRoundedCornerSizeByPosition(position: Int): Int { + val delta = displayRotation - Surface.ROTATION_0 + return when ((position + delta) % BOUNDS_POSITION_LENGTH) { + BOUNDS_POSITION_LEFT -> roundedCornerTopSize.coerceAtLeast(roundedCornerBottomSize) + BOUNDS_POSITION_TOP -> roundedCornerTopSize + BOUNDS_POSITION_RIGHT -> roundedCornerTopSize.coerceAtLeast(roundedCornerBottomSize) + BOUNDS_POSITION_BOTTOM -> roundedCornerBottomSize + else -> throw IllegalArgumentException("Incorrect position: $position") + } } private fun drawRoundedCorners(canvas: Canvas) { diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 2ec5f4f894f9..ae41cae10bba 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -847,14 +847,20 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab pw.println(" mIsRoundedCornerMultipleRadius:" + mIsRoundedCornerMultipleRadius); pw.println(" mIsPrivacyDotEnabled:" + isPrivacyDotEnabled()); pw.println(" mPendingRotationChange:" + mPendingRotationChange); - pw.println(" mHwcScreenDecorationSupport:"); - if (mHwcScreenDecorationSupport == null) { - pw.println(" null"); - } else { - pw.println(" format: " + if (mHwcScreenDecorationSupport != null) { + pw.println(" mHwcScreenDecorationSupport:"); + pw.println(" format=" + PixelFormat.formatToString(mHwcScreenDecorationSupport.format)); - pw.println(" alphaInterpretation: " + pw.println(" alphaInterpretation=" + alphaInterpretationToString(mHwcScreenDecorationSupport.alphaInterpretation)); + } else { + pw.println(" mHwcScreenDecorationSupport: null"); + } + if (mScreenDecorHwcLayer != null) { + pw.println(" mScreenDecorHwcLayer:"); + pw.println(" transparentRegion=" + mScreenDecorHwcLayer.transparentRect); + } else { + pw.println(" mScreenDecorHwcLayer: null"); } pw.println(" mRoundedDefault(x,y)=(" + mRoundedDefault.x + "," + mRoundedDefault.y + ")"); pw.println(" mRoundedDefaultTop(x,y)=(" + mRoundedDefaultTop.x + "," + mRoundedDefaultTop.y diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 3ece3ccf38fa..24a655c2856d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -20,6 +20,7 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import android.annotation.NonNull; import android.content.res.Configuration; +import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; @@ -47,6 +48,7 @@ import java.io.PrintWriter; * Class that coordinates non-HBM animations during keyguard authentication. */ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + public static final String TAG = "UdfpsKeyguardViewCtrl"; @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController; @@ -246,7 +248,12 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud } if (mInputBouncerHiddenAmount < .5f || mIsBouncerVisible) { - return true; + if (!getStatusBarStateController().isDozing()) { + return true; + } else { + Log.e(TAG, "Bouncer state claims visible on doze hiddenAmount=" + + mInputBouncerHiddenAmount + " bouncerVisible=" + mIsBouncerVisible); + } } return false; diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java deleted file mode 100644 index 28a38083218e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import android.app.Activity; -import android.os.Bundle; - -/** - * Activity for selecting which application ought to handle an ACTION_SEND intent. - */ -public final class ChooserActivity extends Activity { - - private static final String TAG = "ChooserActivity"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - ChooserHelper.onChoose(this); - finish(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java deleted file mode 100644 index 6c1da4711fec..000000000000 --- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import android.app.Activity; -import android.app.ActivityTaskManager; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.os.StrictMode; - -/** - * When a target is chosen from the SystemUI Chooser activity, unpack its arguments and - * startActivityAsCaller to handle the now-chosen intent. - */ -public class ChooserHelper { - - private static final String TAG = "ChooserHelper"; - - static void onChoose(Activity activity) { - final Intent thisIntent = activity.getIntent(); - final Bundle thisExtras = thisIntent.getExtras(); - final Intent chosenIntent = thisIntent.getParcelableExtra(Intent.EXTRA_INTENT); - final Bundle options = thisIntent.getParcelableExtra(ActivityTaskManager.EXTRA_OPTIONS); - final IBinder permissionToken = - thisExtras.getBinder(ActivityTaskManager.EXTRA_PERMISSION_TOKEN); - final boolean ignoreTargetSecurity = - thisIntent.getBooleanExtra(ActivityTaskManager.EXTRA_IGNORE_TARGET_SECURITY, false); - final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1); - - // We're dispatching intents that might be coming from legacy apps, so - // (as in com.android.internal.app.ResolverActivity) exempt ourselves from death. - StrictMode.disableDeathOnFileUriExposure(); - try { - activity.startActivityAsCaller( - chosenIntent, options, permissionToken, ignoreTargetSecurity, userId); - } finally { - StrictMode.enableDeathOnFileUriExposure(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index bd472a48ab3e..71c538d9324b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -71,6 +71,7 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.CaptioningManager; import android.view.inputmethod.InputMethodManager; import com.android.internal.app.IBatteryStats; @@ -120,6 +121,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static CaptioningManager provideCaptioningManager(Context context) { + return context.getSystemService(CaptioningManager.class); + } + + @Provides + @Singleton static ColorDisplayManager provideColorDisplayManager(Context context) { return context.getSystemService(ColorDisplayManager.class); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 338a8b2a2a59..7e1fce298fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -33,6 +33,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.Complication; +import com.android.systemui.dreams.complication.DreamPreviewComplication; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; @@ -57,6 +58,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final DreamPreviewComplication mPreviewComplication; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -96,12 +98,14 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ @Main Executor executor, DreamOverlayComponent.Factory dreamOverlayComponentFactory, DreamOverlayStateController stateController, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + DreamPreviewComplication previewComplication) { mContext = context; mExecutor = executor; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; + mPreviewComplication = previewComplication; final DreamOverlayComponent component = dreamOverlayComponentFactory.create(mViewModelStore, mHost); @@ -125,6 +129,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ windowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); + mPreviewComplication.setDreamLabel(null); + mStateController.removeComplication(mPreviewComplication); + mStateController.setPreviewMode(false); super.onDestroy(); } @@ -133,6 +140,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ setCurrentState(Lifecycle.State.STARTED); mExecutor.execute(() -> { mStateController.setShouldShowComplications(shouldShowComplications()); + mStateController.setPreviewMode(isPreviewMode()); + if (isPreviewMode()) { + mPreviewComplication.setDreamLabel(getDreamLabel()); + mStateController.addComplication(mPreviewComplication); + } addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index fc71e2fb2329..6860998680cb 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -50,6 +50,7 @@ public class DreamOverlayStateController implements private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0; + public static final int STATE_PREVIEW_MODE = 1 << 1; private static final int OP_CLEAR_STATE = 1; private static final int OP_SET_STATE = 2; @@ -249,4 +250,18 @@ public class DreamOverlayStateController implements mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged); }); } + + /** + * Sets whether the dream is running in preview mode. + */ + public void setPreviewMode(boolean isPreviewMode) { + modifyState(isPreviewMode ? OP_SET_STATE : OP_CLEAR_STATE, STATE_PREVIEW_MODE); + } + + /** + * Returns whether the dream is running in preview mode. + */ + public boolean isPreviewMode() { + return containsState(STATE_PREVIEW_MODE); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java index 240389a988ae..a5dcd39264df 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java @@ -59,7 +59,19 @@ public class SmartSpaceComplication implements Complication { @Override public void start() { - if (mSmartSpaceController.isEnabled()) { + addOrRemoveOverlay(); + mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() { + @Override + public void onStateChanged() { + addOrRemoveOverlay(); + } + }); + } + + private void addOrRemoveOverlay() { + if (mDreamOverlayStateController.isPreviewMode()) { + mDreamOverlayStateController.removeComplication(mComplication); + } else if (mSmartSpaceController.isEnabled()) { mDreamOverlayStateController.addComplication(mComplication); } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java new file mode 100644 index 000000000000..23343b17232f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication; + +import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DREAM_LABEL; +import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS; +import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_VIEW; + +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Preview complication shown when user is previewing a dream. + */ +public class DreamPreviewComplication implements Complication { + DreamPreviewComplicationComponent.Factory mComponentFactory; + @Nullable + private CharSequence mDreamLabel; + + /** + * Default constructor for {@link DreamPreviewComplication}. + */ + @Inject + public DreamPreviewComplication( + DreamPreviewComplicationComponent.Factory componentFactory) { + mComponentFactory = componentFactory; + } + + /** + * Create {@link DreamPreviewViewHolder}. + */ + @Override + public ViewHolder createView(ComplicationViewModel model) { + return mComponentFactory.create(model, mDreamLabel).getViewHolder(); + } + + /** + * Sets the user-facing label for the current dream. + */ + public void setDreamLabel(@Nullable CharSequence dreamLabel) { + mDreamLabel = dreamLabel; + } + + /** + * ViewHolder to contain value/logic associated with a Preview Complication View. + */ + public static class DreamPreviewViewHolder implements ViewHolder { + private final TextView mView; + private final ComplicationLayoutParams mLayoutParams; + private final DreamPreviewViewController mViewController; + + @Inject + DreamPreviewViewHolder(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view, + DreamPreviewViewController controller, + @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS) + ComplicationLayoutParams layoutParams, + @Named(DREAM_LABEL) @Nullable CharSequence dreamLabel) { + mView = view; + mLayoutParams = layoutParams; + mViewController = controller; + mViewController.init(); + + if (!TextUtils.isEmpty(dreamLabel)) { + mView.setText(dreamLabel); + } + } + + @Override + public View getView() { + return mView; + } + + @Override + public ComplicationLayoutParams getLayoutParams() { + return mLayoutParams; + } + + @Override + public int getCategory() { + return CATEGORY_SYSTEM; + } + } + + /** + * ViewController to contain value/logic associated with a Preview Complication View. + */ + static class DreamPreviewViewController extends ViewController<TextView> { + private final ComplicationViewModel mViewModel; + + @Inject + DreamPreviewViewController(@Named(DREAM_PREVIEW_COMPLICATION_VIEW) TextView view, + ComplicationViewModel viewModel) { + super(view); + mViewModel = viewModel; + } + + @Override + protected void onViewAttached() { + mView.setOnClickListener(v -> mViewModel.exitDream()); + } + + @Override + protected void onViewDetached() { + mView.setOnClickListener(null); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java new file mode 100644 index 000000000000..502e31ed0c7f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamPreviewComplicationComponent.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dreams.complication.dagger; + + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.android.internal.util.Preconditions; +import com.android.systemui.R; +import com.android.systemui.dreams.complication.ComplicationLayoutParams; +import com.android.systemui.dreams.complication.ComplicationViewModel; +import com.android.systemui.dreams.complication.DreamPreviewComplication.DreamPreviewViewHolder; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Named; +import javax.inject.Scope; + +import dagger.BindsInstance; +import dagger.Module; +import dagger.Provides; +import dagger.Subcomponent; + +/** + * {@link DreamPreviewComplicationComponent} is responsible for generating dependencies + * surrounding the + * Preview {@link com.android.systemui.dreams.complication.Complication}, such as the layout + * details. + */ +@Subcomponent(modules = { + DreamPreviewComplicationComponent.DreamPreviewComplicationModule.class, +}) +@DreamPreviewComplicationComponent.DreamPreviewComplicationScope +public interface DreamPreviewComplicationComponent { + String DREAM_LABEL = "dream_label"; + + /** + * Creates {@link DreamPreviewViewHolder}. + */ + DreamPreviewViewHolder getViewHolder(); + + @Documented + @Retention(RUNTIME) + @Scope + @interface DreamPreviewComplicationScope { + } + + /** + * Generates {@link DreamPreviewComplicationComponent}. + */ + @Subcomponent.Factory + interface Factory { + DreamPreviewComplicationComponent create( + @BindsInstance ComplicationViewModel viewModel, + @Named(DREAM_LABEL) @BindsInstance @Nullable CharSequence dreamLabel); + } + + /** + * Scoped values for {@link DreamPreviewComplicationComponent}. + */ + @Module + interface DreamPreviewComplicationModule { + String DREAM_PREVIEW_COMPLICATION_VIEW = "preview_complication_view"; + String DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS = "preview_complication_layout_params"; + // Order weight of insert into parent container + int INSERT_ORDER_WEIGHT = 1000; + + /** + * Provides the complication view. + */ + @Provides + @DreamPreviewComplicationScope + @Named(DREAM_PREVIEW_COMPLICATION_VIEW) + static TextView provideComplicationView(LayoutInflater layoutInflater) { + return Preconditions.checkNotNull((TextView) + layoutInflater.inflate(R.layout.dream_overlay_complication_preview, + null, false), + "R.layout.dream_overlay_complication_preview did not properly inflated"); + } + + /** + * Provides the layout parameters for the complication view. + */ + @Provides + @DreamPreviewComplicationScope + @Named(DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS) + static ComplicationLayoutParams provideLayoutParams() { + return new ComplicationLayoutParams(0, + ViewGroup.LayoutParams.WRAP_CONTENT, + ComplicationLayoutParams.POSITION_TOP + | ComplicationLayoutParams.POSITION_START, + ComplicationLayoutParams.DIRECTION_DOWN, + INSERT_ORDER_WEIGHT, /* snapToGuide= */ true); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index c61f7968c595..65f060b43c08 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -19,6 +19,7 @@ package com.android.systemui.dreams.dagger; import android.content.Context; import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent; import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule; import com.android.systemui.dreams.touch.dagger.DreamTouchModule; @@ -34,6 +35,7 @@ import dagger.Provides; }, subcomponents = { DreamOverlayComponent.class, + DreamPreviewComplicationComponent.class, }) public interface DreamModule { /** @@ -43,4 +45,4 @@ public interface DreamModule { static DreamBackend providesDreamBackend(Context context) { return DreamBackend.getInstance(context); } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt index 96a90dfc7fe9..9d6e3c295100 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt @@ -28,6 +28,9 @@ interface FeatureFlags : FlagListenable { /** Returns a boolean value for the given flag. */ fun isEnabled(flag: ResourceBooleanFlag): Boolean + /** Returns a boolean value for the given flag. */ + fun isEnabled(flag: SysPropBooleanFlag): Boolean + /** Returns a string value for the given flag. */ fun getString(flag: StringFlag): String diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index df605990fa81..677990437f3d 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -49,6 +49,7 @@ import java.util.ArrayList; import java.util.Map; import java.util.Objects; import java.util.TreeMap; +import java.util.function.Consumer; import java.util.function.Supplier; import javax.inject.Inject; @@ -69,6 +70,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { private final FlagManager mFlagManager; private final SecureSettings mSecureSettings; private final Resources mResources; + private final SystemPropertiesHelper mSystemProperties; private final Supplier<Map<Integer, Flag<?>>> mFlagsCollector; private final Map<Integer, Boolean> mBooleanFlagCache = new TreeMap<>(); private final Map<Integer, String> mStringFlagCache = new TreeMap<>(); @@ -79,6 +81,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { FlagManager flagManager, Context context, SecureSettings secureSettings, + SystemPropertiesHelper systemProperties, @Main Resources resources, DumpManager dumpManager, @Nullable Supplier<Map<Integer, Flag<?>>> flagsCollector, @@ -86,11 +89,12 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { mFlagManager = flagManager; mSecureSettings = secureSettings; mResources = resources; + mSystemProperties = systemProperties; mFlagsCollector = flagsCollector != null ? flagsCollector : Flags::collectFlags; IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_SET_FLAG); filter.addAction(ACTION_GET_FLAGS); - flagManager.setRestartAction(this::restartSystemUI); + flagManager.setOnSettingsChangedAction(this::restartSystemUI); flagManager.setClearCacheAction(this::removeFromCache); context.registerReceiver(mReceiver, filter, null, null, Context.RECEIVER_EXPORTED_UNAUDITED); @@ -121,6 +125,17 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { return mBooleanFlagCache.get(id); } + @Override + public boolean isEnabled(@NonNull SysPropBooleanFlag flag) { + int id = flag.getId(); + if (!mBooleanFlagCache.containsKey(id)) { + mBooleanFlagCache.put( + id, mSystemProperties.getBoolean(flag.getName(), flag.getDefault())); + } + + return mBooleanFlagCache.get(id); + } + @NonNull @Override public String getString(@NonNull StringFlag flag) { @@ -180,16 +195,28 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { mSecureSettings.putString(mFlagManager.idToSettingsKey(id), data); Log.i(TAG, "Set id " + id + " to " + value); removeFromCache(id); - mFlagManager.dispatchListenersAndMaybeRestart(id); + mFlagManager.dispatchListenersAndMaybeRestart(id, this::restartSystemUI); + } + + private <T> void eraseFlag(Flag<T> flag) { + if (flag instanceof SysPropFlag) { + mSystemProperties.erase(((SysPropFlag<T>) flag).getName()); + dispatchListenersAndMaybeRestart(flag.getId(), this::restartAndroid); + } else { + eraseFlag(flag.getId()); + } } /** Erase a flag's overridden value if there is one. */ - public void eraseFlag(int id) { + private void eraseFlag(int id) { eraseInternal(id); removeFromCache(id); - mFlagManager.dispatchListenersAndMaybeRestart(id); + dispatchListenersAndMaybeRestart(id, this::restartSystemUI); } + private void dispatchListenersAndMaybeRestart(int id, Consumer<Boolean> restartAction) { + mFlagManager.dispatchListenersAndMaybeRestart(id, restartAction); + } /** Works just like {@link #eraseFlag(int)} except that it doesn't restart SystemUI. */ private void eraseInternal(int id) { // We can't actually "erase" things from sysprops, but we can set them to empty! @@ -217,7 +244,11 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { System.exit(0); } - private void restartAndroid() { + private void restartAndroid(boolean requestSuppress) { + if (requestSuppress) { + Log.i(TAG, "Android Restart Suppressed"); + return; + } Log.i(TAG, "Restarting Android"); try { mBarService.restart(); @@ -273,7 +304,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { Flag<?> flag = flagMap.get(id); if (!extras.containsKey(EXTRA_VALUE)) { - eraseFlag(id); + eraseFlag(flag); return; } @@ -282,6 +313,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE); } else if (flag instanceof ResourceBooleanFlag && value instanceof Boolean) { setFlagValue(id, (Boolean) value, BooleanFlagSerializer.INSTANCE); + } else if (flag instanceof SysPropBooleanFlag && value instanceof Boolean) { + // Store SysProp flags in SystemProperties where they can read by outside parties. + mSystemProperties.setBoolean( + ((SysPropBooleanFlag) flag).getName(), (Boolean) value); } else if (flag instanceof StringFlag && value instanceof String) { setFlagValue(id, (String) value, StringFlagSerializer.INSTANCE); } else if (flag instanceof ResourceStringFlag && value instanceof String) { @@ -306,6 +341,9 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { if (f instanceof ResourceBooleanFlag) { return new BooleanFlag(f.getId(), isEnabled((ResourceBooleanFlag) f)); } + if (f instanceof SysPropBooleanFlag) { + return new BooleanFlag(f.getId(), isEnabled((SysPropBooleanFlag) f)); + } // TODO: add support for other flag types. Log.w(TAG, "Unsupported Flag Type. Please file a bug."); diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java index 348a8e20122e..1fb1acfae1cb 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java @@ -43,11 +43,17 @@ import javax.inject.Inject; @SysUISingleton public class FeatureFlagsRelease implements FeatureFlags, Dumpable { private final Resources mResources; + private final SystemPropertiesHelper mSystemProperties; SparseBooleanArray mBooleanCache = new SparseBooleanArray(); SparseArray<String> mStringCache = new SparseArray<>(); + @Inject - public FeatureFlagsRelease(@Main Resources resources, DumpManager dumpManager) { + public FeatureFlagsRelease( + @Main Resources resources, + SystemPropertiesHelper systemProperties, + DumpManager dumpManager) { mResources = resources; + mSystemProperties = systemProperties; dumpManager.registerDumpable("SysUIFlags", this); } @@ -72,6 +78,17 @@ public class FeatureFlagsRelease implements FeatureFlags, Dumpable { return mBooleanCache.valueAt(cacheIndex); } + @Override + public boolean isEnabled(SysPropBooleanFlag flag) { + int cacheIndex = mBooleanCache.indexOfKey(flag.getId()); + if (cacheIndex < 0) { + return isEnabled( + flag.getId(), mSystemProperties.getBoolean(flag.getName(), flag.getDefault())); + } + + return mBooleanCache.valueAt(cacheIndex); + } + private boolean isEnabled(int key, boolean defaultValue) { mBooleanCache.append(key, defaultValue); return defaultValue; diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt index 1dc5a9f3adc5..6c160975587b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt @@ -34,6 +34,10 @@ open class SystemPropertiesHelper @Inject constructor() { return SystemProperties.getBoolean(name, default) } + fun setBoolean(name: String, value: Boolean) { + SystemProperties.set(name, if (value) "1" else "0") + } + fun set(name: String, value: String) { SystemProperties.set(name, value) } @@ -41,4 +45,8 @@ open class SystemPropertiesHelper @Inject constructor() { fun set(name: String, value: Int) { set(name, value.toString()) } + + fun erase(name: String) { + set(name, "") + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java index 32b58c2d1bca..822b1cfdf877 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java @@ -18,8 +18,8 @@ package com.android.systemui.keyguard; import android.os.Handler; import android.os.Message; -import android.os.PowerManager; import android.os.RemoteException; +import android.os.Trace; import android.util.Log; import com.android.internal.policy.IKeyguardDrawnCallback; @@ -83,6 +83,11 @@ public class KeyguardLifecyclesDispatcher { final Object obj = msg.obj; switch (msg.what) { case SCREEN_TURNING_ON: + Trace.beginSection("KeyguardLifecyclesDispatcher#SCREEN_TURNING_ON"); + final String onDrawWaitingTraceTag = + "Waiting for KeyguardDrawnCallback#onDrawn"; + int traceCookie = System.identityHashCode(msg); + Trace.beginAsyncSection(onDrawWaitingTraceTag, traceCookie); // Ensure the drawn callback is only ever called once mScreenLifecycle.dispatchScreenTurningOn(new Runnable() { boolean mInvoked; @@ -92,6 +97,7 @@ public class KeyguardLifecyclesDispatcher { if (!mInvoked) { mInvoked = true; try { + Trace.endAsyncSection(onDrawWaitingTraceTag, traceCookie); ((IKeyguardDrawnCallback) obj).onDrawn(); } catch (RemoteException e) { Log.w(TAG, "Exception calling onDrawn():", e); @@ -101,6 +107,7 @@ public class KeyguardLifecyclesDispatcher { } } }); + Trace.endSection(); break; case SCREEN_TURNED_ON: mScreenLifecycle.dispatchScreenTurnedOn(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index f4b6fbd0324f..eee39552bcc8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -795,9 +795,6 @@ class MediaHierarchyManager @Inject constructor( @TransformationType fun calculateTransformationType(): Int { if (isTransitioningToFullShade) { - if (inSplitShade) { - return TRANSFORMATION_TYPE_TRANSITION - } return TRANSFORMATION_TYPE_FADE } if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS || @@ -964,7 +961,6 @@ class MediaHierarchyManager @Inject constructor( (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS qsExpansion > 0.4f && onLockscreen -> LOCATION_QS !hasActiveMedia -> LOCATION_QS - onLockscreen && isSplitShadeExpanding() -> LOCATION_QS onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN else -> LOCATION_QQS @@ -990,10 +986,6 @@ class MediaHierarchyManager @Inject constructor( return location } - private fun isSplitShadeExpanding(): Boolean { - return inSplitShade && isTransitioningToFullShade - } - /** * Are we currently transforming to the full shade and already in QQS */ @@ -1001,10 +993,6 @@ class MediaHierarchyManager @Inject constructor( if (!isTransitioningToFullShade) { return false } - if (inSplitShade) { - // Split shade doesn't use QQS. - return false - } return fullShadeTransitionProgress > 0.5f } @@ -1012,10 +1000,6 @@ class MediaHierarchyManager @Inject constructor( * Is the current transformationType fading */ private fun isCurrentlyFading(): Boolean { - if (isSplitShadeExpanding()) { - // Split shade always uses transition instead of fade. - return false - } if (isTransitioningToFullShade) { return true } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt index ee2fba09358b..6ec2b6e89cb0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt @@ -29,6 +29,7 @@ import androidx.annotation.VisibleForTesting import com.android.internal.widget.CachingIconView import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.DelayableExecutor /** @@ -42,6 +43,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( internal val context: Context, private val windowManager: WindowManager, @Main private val mainExecutor: DelayableExecutor, + private val tapGestureDetector: TapGestureDetector, @LayoutRes private val chipLayoutRes: Int ) { /** The window layout parameters we'll use when attaching the view to a window. */ @@ -82,6 +84,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( // Add view if necessary if (oldChipView == null) { + tapGestureDetector.addOnGestureDetectedCallback(TAG, this::removeChip) windowManager.addView(chipView, windowLayoutParams) } @@ -96,6 +99,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( // TransferTriggered state: Once the user has initiated the transfer, they should be able // to move away from the receiver device but still see the status of the transfer. if (chipView == null) { return } + tapGestureDetector.removeOnGestureDetectedCallback(TAG) windowManager.removeView(chipView) chipView = null } @@ -128,5 +132,6 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and // UpdateMediaTapToTransferReceiverDisplayTest private const val WINDOW_TITLE = "Media Transfer Chip View" +private val TAG = MediaTttChipControllerCommon::class.simpleName!! @VisibleForTesting const val TIMEOUT_MILLIS = 3000L diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 214a8888e0b5..b6f1aea9a384 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -29,6 +29,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.DelayableExecutor import javax.inject.Inject @@ -43,9 +44,10 @@ class MediaTttChipControllerReceiver @Inject constructor( context: Context, windowManager: WindowManager, mainExecutor: DelayableExecutor, + tapGestureDetector: TapGestureDetector, @Main private val mainHandler: Handler, ) : MediaTttChipControllerCommon<ChipStateReceiver>( - context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver + context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip_receiver ) { private val commandQueueCallbacks = object : CommandQueue.Callbacks { override fun updateMediaTapToTransferReceiverDisplay( diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt index 482e604a0635..fef17fdcbbdc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt @@ -30,6 +30,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.DelayableExecutor import javax.inject.Inject @@ -42,9 +43,10 @@ class MediaTttChipControllerSender @Inject constructor( commandQueue: CommandQueue, context: Context, windowManager: WindowManager, - @Main private val mainExecutor: DelayableExecutor, + @Main mainExecutor: DelayableExecutor, + tapGestureDetector: TapGestureDetector, ) : MediaTttChipControllerCommon<ChipStateSender>( - context, windowManager, mainExecutor, R.layout.media_ttt_chip + context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip ) { private val commandQueueCallbacks = object : CommandQueue.Callbacks { override fun updateMediaTapToTransferSenderDisplay( diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 8b39e5c24ded..a242df38b62f 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -440,7 +440,9 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHomeButtonLongPressDurationMs = Optional.of( properties.getLong(HOME_BUTTON_LONG_PRESS_DURATION_MS, 0) ).filter(duration -> duration != 0); - reconfigureHomeLongClick(); + if (mNavigationBarView != null) { + reconfigureHomeLongClick(); + } } } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 3ef72202a591..3c7933f0d218 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -513,8 +513,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mContainer.setExpansion(expansion); final float translationScaleY = (mInSplitShade ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); - boolean onKeyguard = isKeyguardState(); - boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard; + boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard; if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( onKeyguardAndExpanded @@ -548,7 +547,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mHeader.updateResources(); } } - mQSPanelController.setIsOnKeyguard(onKeyguard); mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index b04d75273831..5126fcb4c34d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -110,8 +110,6 @@ public class QSPanel extends LinearLayout implements Tunable { private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>(); private final Rect mClippingRect = new Rect(); private boolean mUseNewFooter = false; - private ViewGroup mMediaHostView; - private boolean mShouldMoveMediaOnExpansion = true; public QSPanel(Context context, AttributeSet attrs) { super(context, attrs); @@ -291,15 +289,9 @@ public class QSPanel extends LinearLayout implements Tunable { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (move) { - int topOffset; - if (child == mMediaHostView && !mShouldMoveMediaOnExpansion) { - topOffset = 0; - } else { - topOffset = tileHeightOffset; - } int top = Objects.requireNonNull(mChildrenLayoutTop.get(child)); - child.setLeftTopRightBottom(child.getLeft(), top + topOffset, - child.getRight(), top + topOffset + child.getHeight()); + child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset, + child.getRight(), top + tileHeightOffset + child.getHeight()); } if (child == mTileLayout) { move = true; @@ -471,7 +463,6 @@ public class QSPanel extends LinearLayout implements Tunable { if (!mUsingMediaPlayer) { return; } - mMediaHostView = hostView; ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this; ViewGroup currentParent = (ViewGroup) hostView.getParent(); if (currentParent != newParent) { @@ -665,19 +656,6 @@ public class QSPanel extends LinearLayout implements Tunable { updatePadding(); } - /** - * Sets whether the media container should move during the expansion of the QS Panel. - * - * As the QS Panel expands and the QS unsquish, the views below the QS tiles move to adapt to - * the new height of the QS tiles. - * - * In some cases this might not be wanted for media. One example is when there is a transition - * animation of the media container happening on split shade lock screen. - */ - public void setShouldMoveMediaOnExpansion(boolean shouldMoveMediaOnExpansion) { - mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion; - } - private class H extends Handler { private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java index 6572daa91269..3172aa9592dd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -419,16 +419,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr return mView.getBrightnessView(); } - /** Sets whether we are currently on lock screen. */ - public void setIsOnKeyguard(boolean isOnKeyguard) { - boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard; - // When the split shade is expanding on lockscreen, the media container transitions from the - // lockscreen to QS. - // We have to prevent the media container position from moving during the transition to have - // a smooth translation animation without stuttering. - mView.setShouldMoveMediaOnExpansion(!isOnSplitShadeLockscreen); - } - /** */ public static final class TileRecord { public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 131589f33fee..999818ff943f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -116,7 +116,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy private String mTileSpec; @Nullable - private EnforcedAdmin mEnforcedAdmin; + @VisibleForTesting + protected EnforcedAdmin mEnforcedAdmin; private boolean mShowingDetail; private int mIsFullQs; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index c61c18a2a029..f736231bc22b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; +import android.os.UserManager; import android.provider.Settings; import android.service.quicksettings.Tile; import android.text.TextUtils; @@ -114,6 +115,7 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { @Override protected void handleUpdateState(BooleanState state, Object arg) { + checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_BLUETOOTH); final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING; final boolean enabled = transientEnabling || mController.isBluetoothEnabled(); final boolean connected = mController.isBluetoothConnected(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 8a9d6ddb2e14..8366bddaab9f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -342,7 +342,9 @@ class LockscreenShadeTransitionController @Inject constructor( qS.setTransitionToFullShadeAmount(field, qSDragProgress) notificationPanelController.setTransitionToFullShadeAmount(field, false /* animate */, 0 /* delay */) - mediaHierarchyManager.setTransitionToFullShadeAmount(field) + // TODO: appear media also in split shade + val mediaAmount = if (useSplitShade) 0f else field + mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount) transitionToShadeAmountCommon(field) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt index 48717e29d06a..5df593b64c24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/WiredChargingRippleController.kt @@ -175,7 +175,7 @@ class WiredChargingRippleController @Inject constructor( val width = displayMetrics.widthPixels val height = displayMetrics.heightPixels rippleView.radius = Integer.max(width, height).toFloat() - rippleView.origin = when (RotationUtils.getRotation(context)) { + rippleView.origin = when (RotationUtils.getExactRotation(context)) { RotationUtils.ROTATION_LANDSCAPE -> { PointF(width * normalizedPortPosY, height * (1 - normalizedPortPosX)) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt new file mode 100644 index 000000000000..76766b0d0ef0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.systemui.statusbar.gesture + +import android.annotation.CallSuper +import android.os.Looper +import android.view.Choreographer +import android.view.Display +import android.view.InputEvent +import com.android.systemui.shared.system.InputChannelCompat +import com.android.systemui.shared.system.InputMonitorCompat + +/** + * An abstract class to help detect gestures that occur anywhere on the display (not specific to a + * certain view). + * + * This class handles starting/stopping the gesture detection system as well as + * registering/unregistering callbacks for when gestures occur. Note that the class will only listen + * for gestures when there's at least one callback registered. + * + * Subclasses should implement [onInputEvent] to detect their specific gesture. Once a specific + * gesture is detected, they should call [onGestureDetected] (which will notify the callbacks). + */ +abstract class GenericGestureDetector( + private val tag: String +) { + /** + * Active callbacks, each associated with a tag. Gestures will only be monitored if + * [callbacks.size] > 0. + */ + private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf() + + private var inputMonitor: InputMonitorCompat? = null + private var inputReceiver: InputChannelCompat.InputEventReceiver? = null + + /** Adds a callback that will be triggered when the tap gesture is detected. */ + fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) { + val callbacksWasEmpty = callbacks.isEmpty() + callbacks[tag] = callback + if (callbacksWasEmpty) { + startGestureListening() + } + } + + /** Removes the callback. */ + fun removeOnGestureDetectedCallback(tag: String) { + callbacks.remove(tag) + if (callbacks.isEmpty()) { + stopGestureListening() + } + } + + /** Triggered each time a touch event occurs (and at least one callback is registered). */ + abstract fun onInputEvent(ev: InputEvent) + + /** Should be called by subclasses when their specific gesture is detected. */ + internal fun onGestureDetected() { + callbacks.values.forEach { it.invoke() } + } + + /** Start listening to touch events. */ + @CallSuper + internal open fun startGestureListening() { + stopGestureListening() + + inputMonitor = InputMonitorCompat(tag, Display.DEFAULT_DISPLAY).also { + inputReceiver = it.getInputReceiver( + Looper.getMainLooper(), + Choreographer.getInstance(), + this::onInputEvent + ) + } + } + + /** Stop listening to touch events. */ + @CallSuper + internal open fun stopGestureListening() { + inputMonitor?.let { + inputMonitor = null + it.dispose() + } + inputReceiver?.let { + inputReceiver = null + it.dispose() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt index 7cdf69d1ecbf..fcb285a50ae7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt @@ -17,15 +17,13 @@ package com.android.systemui.statusbar.gesture import android.content.Context -import android.os.Looper -import android.view.Choreographer -import android.view.Display import android.view.InputEvent import android.view.MotionEvent -import android.view.MotionEvent.* +import android.view.MotionEvent.ACTION_CANCEL +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_UP import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.shared.system.InputChannelCompat -import com.android.systemui.shared.system.InputMonitorCompat import com.android.systemui.statusbar.window.StatusBarWindowController import javax.inject.Inject @@ -38,43 +36,17 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor( context: Context, private val statusBarWindowController: StatusBarWindowController, private val logger: SwipeStatusBarAwayGestureLogger -) { - - /** - * Active callbacks, each associated with a tag. Gestures will only be monitored if - * [callbacks.size] > 0. - */ - private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf() +) : GenericGestureDetector(SwipeStatusBarAwayGestureHandler::class.simpleName!!) { private var startY: Float = 0f private var startTime: Long = 0L private var monitoringCurrentTouch: Boolean = false - private var inputMonitor: InputMonitorCompat? = null - private var inputReceiver: InputChannelCompat.InputEventReceiver? = null - private var swipeDistanceThreshold: Int = context.resources.getDimensionPixelSize( com.android.internal.R.dimen.system_gestures_start_threshold ) - /** Adds a callback that will be triggered when the swipe away gesture is detected. */ - fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) { - val callbacksWasEmpty = callbacks.isEmpty() - callbacks[tag] = callback - if (callbacksWasEmpty) { - startGestureListening() - } - } - - /** Removes the callback. */ - fun removeOnGestureDetectedCallback(tag: String) { - callbacks.remove(tag) - if (callbacks.isEmpty()) { - stopGestureListening() - } - } - - private fun onInputEvent(ev: InputEvent) { + override fun onInputEvent(ev: InputEvent) { if (ev !is MotionEvent) { return } @@ -108,7 +80,7 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor( ) { monitoringCurrentTouch = false logger.logGestureDetected(ev.y.toInt()) - callbacks.values.forEach { it.invoke() } + onGestureDetected() } } ACTION_CANCEL, ACTION_UP -> { @@ -120,33 +92,15 @@ open class SwipeStatusBarAwayGestureHandler @Inject constructor( } } - /** Start listening for the swipe gesture. */ - private fun startGestureListening() { - stopGestureListening() - + override fun startGestureListening() { + super.startGestureListening() logger.logInputListeningStarted() - inputMonitor = InputMonitorCompat(TAG, Display.DEFAULT_DISPLAY).also { - inputReceiver = it.getInputReceiver( - Looper.getMainLooper(), - Choreographer.getInstance(), - this::onInputEvent - ) - } } - /** Stop listening for the swipe gesture. */ - private fun stopGestureListening() { - inputMonitor?.let { - logger.logInputListeningStopped() - inputMonitor = null - it.dispose() - } - inputReceiver?.let { - inputReceiver = null - it.dispose() - } + override fun stopGestureListening() { + super.stopGestureListening() + logger.logInputListeningStopped() } } private const val SWIPE_TIMEOUT_MS: Long = 500 -private val TAG = SwipeStatusBarAwayGestureHandler::class.simpleName diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt new file mode 100644 index 000000000000..4107ce2593f1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.gesture + +import android.content.Context +import android.view.GestureDetector +import android.view.InputEvent +import android.view.MotionEvent +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject + +/** + * A class to detect when a user taps the screen. To be notified when the tap is detected, add a + * callback via [addOnGestureDetectedCallback]. + */ +@SysUISingleton +class TapGestureDetector @Inject constructor( + private val context: Context +) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) { + + private val gestureListener = object : GestureDetector.SimpleOnGestureListener() { + override fun onSingleTapUp(e: MotionEvent?): Boolean { + onGestureDetected() + return true + } + } + + private var gestureDetector: GestureDetector? = null + + override fun onInputEvent(ev: InputEvent) { + if (ev !is MotionEvent) { + return + } + // Pass all events to [gestureDetector], which will then notify [gestureListener] when a tap + // is detected. + gestureDetector!!.onTouchEvent(ev) + } + + /** Start listening for the tap gesture. */ + override fun startGestureListening() { + super.startGestureListening() + gestureDetector = GestureDetector(context, gestureListener) + } + + /** Stop listening for the swipe gesture. */ + override fun stopGestureListening() { + super.stopGestureListening() + gestureDetector = null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 244103cf419e..ccb37aeb18a9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -68,6 +68,7 @@ public class AutoTileManager implements UserAwareController { private UserHandle mCurrentUser; private boolean mInitialized; + private final String mSafetySpec; protected final Context mContext; protected final QSTileHost mHost; @@ -113,6 +114,13 @@ public class AutoTileManager implements UserAwareController { mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable; mDeviceControlsController = deviceControlsController; mWalletController = walletController; + String safetySpecRes; + try { + safetySpecRes = context.getResources().getString(R.string.safety_quick_settings_tile); + } catch (Resources.NotFoundException | NullPointerException e) { + safetySpecRes = null; + } + mSafetySpec = safetySpecRes; } /** @@ -155,6 +163,9 @@ public class AutoTileManager implements UserAwareController { if (!mAutoTracker.isAdded(WALLET)) { initWalletController(); } + if (mSafetySpec != null && !mAutoTracker.isAdded(mSafetySpec)) { + initSafetyTile(); + } int settingsN = mAutoAddSettingList.size(); for (int i = 0; i < settingsN; i++) { @@ -315,6 +326,15 @@ public class AutoTileManager implements UserAwareController { } } + private void initSafetyTile() { + if (mSafetySpec == null) { + return; + } + if (mAutoTracker.isAdded(mSafetySpec)) return; + mHost.addTile(CustomTile.getComponentFromSpec(mSafetySpec), true); + mAutoTracker.setTileAdded(mSafetySpec); + } + @VisibleForTesting final NightDisplayListener.Callback mNightDisplayCallback = new NightDisplayListener.Callback() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index b141110a2ec2..8b25c2bc20b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -16,9 +16,14 @@ package com.android.systemui.statusbar.phone; +import android.content.ContentResolver; +import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.database.ContentObserver; import android.hardware.display.AmbientDisplayConfiguration; +import android.net.Uri; +import android.os.Handler; import android.os.PowerManager; import android.os.SystemProperties; import android.os.UserHandle; @@ -34,6 +39,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; @@ -86,6 +92,7 @@ public class DozeParameters implements private boolean mDozeAlwaysOn; private boolean mControlScreenOffAnimation; + private boolean mIsQuickPickupEnabled; private boolean mKeyguardShowing; @VisibleForTesting @@ -101,10 +108,17 @@ public class DozeParameters implements public void onShadeExpandedChanged(boolean expanded) { updateControlScreenOff(); } + + @Override + public void onUserSwitchComplete(int newUserId) { + updateQuickPickupEnabled(); + } }; @Inject protected DozeParameters( + Context context, + @Background Handler handler, @Main Resources resources, AmbientDisplayConfiguration ambientDisplayConfiguration, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, @@ -146,6 +160,14 @@ public class DozeParameters implements if (mFoldAodAnimationController != null) { mFoldAodAnimationController.addCallback(this); } + + SettingsObserver quickPickupSettingsObserver = new SettingsObserver(context, handler); + quickPickupSettingsObserver.observe(); + } + + private void updateQuickPickupEnabled() { + mIsQuickPickupEnabled = + mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT); } public boolean getDisplayStateSupported() { @@ -239,8 +261,11 @@ public class DozeParameters implements return mDozeAlwaysOn && !mBatteryController.isAodPowerSave(); } + /** + * Whether the quick pickup gesture is supported and enabled for the device. + */ public boolean isQuickPickupEnabled() { - return mAmbientDisplayConfiguration.quickPickupSensorEnabled(UserHandle.USER_CURRENT); + return mIsQuickPickupEnabled; } /** @@ -436,6 +461,7 @@ public class DozeParameters implements pw.print("getPickupVibrationThreshold(): "); pw.println(getPickupVibrationThreshold()); pw.print("getSelectivelyRegisterSensorsUsingProx(): "); pw.println(getSelectivelyRegisterSensorsUsingProx()); + pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled()); } private boolean getPostureSpecificBool( @@ -458,4 +484,44 @@ public class DozeParameters implements */ void onAlwaysOnChange(); } + + private final class SettingsObserver extends ContentObserver { + private final Uri mQuickPickupGesture = + Settings.Secure.getUriFor(Settings.Secure.DOZE_QUICK_PICKUP_GESTURE); + private final Uri mPickupGesture = + Settings.Secure.getUriFor(Settings.Secure.DOZE_PICK_UP_GESTURE); + private final Uri mAlwaysOnEnabled = + Settings.Secure.getUriFor(Settings.Secure.DOZE_ALWAYS_ON); + private final Context mContext; + + SettingsObserver(Context context, Handler handler) { + super(handler); + mContext = context; + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(mQuickPickupGesture, false, this, + UserHandle.USER_ALL); + resolver.registerContentObserver(mPickupGesture, false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(mAlwaysOnEnabled, false, this, UserHandle.USER_ALL); + update(null); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + update(uri); + } + + public void update(Uri uri) { + if (uri == null + || mQuickPickupGesture.equals(uri) + || mPickupGesture.equals(uri) + || mAlwaysOnEnabled.equals(uri)) { + // the quick pickup gesture is dependent on alwaysOn being disabled and + // the pickup gesture being enabled + updateQuickPickupEnabled(); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index 72237b1ca6c6..53ef97dc317c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; -import com.android.wm.shell.bubbles.Bubbles; import java.util.ArrayList; import java.util.Optional; @@ -50,7 +49,6 @@ public class ShadeControllerImpl implements ShadeController { private final int mDisplayId; protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy; private final Lazy<AssistManager> mAssistManagerLazy; - private final Optional<Bubbles> mBubblesOptional; private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>(); @@ -62,8 +60,7 @@ public class ShadeControllerImpl implements ShadeController { StatusBarKeyguardViewManager statusBarKeyguardViewManager, WindowManager windowManager, Lazy<Optional<StatusBar>> statusBarOptionalLazy, - Lazy<AssistManager> assistManagerLazy, - Optional<Bubbles> bubblesOptional + Lazy<AssistManager> assistManagerLazy ) { mCommandQueue = commandQueue; mStatusBarStateController = statusBarStateController; @@ -73,7 +70,6 @@ public class ShadeControllerImpl implements ShadeController { // TODO: Remove circular reference to StatusBar when possible. mStatusBarOptionalLazy = statusBarOptionalLazy; mAssistManagerLazy = assistManagerLazy; - mBubblesOptional = bubblesOptional; } @Override @@ -131,8 +127,6 @@ public class ShadeControllerImpl implements ShadeController { getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper(); getNotificationPanelViewController() .collapsePanel(true /* animate */, delayed, speedUpFactor); - } else if (mBubblesOptional.isPresent()) { - mBubblesOptional.get().collapseStack(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 82e0e6726437..cffdc290c041 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -270,6 +270,7 @@ public class StatusBar extends CoreStartable implements protected static final int MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU = 1027; // Should match the values in PhoneWindowManager + public static final String SYSTEM_DIALOG_REASON_KEY = "reason"; public static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; public static final String SYSTEM_DIALOG_REASON_DREAM = "dream"; static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot"; @@ -2661,15 +2662,12 @@ public class StatusBar extends CoreStartable implements Trace.beginSection("StatusBar#onReceive"); if (DEBUG) Log.v(TAG, "onReceive: " + intent); String action = intent.getAction(); + String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { KeyboardShortcuts.dismiss(); mRemoteInputManager.closeRemoteInputs(); - if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) { - mBubblesOptional.get().collapseStack(); - } if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) { int flags = CommandQueue.FLAG_EXCLUDE_NONE; - String reason = intent.getStringExtra("reason"); if (reason != null) { if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) { flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; @@ -2684,19 +2682,13 @@ public class StatusBar extends CoreStartable implements } mShadeController.animateCollapsePanels(flags); } - } - else if (Intent.ACTION_SCREEN_OFF.equals(action)) { + } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { if (mNotificationShadeWindowController != null) { mNotificationShadeWindowController.setNotTouchable(false); } - if (mBubblesOptional.isPresent() && mBubblesOptional.get().isStackExpanded()) { - // Post to main thread, since updating the UI. - mMainExecutor.execute(() -> mBubblesOptional.get().collapseStack()); - } finishBarAnimations(); resetUserExpandedStates(); - } - else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) { + } else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) { mQSPanelController.showDeviceMonitoringDialog(); } Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 11d9c311fb6c..a96ba56be58d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -1077,7 +1077,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb && mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING; boolean keyguardShowing = mShowing && !mOccluded; boolean hideWhileDozing = mDozing && !isWakeAndUnlockPulsing; - boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked) + boolean keyguardWithGestureNav = (keyguardShowing && !mDozing && !mScreenOffAnimationPlaying + || mPulsing && !mIsDocked) && mGesturalNav; return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav @@ -1091,7 +1092,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean keyguardShowing = mLastShowing && !mLastOccluded; boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING; boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing - || mLastPulsing && !mLastIsDocked) && mLastGesturalNav; + && !mLastScreenOffAnimationPlaying || mLastPulsing && !mLastIsDocked) + && mLastGesturalNav; return (!keyguardShowing && !hideWhileDozing && !mLastScreenOffAnimationPlaying || mLastBouncerShowing || mLastRemoteInputActive || keyguardWithGestureNav || mLastGlobalActionsVisible); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index c7f7258513d0..6e7231ef5ca3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -242,8 +242,14 @@ class OngoingCallController @Inject constructor( * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call. */ private fun setUpUidObserver(currentCallNotificationInfo: CallNotificationInfo) { - isCallAppVisible = isProcessVisibleToUser( - iActivityManager.getUidProcessState(currentCallNotificationInfo.uid, null)) + try { + isCallAppVisible = isProcessVisibleToUser( + iActivityManager.getUidProcessState(currentCallNotificationInfo.uid, null) + ) + } catch (se: SecurityException) { + Log.e(TAG, "Security exception when trying to get process state: $se") + return + } if (uidObserver != null) { iActivityManager.unregisterUidObserver(uidObserver) @@ -275,12 +281,17 @@ class OngoingCallController @Inject constructor( override fun onUidCachedChanged(uid: Int, cached: Boolean) {} } - iActivityManager.registerUidObserver( + try { + iActivityManager.registerUidObserver( uidObserver, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_UNKNOWN, null - ) + ) + } catch (se: SecurityException) { + Log.e(TAG, "Security exception when trying to register uid observer: $se") + return + } } /** Returns true if the given [procState] represents a process that's visible to the user. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index f4e53e22b02c..d1c9b3f1c2c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -76,14 +76,11 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.QSUserSwitcherEvent; import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower; import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; import com.android.systemui.util.settings.SecureSettings; -import dagger.Lazy; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -129,7 +126,6 @@ public class UserSwitcherController implements Dumpable { private final InteractionJankMonitor mInteractionJankMonitor; private final LatencyTracker mLatencyTracker; private final DialogLaunchAnimator mDialogLaunchAnimator; - private final Lazy<ShadeController> mShadeController; private ArrayList<UserRecord> mUsers = new ArrayList<>(); @VisibleForTesting @@ -178,7 +174,6 @@ public class UserSwitcherController implements Dumpable { InteractionJankMonitor interactionJankMonitor, LatencyTracker latencyTracker, DumpManager dumpManager, - Lazy<ShadeController> shadeController, DialogLaunchAnimator dialogLaunchAnimator) { mContext = context; mActivityManager = activityManager; @@ -207,7 +202,6 @@ public class UserSwitcherController implements Dumpable { mActivityStarter = activityStarter; mUserManager = userManager; mDialogLaunchAnimator = dialogLaunchAnimator; - mShadeController = shadeController; IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_ADDED); @@ -1206,8 +1200,12 @@ public class UserSwitcherController implements Dumpable { if (ActivityManager.isUserAMonkey()) { return; } - mShadeController.get().collapsePanel(); - getContext().startActivity(CreateUserActivity.createIntentForStart(getContext())); + // Use broadcast instead of ShadeController, as this dialog may have started in + // another process and normal dagger bindings are not available + getContext().sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + getContext().startActivityAsUser( + CreateUserActivity.createIntentForStart(getContext()), + mUserTracker.getUserHandle()); } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java index 1862ed3a4de8..ae23ca64e31e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java +++ b/packages/SystemUI/src/com/android/systemui/volume/CaptionsToggleImageButton.java @@ -28,14 +28,11 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit import com.android.keyguard.AlphaOptimizedImageButton; import com.android.systemui.R; -/** Toggle button in Volume Dialog that allows extra state for when streams are opted-out */ +/** Toggle button in Volume Dialog for controlling system captions state */ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { - private static final int[] OPTED_OUT_STATE = new int[] { R.attr.optedOut }; - private ConfirmedTapListener mConfirmedTapListener; private boolean mCaptionsEnabled = false; - private boolean mOptedOut = false; private GestureDetector mGestureDetector; private GestureDetector.SimpleOnGestureListener mGestureListener = @@ -60,11 +57,7 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { @Override public int[] onCreateDrawableState(int extraSpace) { - int[] state = super.onCreateDrawableState(extraSpace + 1); - if (mOptedOut) { - mergeDrawableStates(state, OPTED_OUT_STATE); - } - return state; + return super.onCreateDrawableState(extraSpace + 1); } Runnable setCaptionsEnabled(boolean areCaptionsEnabled) { @@ -95,16 +88,6 @@ public class CaptionsToggleImageButton extends AlphaOptimizedImageButton { return this.mCaptionsEnabled; } - /** Sets whether or not the current stream has opted out of captions */ - void setOptedOut(boolean isOptedOut) { - this.mOptedOut = isOptedOut; - refreshDrawableState(); - } - - boolean getOptedOut() { - return this.mOptedOut; - } - void setOnConfirmedTapListener(ConfirmedTapListener listener, Handler handler) { mConfirmedTapListener = listener; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 57c7f11b752d..97e03a68205c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -54,6 +54,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.CaptioningManager; import androidx.lifecycle.Observer; @@ -130,6 +131,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private final Receiver mReceiver = new Receiver(); private final RingerModeObservers mRingerModeObservers; private final MediaSessions mMediaSessions; + private final CaptioningManager mCaptioningManager; protected C mCallbacks = new C(); private final State mState = new State(); protected final MediaSessionsCallbacks mMediaSessionsCallbacksW; @@ -175,7 +177,8 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa IAudioService iAudioService, AccessibilityManager accessibilityManager, PackageManager packageManager, - WakefulnessLifecycle wakefulnessLifecycle) { + WakefulnessLifecycle wakefulnessLifecycle, + CaptioningManager captioningManager) { mContext = context.getApplicationContext(); mPackageManager = packageManager; mWakefulnessLifecycle = wakefulnessLifecycle; @@ -200,6 +203,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa mVibrator = vibrator; mHasVibrator = mVibrator.hasVibrator(); mAudioService = iAudioService; + mCaptioningManager = captioningManager; boolean accessibilityVolumeStreamActive = accessibilityManager .isAccessibilityVolumeStreamActive(); @@ -307,20 +311,11 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public boolean areCaptionsEnabled() { - int currentValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.ODI_CAPTIONS_ENABLED, 0, UserHandle.USER_CURRENT); - return currentValue == 1; + return mCaptioningManager.isSystemAudioCaptioningEnabled(); } public void setCaptionsEnabled(boolean isEnabled) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0, UserHandle.USER_CURRENT); - } - - @Override - public boolean isCaptionStreamOptedOut() { - // TODO(b/129768185): Removing secure setting, to be replaced by sound event listener - return false; + mCaptioningManager.setSystemAudioCaptioningEnabled(isEnabled); } public void getCaptionsComponentState(boolean fromTooltip) { @@ -423,6 +418,13 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } private void onGetCaptionsComponentStateW(boolean fromTooltip) { + if (mCaptioningManager.isSystemAudioCaptioningUiEnabled()) { + mCallbacks.onCaptionComponentStateChanged(true, fromTooltip); + return; + } + + // TODO(b/220968335): Remove this check once system captions component migrates + // to new CaptioningManager APIs. try { String componentNameString = mContext.getString( com.android.internal.R.string.config_defaultSystemCaptionsService); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 58f74a0d2a02..bfdcbd6deb48 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -1180,11 +1180,6 @@ public class VolumeDialogImpl implements VolumeDialog, if (mODICaptionsIcon.getCaptionsEnabled() != captionsEnabled) { mHandler.post(mODICaptionsIcon.setCaptionsEnabled(captionsEnabled)); } - - boolean isOptedOut = mController.isCaptionStreamOptedOut(); - if (mODICaptionsIcon.getOptedOut() != isOptedOut) { - mHandler.post(() -> mODICaptionsIcon.setOptedOut(isOptedOut)); - } } private void onCaptionIconClicked() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt new file mode 100644 index 000000000000..95aa08dff379 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui + +import android.graphics.Insets +import android.graphics.PixelFormat +import android.graphics.Rect +import android.graphics.RectF +import android.hardware.graphics.common.DisplayDecorationSupport +import android.testing.AndroidTestingRunner +import android.view.Display +import android.view.DisplayCutout +import android.view.DisplayInfo +import android.view.View +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.systemui.util.mockito.eq +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ScreenDecorHwcLayerTest : SysuiTestCase() { + + @Mock private lateinit var mockDisplay: Display + @Mock private lateinit var mockRootView: View + + private val displayWidth = 100 + private val displayHeight = 200 + private val cutoutSize = 10 + private val roundedSizeTop = 15 + private val roundedSizeBottom = 20 + + private lateinit var decorHwcLayer: ScreenDecorHwcLayer + private val cutoutTop: DisplayCutout = DisplayCutout.Builder() + .setSafeInsets(Insets.of(0, cutoutSize, 0, 0)) + .setBoundingRectTop(Rect(1, 0, 2, cutoutSize)) + .build() + + private val cutoutRight: DisplayCutout = DisplayCutout.Builder() + .setSafeInsets(Insets.of(0, 0, cutoutSize, 0)) + .setBoundingRectRight(Rect(displayWidth - cutoutSize, 50, displayWidth, 52)) + .build() + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mContext.orCreateTestableResources.addOverride( + R.array.config_displayUniqueIdArray, arrayOf<String>()) + mContext.orCreateTestableResources.addOverride( + R.bool.config_fillMainBuiltInDisplayCutout, true) + + val decorationSupport = DisplayDecorationSupport() + decorationSupport.format = PixelFormat.R_8 + decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport)) + whenever(decorHwcLayer.width).thenReturn(displayWidth) + whenever(decorHwcLayer.height).thenReturn(displayHeight) + whenever(decorHwcLayer.display).thenReturn(mockDisplay) + whenever(decorHwcLayer.rootView).thenReturn(mockRootView) + whenever(mockRootView.left).thenReturn(0) + whenever(mockRootView.top).thenReturn(0) + whenever(mockRootView.right).thenReturn(displayWidth) + whenever(mockRootView.bottom).thenReturn(displayHeight) + } + + @Test + fun testTransparentRegion_noCutout_noRoundedCorner_noProtection() { + setupConfigs(null, 0, 0, RectF(), 0f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, 0, decorHwcLayer.width, decorHwcLayer.height)) + } + + @Test + fun testTransparentRegion_onlyShortEdgeCutout() { + setupConfigs(cutoutTop, 0, 0, RectF(), 0f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, cutoutSize, decorHwcLayer.width, decorHwcLayer.height)) + } + + @Test + fun testTransparentRegion_onlyLongEdgeCutout() { + setupConfigs(cutoutRight, 0, 0, RectF(), 0f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, 0, decorHwcLayer.width - cutoutSize, decorHwcLayer.height)) + } + + @Test + fun testTransparentRegion_onlyRoundedCorners() { + setupConfigs(null, roundedSizeTop, roundedSizeBottom, RectF(), 0f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, roundedSizeTop, decorHwcLayer.width, + decorHwcLayer.height - roundedSizeBottom)) + } + + @Test + fun testTransparentRegion_onlyCutoutProtection() { + setupConfigs(null, 0, 0, RectF(48f, 1f, 52f, 5f), 0.5f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, 4, decorHwcLayer.width, decorHwcLayer.height)) + + decorHwcLayer.cameraProtectionProgress = 1f + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, 5, decorHwcLayer.width, decorHwcLayer.height)) + } + + @Test + fun testTransparentRegion_hasShortEdgeCutout_hasRoundedCorner_hasCutoutProtection() { + setupConfigs(cutoutTop, roundedSizeTop, roundedSizeBottom, RectF(48f, 1f, 52f, 5f), 1f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(0, 15, decorHwcLayer.width, decorHwcLayer.height - 20)) + } + + @Test + fun testTransparentRegion_hasLongEdgeCutout_hasRoundedCorner_hasCutoutProtection() { + setupConfigs(cutoutRight, roundedSizeTop, roundedSizeBottom, RectF(48f, 1f, 52f, 5f), 1f) + + decorHwcLayer.calculateTransparentRect() + + assertThat(decorHwcLayer.transparentRect) + .isEqualTo(Rect(20, 5, decorHwcLayer.width - 20, decorHwcLayer.height)) + } + + private fun setupConfigs( + cutout: DisplayCutout?, + roundedTop: Int, + roundedBottom: Int, + protectionRect: RectF, + protectionProgress: Float + ) { + whenever(mockDisplay.getDisplayInfo(eq(decorHwcLayer.displayInfo)) + ).then { + val info = it.getArgument<DisplayInfo>(0) + info.displayCutout = cutout + return@then true + } + decorHwcLayer.updateRoundedCornerSize(roundedTop, roundedBottom) + decorHwcLayer.protectionRect.set(protectionRect) + decorHwcLayer.cameraProtectionProgress = protectionProgress + decorHwcLayer.updateCutout() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java deleted file mode 100644 index ecfb9ee4950e..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.chooser; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Activity; -import android.app.ActivityTaskManager; -import android.content.Intent; -import android.os.Binder; -import android.test.suitebuilder.annotation.SmallTest; - -import androidx.test.runner.AndroidJUnit4; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ChooserHelperTest extends SysuiTestCase { - - @Test - public void testOnChoose_CallsStartActivityAsCallerWithToken() { - final Intent intent = new Intent(); - final Binder token = new Binder(); - intent.putExtra(ActivityTaskManager.EXTRA_PERMISSION_TOKEN, token); - - final Activity mockActivity = mock(Activity.class); - when(mockActivity.getIntent()).thenReturn(intent); - - ChooserHelper.onChoose(mockActivity); - verify(mockActivity, times(1)).startActivityAsCaller( - any(), any(), eq(token), anyBoolean(), anyInt()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 15aaf5fa8e42..21768edd40e9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -38,6 +38,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.complication.DreamPreviewComplication; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; import com.android.systemui.util.concurrency.FakeExecutor; @@ -95,6 +96,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock DreamOverlayStateController mStateController; + @Mock + DreamPreviewComplication mPreviewComplication; + DreamOverlayService mService; @Before @@ -119,7 +123,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mService = new DreamOverlayService(mContext, mMainExecutor, mDreamOverlayComponentFactory, mStateController, - mKeyguardUpdateMonitor); + mKeyguardUpdateMonitor, + mPreviewComplication); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 515a1ac814a9..49da4bd5a825 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -61,7 +61,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { } @Test - public void testStateChange() { + public void testStateChange_overlayActive() { final DreamOverlayStateController stateController = new DreamOverlayStateController( mExecutor); stateController.addCallback(mCallback); @@ -83,6 +83,38 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { } @Test + public void testStateChange_isPreviewMode() { + final DreamOverlayStateController stateController = new DreamOverlayStateController( + mExecutor); + stateController.addCallback(mCallback); + stateController.setPreviewMode(true); + mExecutor.runAllReady(); + + verify(mCallback).onStateChanged(); + assertThat(stateController.isPreviewMode()).isTrue(); + + Mockito.clearInvocations(mCallback); + stateController.setPreviewMode(true); + mExecutor.runAllReady(); + verify(mCallback, never()).onStateChanged(); + } + + @Test + public void testPreviewModeFalseByDefault() { + final DreamOverlayStateController stateController = new DreamOverlayStateController( + mExecutor); + assertThat(stateController.isPreviewMode()).isFalse(); + } + + @Test + public void testPreviewModeSetToTrue() { + final DreamOverlayStateController stateController = new DreamOverlayStateController( + mExecutor); + stateController.setPreviewMode(true); + assertThat(stateController.isPreviewMode()).isTrue(); + } + + @Test public void testCallback() { final DreamOverlayStateController stateController = new DreamOverlayStateController( mExecutor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt index 4cc5673eaf97..23a5b2b2c5b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources -import androidx.test.filters.SmallTest +import android.test.suitebuilder.annotation.SmallTest import com.android.internal.statusbar.IStatusBarService import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager @@ -35,6 +35,8 @@ import org.junit.Assert import org.junit.Before import org.junit.Test import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyString import org.mockito.Mockito.inOrder import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -57,6 +59,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { @Mock private lateinit var mFlagManager: FlagManager @Mock private lateinit var mMockContext: Context @Mock private lateinit var mSecureSettings: SecureSettings + @Mock private lateinit var mSystemProperties: SystemPropertiesHelper @Mock private lateinit var mResources: Resources @Mock private lateinit var mDumpManager: DumpManager @Mock private lateinit var mBarService: IStatusBarService @@ -71,12 +74,13 @@ class FeatureFlagsDebugTest : SysuiTestCase() { mFlagManager, mMockContext, mSecureSettings, + mSystemProperties, mResources, mDumpManager, { mFlagMap }, mBarService ) - verify(mFlagManager).restartAction = any() + verify(mFlagManager).onSettingsChangedAction = any() mBroadcastReceiver = withArgCaptor { verify(mMockContext).registerReceiver(capture(), any(), nullable(), nullable(), any()) @@ -123,6 +127,22 @@ class FeatureFlagsDebugTest : SysuiTestCase() { } @Test + fun testReadSysPropBooleanFlag() { + whenever(mSystemProperties.getBoolean(anyString(), anyBoolean())).thenAnswer { + if ("b".equals(it.getArgument<String?>(0))) { + return@thenAnswer true + } + return@thenAnswer it.getArgument(1) + } + + assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(1, "a"))).isFalse() + assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(2, "b"))).isTrue() + assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(3, "c", true))).isTrue() + assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(4, "d", false))).isFalse() + assertThat(mFeatureFlagsDebug.isEnabled(SysPropBooleanFlag(5, "e"))).isFalse() + } + + @Test fun testReadStringFlag() { whenever(mFlagManager.readFlagValue<String>(eq(3), any())).thenReturn("foo") whenever(mFlagManager.readFlagValue<String>(eq(4), any())).thenReturn("bar") @@ -259,7 +279,7 @@ class FeatureFlagsDebugTest : SysuiTestCase() { verify(mFlagManager, times(numReads)).readFlagValue(eq(id), any<FlagSerializer<*>>()) verify(mFlagManager).idToSettingsKey(eq(id)) verify(mSecureSettings).putString(eq("key-$id"), eq(data)) - verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id)) + verify(mFlagManager).dispatchListenersAndMaybeRestart(eq(id), any()) }.verifyNoMoreInteractions() verifyNoMoreInteractions(mFlagManager, mSecureSettings) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt index b5e6602594eb..ad304c49bd41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt @@ -17,7 +17,7 @@ package com.android.systemui.flags import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Resources -import androidx.test.filters.SmallTest +import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.util.mockito.any @@ -44,12 +44,13 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { private lateinit var mFeatureFlagsRelease: FeatureFlagsRelease @Mock private lateinit var mResources: Resources + @Mock private lateinit var mSystemProperties: SystemPropertiesHelper @Mock private lateinit var mDumpManager: DumpManager @Before fun setup() { MockitoAnnotations.initMocks(this) - mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mDumpManager) + mFeatureFlagsRelease = FeatureFlagsRelease(mResources, mSystemProperties, mDumpManager) } @After @@ -87,6 +88,17 @@ class FeatureFlagsReleaseTest : SysuiTestCase() { } @Test + fun testSysPropBooleanFlag() { + val flagId = 213 + val flagName = "sys_prop_flag" + val flagDefault = true + + val flag = SysPropBooleanFlag(flagId, flagName, flagDefault) + whenever(mSystemProperties.getBoolean(flagName, flagDefault)).thenReturn(flagDefault) + assertThat(mFeatureFlagsRelease.isEnabled(flag)).isEqualTo(flagDefault) + } + + @Test fun testDump() { val flag1 = BooleanFlag(1, true) val flag2 = ResourceBooleanFlag(2, 1002) diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt index 644bd2147611..a2eca81b04ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt @@ -19,7 +19,7 @@ import android.content.Context import android.database.ContentObserver import android.net.Uri import android.os.Handler -import androidx.test.filters.SmallTest +import android.test.suitebuilder.annotation.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -130,14 +130,14 @@ class FlagManagerTest : SysuiTestCase() { mFlagManager.addListener(BooleanFlag(1, true), listener1) mFlagManager.addListener(BooleanFlag(10, true), listener10) - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, null) val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> { verify(listener1).onFlagChanged(capture()) } assertThat(flagEvent1.flagId).isEqualTo(1) verifyNoMoreInteractions(listener1, listener10) - mFlagManager.dispatchListenersAndMaybeRestart(10) + mFlagManager.dispatchListenersAndMaybeRestart(10, null) val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> { verify(listener10).onFlagChanged(capture()) } @@ -151,14 +151,14 @@ class FlagManagerTest : SysuiTestCase() { mFlagManager.addListener(BooleanFlag(1, true), listener) mFlagManager.addListener(BooleanFlag(10, true), listener) - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, null) val flagEvent1 = withArgCaptor<FlagListenable.FlagEvent> { verify(listener).onFlagChanged(capture()) } assertThat(flagEvent1.flagId).isEqualTo(1) verifyNoMoreInteractions(listener) - mFlagManager.dispatchListenersAndMaybeRestart(10) + mFlagManager.dispatchListenersAndMaybeRestart(10, null) val flagEvent10 = withArgCaptor<FlagListenable.FlagEvent> { verify(listener, times(2)).onFlagChanged(capture()) } @@ -169,8 +169,7 @@ class FlagManagerTest : SysuiTestCase() { @Test fun testRestartWithNoListeners() { val restartAction = mock<Consumer<Boolean>>() - mFlagManager.restartAction = restartAction - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) verify(restartAction).accept(eq(false)) verifyNoMoreInteractions(restartAction) } @@ -178,11 +177,10 @@ class FlagManagerTest : SysuiTestCase() { @Test fun testListenerCanSuppressRestart() { val restartAction = mock<Consumer<Boolean>>() - mFlagManager.restartAction = restartAction mFlagManager.addListener(BooleanFlag(1, true)) { event -> event.requestNoRestart() } - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) verify(restartAction).accept(eq(true)) verifyNoMoreInteractions(restartAction) } @@ -190,11 +188,10 @@ class FlagManagerTest : SysuiTestCase() { @Test fun testListenerOnlySuppressesRestartForOwnFlag() { val restartAction = mock<Consumer<Boolean>>() - mFlagManager.restartAction = restartAction mFlagManager.addListener(BooleanFlag(10, true)) { event -> event.requestNoRestart() } - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) verify(restartAction).accept(eq(false)) verifyNoMoreInteractions(restartAction) } @@ -202,14 +199,13 @@ class FlagManagerTest : SysuiTestCase() { @Test fun testRestartWhenNotAllListenersRequestSuppress() { val restartAction = mock<Consumer<Boolean>>() - mFlagManager.restartAction = restartAction mFlagManager.addListener(BooleanFlag(10, true)) { event -> event.requestNoRestart() } mFlagManager.addListener(BooleanFlag(10, true)) { // do not request } - mFlagManager.dispatchListenersAndMaybeRestart(1) + mFlagManager.dispatchListenersAndMaybeRestart(1, restartAction) verify(restartAction).accept(eq(false)) verifyNoMoreInteractions(restartAction) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt index b359ae5317b1..e606be179cc6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt @@ -33,11 +33,10 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.KeyguardBypassController -import com.android.systemui.statusbar.policy.FakeConfigurationController +import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.UniqueObjectHostView -import com.android.systemui.util.mockito.any -import com.google.common.truth.Truth.assertThat +import junit.framework.Assert import org.junit.Assert.assertNotNull import org.junit.Before import org.junit.Rule @@ -45,16 +44,16 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers -import org.mockito.ArgumentMatchers.anyBoolean -import org.mockito.ArgumentMatchers.anyLong import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.any +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyLong import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit -import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) @@ -84,6 +83,8 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Mock private lateinit var keyguardViewController: KeyguardViewController @Mock + private lateinit var configurationController: ConfigurationController + @Mock private lateinit var uniqueObjectHostView: UniqueObjectHostView @Mock private lateinit var dreamOverlayStateController: DreamOverlayStateController @@ -96,7 +97,6 @@ class MediaHierarchyManagerTest : SysuiTestCase() { val mockito = MockitoJUnit.rule() private lateinit var mediaHiearchyManager: MediaHierarchyManager private lateinit var mediaFrame: ViewGroup - private val configurationController = FakeConfigurationController() @Before fun setup() { @@ -176,7 +176,12 @@ class MediaHierarchyManagerTest : SysuiTestCase() { @Test fun testGoingToFullShade() { - goToLockscreen() + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) // Let's transition all the way to full shade mediaHiearchyManager.setTransitionToFullShadeAmount(100000f) @@ -199,48 +204,41 @@ class MediaHierarchyManagerTest : SysuiTestCase() { // Let's make sure alpha is set mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f) - assertThat(mediaFrame.alpha).isNotEqualTo(1.0f) + Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f) } @Test fun testTransformationOnLockScreenIsFading() { - goToLockscreen() - expandQS() - - val transformType = mediaHiearchyManager.calculateTransformationType() - assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) - } - - @Test - fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() { - enableSplitShade() - goToLockscreen() - expandQS() - mediaHiearchyManager.setTransitionToFullShadeAmount(10000f) - - val transformType = mediaHiearchyManager.calculateTransformationType() - assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION) - } - - @Test - fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() { - enableSplitShade() - goToLockscreen() - goToLockedShade() - expandQS() - mediaHiearchyManager.setTransitionToFullShadeAmount(0f) + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + // Let's transition from lockscreen to qs + mediaHiearchyManager.qsExpansion = 1.0f val transformType = mediaHiearchyManager.calculateTransformationType() - assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + Assert.assertTrue("media isn't transforming to qs with a fade", + transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) } @Test fun testTransformationOnLockScreenToQQSisFading() { - goToLockscreen() - goToLockedShade() + // Let's set it onto Lock screen + `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( + true) + statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) + clearInvocations(mediaCarouselController) + // Let's transition from lockscreen to qs + `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) + statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD, + StatusBarState.SHADE_LOCKED) val transformType = mediaHiearchyManager.calculateTransformationType() - assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) + Assert.assertTrue("media isn't transforming to qqswith a fade", + transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE) } @Test @@ -256,32 +254,4 @@ class MediaHierarchyManagerTest : SysuiTestCase() { verify(mediaCarouselController).closeGuts() } - - private fun enableSplitShade() { - context.getOrCreateTestableResources().addOverride( - R.bool.config_use_split_notification_shade, true - ) - configurationController.notifyConfigurationChanged() - } - - private fun goToLockscreen() { - whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) - whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn( - true - ) - statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD) - clearInvocations(mediaCarouselController) - } - - private fun goToLockedShade() { - whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED) - statusBarCallback.value.onStatePreChange( - StatusBarState.KEYGUARD, - StatusBarState.SHADE_LOCKED - ) - } - - private fun expandQS() { - mediaHiearchyManager.qsExpansion = 1.0f - } -} +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt index ea0a5a42ad21..28de17664df1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -50,6 +51,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { private lateinit var appIconDrawable: Drawable @Mock private lateinit var windowManager: WindowManager + @Mock + private lateinit var tapGestureDetector: TapGestureDetector @Before fun setUp() { @@ -58,23 +61,28 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { fakeClock = FakeSystemClock() fakeExecutor = FakeExecutor(fakeClock) - controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor) + controllerCommon = TestControllerCommon( + context, windowManager, fakeExecutor, tapGestureDetector + ) } @Test - fun displayChip_chipAdded() { + fun displayChip_chipAddedAndGestureDetectionStarted() { controllerCommon.displayChip(getState()) verify(windowManager).addView(any(), any()) + verify(tapGestureDetector).addOnGestureDetectedCallback(any(), any()) } @Test - fun displayChip_twice_chipNotAddedTwice() { + fun displayChip_twice_chipAndGestureDetectionNotAddedTwice() { controllerCommon.displayChip(getState()) reset(windowManager) + reset(tapGestureDetector) controllerCommon.displayChip(getState()) verify(windowManager, never()).addView(any(), any()) + verify(tapGestureDetector, never()).addOnGestureDetectedCallback(any(), any()) } @Test @@ -130,7 +138,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun removeChip_chipRemoved() { + fun removeChip_chipRemovedAndGestureDetectionStopped() { // First, add the chip controllerCommon.displayChip(getState()) @@ -138,6 +146,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { controllerCommon.removeChip() verify(windowManager).removeView(any()) + verify(tapGestureDetector).removeOnGestureDetectedCallback(any()) } @Test @@ -174,8 +183,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { context: Context, windowManager: WindowManager, @Main mainExecutor: DelayableExecutor, - ) : MediaTttChipControllerCommon<MediaTttChipState>( - context, windowManager, mainExecutor, R.layout.media_ttt_chip + tapGestureDetector: TapGestureDetector, + ) : MediaTttChipControllerCommon<MediaTttChipState>( + context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip ) { override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 117a6c898510..e5f4df6c622a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -22,6 +22,8 @@ import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.media.MediaRoute2Info import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import android.view.View import android.view.ViewGroup import android.view.WindowManager @@ -30,6 +32,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -37,6 +40,7 @@ import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never @@ -45,6 +49,8 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper class MediaTttChipControllerReceiverTest : SysuiTestCase() { private lateinit var controllerReceiver: MediaTttChipControllerReceiver @@ -76,7 +82,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { context, windowManager, FakeExecutor(FakeSystemClock()), - Handler.getMain() + TapGestureDetector(context), + Handler.getMain(), ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt index b44006429828..e5ba3f3d87bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt @@ -21,6 +21,8 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.media.MediaRoute2Info +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import android.view.View import android.view.WindowManager import android.widget.ImageView @@ -31,6 +33,7 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -38,6 +41,7 @@ import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.never @@ -46,6 +50,8 @@ import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper class MediaTttChipControllerSenderTest : SysuiTestCase() { private lateinit var controllerSender: MediaTttChipControllerSender @@ -73,7 +79,11 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() { context.setMockPackageManager(packageManager) controllerSender = MediaTttChipControllerSender( - commandQueue, context, windowManager, FakeExecutor(FakeSystemClock()) + commandQueue, + context, + windowManager, + FakeExecutor(FakeSystemClock()), + TapGestureDetector(context) ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java index ea4d7cc2529c..c5bc68d6bd7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java @@ -45,6 +45,7 @@ import android.content.Intent; import android.metrics.LogMaker; import android.os.Handler; import android.os.Looper; +import android.provider.Settings; import android.service.quicksettings.Tile; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -58,6 +59,7 @@ import com.android.internal.logging.InstanceId; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; @@ -101,6 +103,7 @@ public class QSTileImplTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private ActivityStarter mActivityStarter; + private UiEventLoggerFake mUiEventLoggerFake; private InstanceId mInstanceId = InstanceId.fakeInstanceId(5); @@ -113,7 +116,7 @@ public class QSTileImplTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); mUiEventLoggerFake = new UiEventLoggerFake(); when(mHost.indexOf(SPEC)).thenReturn(POSITION); - when(mHost.getContext()).thenReturn(mContext.getBaseContext()); + when(mHost.getContext()).thenReturn(mContext); when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake); when(mHost.getNewInstanceId()).thenReturn(mInstanceId); @@ -342,6 +345,22 @@ public class QSTileImplTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @Test + public void testClickOnDisabledByPolicyDoesntClickLaunchesIntent() { + String restriction = "RESTRICTION"; + mTile.getState().disabledByPolicy = true; + EnforcedAdmin admin = EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(restriction); + mTile.setEnforcedAdmin(admin); + + mTile.click(null); + mTestableLooper.processAllMessages(); + assertFalse(mTile.mClicked); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mActivityStarter).postStartActivityDismissingKeyguard(captor.capture(), anyInt()); + assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, captor.getValue().getAction()); + } + private void assertEvent(UiEventLogger.UiEventEnum eventType, UiEventLoggerFake.FakeUiEvent fakeEvent) { assertEquals(eventType.getId(), fakeEvent.eventId); @@ -400,6 +419,10 @@ public class QSTileImplTest extends SysuiTestCase { getState().state = Tile.STATE_ACTIVE; } + public void setEnforcedAdmin(EnforcedAdmin admin) { + mEnforcedAdmin = admin; + } + @Override public BooleanState newTileState() { return new BooleanState(); @@ -412,7 +435,6 @@ public class QSTileImplTest extends SysuiTestCase { @Override protected void handleUpdateState(BooleanState state, Object arg) { - } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt new file mode 100644 index 000000000000..cc472481ad8a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -0,0 +1,117 @@ +package com.android.systemui.qs.tiles + +import android.content.Context +import android.os.Handler +import android.os.Looper +import android.os.UserManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingManagerFake +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.plugins.qs.QSTile +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.QSTileHost +import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.statusbar.policy.BluetoothController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +@SmallTest +class BluetoothTileTest : SysuiTestCase() { + + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var qsLogger: QSLogger + @Mock + private lateinit var qsHost: QSTileHost + @Mock + private lateinit var metricsLogger: MetricsLogger + private val falsingManager = FalsingManagerFake() + @Mock + private lateinit var statusBarStateController: StatusBarStateController + @Mock + private lateinit var activityStarter: ActivityStarter + @Mock + private lateinit var bluetoothController: BluetoothController + + private val uiEventLogger = UiEventLoggerFake() + private lateinit var testableLooper: TestableLooper + private lateinit var tile: FakeBluetoothTile + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + + Mockito.`when`(qsHost.context).thenReturn(mockContext) + Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) + + tile = FakeBluetoothTile( + qsHost, + testableLooper.looper, + Handler(testableLooper.looper), + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger, + bluetoothController + ) + + tile.initialize() + testableLooper.processAllMessages() + } + + @Test + fun testRestrictionChecked() { + tile.refreshState() + testableLooper.processAllMessages() + + assertThat(tile.restrictionChecked).isEqualTo(UserManager.DISALLOW_BLUETOOTH) + } + + private class FakeBluetoothTile( + qsTileHost: QSTileHost, + backgroundLooper: Looper, + mainHandler: Handler, + falsingManager: FalsingManager, + metricsLogger: MetricsLogger, + statusBarStateController: StatusBarStateController, + activityStarter: ActivityStarter, + qsLogger: QSLogger, + bluetoothController: BluetoothController + ) : BluetoothTile( + qsTileHost, + backgroundLooper, + mainHandler, + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger, + bluetoothController + ) { + var restrictionChecked: String? = null + + override fun checkIfRestrictionEnforcedByAdminOnly( + state: QSTile.State?, + userRestriction: String? + ) { + restrictionChecked = userRestriction + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 75ccd8f03a01..9076e1607be5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -23,7 +23,7 @@ import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBar -import com.android.systemui.statusbar.policy.FakeConfigurationController +import com.android.systemui.statusbar.policy.ConfigurationController import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -66,6 +66,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock lateinit var mediaHierarchyManager: MediaHierarchyManager @Mock lateinit var scrimController: ScrimController + @Mock lateinit var configurationController: ConfigurationController @Mock lateinit var falsingManager: FalsingManager @Mock lateinit var notificationPanelController: NotificationPanelViewController @Mock lateinit var nsslController: NotificationStackScrollLayoutController @@ -76,8 +77,6 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var qS: QS @JvmField @Rule val mockito = MockitoJUnit.rule() - private val configurationController = FakeConfigurationController() - @Before fun setup() { val helper = NotificationTestHelper( @@ -245,27 +244,4 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(depthController).transitionToFullShadeProgress = anyFloat() } - - @Test - fun setDragDownAmount_setsValueOnMediaHierarchyManager() { - transitionController.dragDownAmount = 10f - - verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f) - } - - @Test - fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() { - enableSplitShade() - - transitionController.dragDownAmount = 10f - - verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f) - } - - private fun enableSplitShade() { - context.getOrCreateTestableResources().addOverride( - R.bool.config_use_split_notification_shade, true - ) - configurationController.notifyConfigurationChanged() - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt new file mode 100644 index 000000000000..c0389034b4f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt @@ -0,0 +1,130 @@ +package com.android.systemui.statusbar.gesture + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.InputEvent +import android.view.MotionEvent +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class GenericGestureDetectorTest : SysuiTestCase() { + + private lateinit var gestureDetector: TestGestureDetector + + @Before + fun setUp() { + gestureDetector = TestGestureDetector() + } + + @Test + fun noCallbacksRegistered_notGestureListening() { + assertThat(gestureDetector.isGestureListening).isFalse() + } + + @Test + fun callbackRegistered_isGestureListening() { + gestureDetector.addOnGestureDetectedCallback("tag"){} + + assertThat(gestureDetector.isGestureListening).isTrue() + } + + @Test + fun multipleCallbacksRegistered_isGestureListening() { + gestureDetector.addOnGestureDetectedCallback("tag"){} + gestureDetector.addOnGestureDetectedCallback("tag2"){} + + assertThat(gestureDetector.isGestureListening).isTrue() + } + + @Test + fun allCallbacksUnregistered_notGestureListening() { + gestureDetector.addOnGestureDetectedCallback("tag"){} + gestureDetector.addOnGestureDetectedCallback("tag2"){} + + gestureDetector.removeOnGestureDetectedCallback("tag") + gestureDetector.removeOnGestureDetectedCallback("tag2") + + assertThat(gestureDetector.isGestureListening).isFalse() + } + + @Test + fun someButNotAllCallbacksUnregistered_isGestureListening() { + gestureDetector.addOnGestureDetectedCallback("tag"){} + gestureDetector.addOnGestureDetectedCallback("tag2"){} + + gestureDetector.removeOnGestureDetectedCallback("tag2") + + assertThat(gestureDetector.isGestureListening).isTrue() + } + + @Test + fun onInputEvent_meetsGestureCriteria_allCallbacksNotified() { + var callbackNotified = false + gestureDetector.addOnGestureDetectedCallback("tag"){ + callbackNotified = true + } + + gestureDetector.onInputEvent( + MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0) + ) + + assertThat(callbackNotified).isTrue() + } + + @Test + fun onInputEvent_doesNotMeetGestureCriteria_callbackNotNotified() { + var callbackNotified = false + gestureDetector.addOnGestureDetectedCallback("tag"){ + callbackNotified = true + } + + gestureDetector.onInputEvent( + MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X - 5, 0f, 0) + ) + + assertThat(callbackNotified).isFalse() + } + + @Test + fun callbackUnregisteredThenGestureDetected_oldCallbackNotNotified() { + var oldCallbackNotified = false + gestureDetector.addOnGestureDetectedCallback("tag"){ + oldCallbackNotified = true + } + gestureDetector.addOnGestureDetectedCallback("tag2"){} + + gestureDetector.removeOnGestureDetectedCallback("tag") + gestureDetector.onInputEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, CORRECT_X, 0f, 0)) + + assertThat(oldCallbackNotified).isFalse() + } + + inner class TestGestureDetector : GenericGestureDetector("fakeTag") { + var isGestureListening = false + + override fun onInputEvent(ev: InputEvent) { + if (ev is MotionEvent && ev.x == CORRECT_X) { + onGestureDetected() + } + } + + override fun startGestureListening() { + super.startGestureListening() + isGestureListening = true + } + + override fun stopGestureListening() { + super.stopGestureListening() + isGestureListening = false + } + } +} + +private const val CORRECT_X = 1234f diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index 22899367afce..0e12c2adc09a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -52,6 +52,7 @@ import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.ReduceBrightColorsController; import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.external.CustomTile; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; @@ -85,6 +86,7 @@ public class AutoTileManagerTest extends SysuiTestCase { private static final String TEST_SETTING_COMPONENT = "setting_component"; private static final String TEST_COMPONENT = "test_pkg/test_cls"; private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")"; + private static final String TEST_CUSTOM_SAFETY_SPEC = "custom(safety_pkg/safety_cls)"; private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR; private static final int USER = 0; @@ -121,6 +123,8 @@ public class AutoTileManagerTest extends SysuiTestCase { ); mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_nightDisplayAvailable, true); + mContext.getOrCreateTestableResources().addOverride( + R.string.safety_quick_settings_tile, TEST_CUSTOM_SAFETY_SPEC); when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker); when(mQsTileHost.getUserContext()).thenReturn(mUserContext); @@ -435,6 +439,25 @@ public class AutoTileManagerTest extends SysuiTestCase { } @Test + public void testSafetyTileNotAdded_ifPreviouslyAdded() { + ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC); + mAutoTileManager.init(); + verify(mQsTileHost, times(1)).addTile(safetyComponent, true); + when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true); + mAutoTileManager.init(); + verify(mQsTileHost, times(1)).addTile(safetyComponent, true); + } + + @Test + public void testSafetyTileAdded_onUserChange() { + ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC); + mAutoTileManager.init(); + verify(mQsTileHost, times(1)).addTile(safetyComponent, true); + when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(false); + mAutoTileManager.changeUser(UserHandle.of(USER + 1)); + verify(mQsTileHost, times(2)).addTile(safetyComponent, true); + } + @Test public void testEmptyArray_doesNotCrash() { mContext.getOrCreateTestableResources().addOverride( R.array.config_quickSettingsAutoAdd, new String[0]); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index a14ea54fc7e8..5f2bbd341962 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.content.res.Resources; import android.hardware.display.AmbientDisplayConfiguration; +import android.os.Handler; import android.os.PowerManager; import android.provider.Settings; import android.test.suitebuilder.annotation.SmallTest; @@ -61,6 +62,7 @@ import java.util.Optional; public class DozeParametersTest extends SysuiTestCase { private DozeParameters mDozeParameters; + @Mock Handler mHandler; @Mock Resources mResources; @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration; @Mock private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; @@ -102,6 +104,8 @@ public class DozeParametersTest extends SysuiTestCase { .thenReturn(mFoldAodAnimationController); mDozeParameters = new DozeParameters( + mContext, + mHandler, mResources, mAmbientDisplayConfiguration, mAlwaysOnDisplayPolicy, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index c7db9e43166b..86109367d504 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -381,7 +381,7 @@ public class StatusBarTest extends SysuiTestCase { mShadeController = new ShadeControllerImpl(mCommandQueue, mStatusBarStateController, mNotificationShadeWindowController, mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class), - () -> Optional.of(mStatusBar), () -> mAssistManager, Optional.of(mBubbles)); + () -> Optional.of(mStatusBar), () -> mAssistManager); when(mOperatorNameViewControllerFactory.create(any())) .thenReturn(mOperatorNameViewController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index 0920cac9c094..807664d093da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -217,6 +217,27 @@ class OngoingCallControllerTest : SysuiTestCase() { verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any()) } + /** Regression test for b/216248574. */ + @Test + fun entryUpdated_getUidProcessStateThrowsException_noCrash() { + `when`(mockIActivityManager.getUidProcessState(eq(CALL_UID), nullable(String::class.java))) + .thenThrow(SecurityException()) + + // No assert required, just check no crash + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + } + + /** Regression test for b/216248574. */ + @Test + fun entryUpdated_registerUidObserverThrowsException_noCrash() { + `when`(mockIActivityManager.registerUidObserver( + any(), any(), any(), nullable(String::class.java) + )).thenThrow(SecurityException()) + + // No assert required, just check no crash + notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry()) + } + /** * If a call notification is never added before #onEntryRemoved is called, then the listener * should never be notified. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt deleted file mode 100644 index 3a5d9ee16b0a..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.android.systemui.statusbar.policy - -import android.content.res.Configuration - -/** Fake implementation of [ConfigurationController] for tests. */ -class FakeConfigurationController : ConfigurationController { - - private var listener: ConfigurationController.ConfigurationListener? = null - - override fun addCallback(listener: ConfigurationController.ConfigurationListener) { - this.listener = listener - } - - override fun removeCallback(listener: ConfigurationController.ConfigurationListener) { - this.listener = null - } - - override fun onConfigurationChanged(newConfiguration: Configuration?) { - listener?.onConfigChanged(newConfiguration) - } - - override fun notifyThemeChanged() { - listener?.onThemeChanged() - } - - fun notifyConfigurationChanged() { - onConfigurationChanged(newConfiguration = null) - } - - override fun isLayoutRtl(): Boolean = false -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 07e0279e0c52..896dab6c060d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -48,7 +48,6 @@ import com.android.systemui.qs.QSUserSwitcherEvent import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.NotificationShadeWindowView -import com.android.systemui.statusbar.phone.ShadeController import com.android.systemui.telephony.TelephonyListenerManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.settings.SecureSettings @@ -95,7 +94,6 @@ class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var threadedRenderer: ThreadedRenderer @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator - @Mock private lateinit var shadeController: ShadeController private lateinit var testableLooper: TestableLooper private lateinit var bgExecutor: FakeExecutor private lateinit var uiExecutor: FakeExecutor @@ -171,7 +169,6 @@ class UserSwitcherControllerTest : SysuiTestCase() { interactionJankMonitor, latencyTracker, dumpManager, - { shadeController }, dialogLaunchAnimator) userSwitcherController.init(notificationShadeWindowView) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java index b3805533cabd..ec619bb5952a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java @@ -36,6 +36,7 @@ import android.os.Process; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.CaptioningManager; import androidx.test.filters.SmallTest; @@ -88,6 +89,8 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { private PackageManager mPackageManager; @Mock private WakefulnessLifecycle mWakefullnessLifcycle; + @Mock + private CaptioningManager mCaptioningManager; @Before @@ -109,7 +112,7 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mBroadcastDispatcher, mRingerModeTracker, mThreadFactory, mAudioManager, mNotificationManager, mVibrator, mIAudioService, mAccessibilityManager, - mPackageManager, mWakefullnessLifcycle, mCallback); + mPackageManager, mWakefullnessLifcycle, mCaptioningManager, mCallback); mVolumeController.setEnableDialogs(true, true); } @@ -184,10 +187,11 @@ public class VolumeDialogControllerImplTest extends SysuiTestCase { AccessibilityManager accessibilityManager, PackageManager packageManager, WakefulnessLifecycle wakefulnessLifecycle, + CaptioningManager captioningManager, C callback) { super(context, broadcastDispatcher, ringerModeTracker, theadFactory, audioManager, notificationManager, optionalVibrator, iAudioService, accessibilityManager, - packageManager, wakefulnessLifecycle); + packageManager, wakefulnessLifecycle, captioningManager); mCallbacks = callback; ArgumentCaptor<WakefulnessLifecycle.Observer> observerCaptor = diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 4bc4e6ea98f9..593b97e55739 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -50,8 +50,10 @@ import android.app.IActivityManager; import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -197,6 +199,11 @@ public class BubblesTest extends SysuiTestCase { private ArgumentCaptor<NotificationRemoveInterceptor> mRemoveInterceptorCaptor; @Captor private ArgumentCaptor<List<Bubble>> mBubbleListCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; + private BubblesManager mBubblesManager; // TODO(178618782): Move tests on the controller directly to the shell @@ -1357,6 +1364,66 @@ public class BubblesTest extends SysuiTestCase { assertStackCollapsed(); } + @Test + public void testRegisterUnregisterBroadcastListener() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo( + Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo( + Intent.ACTION_SCREEN_OFF); + + mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL); + // TODO: not certain why this isn't called normally when tests are run, perhaps because + // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe + mBubbleController.onAllBubblesAnimatedOut(); + + verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue())); + } + + @Test + public void testBroadcastReceiverCloseDialogs_notGestureNav() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + + assertStackExpanded(); + } + + @Test + public void testBroadcastReceiverCloseDialogs_reasonGestureNav() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + i.putExtra("reason", "gestureNav"); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + assertStackCollapsed(); + } + + @Test + public void testBroadcastReceiver_screenOff() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + + Intent i = new Intent(Intent.ACTION_SCREEN_OFF); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + assertStackCollapsed(); + } + /** Creates a bubble using the userId and package. */ private Bubble createBubble(int userId, String pkg) { final UserHandle userHandle = new UserHandle(userId); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java index 75d8453cb071..cc848bc34217 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java @@ -46,6 +46,9 @@ import android.app.IActivityManager; import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.LauncherApps; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; @@ -179,6 +182,10 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor; @Captor private ArgumentCaptor<List<Bubble>> mBubbleListCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mFilterArgumentCaptor; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; private BubblesManager mBubblesManager; private TestableBubbleController mBubbleController; @@ -1176,6 +1183,67 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase { assertStackCollapsed(); } + + @Test + public void testRegisterUnregisterBroadcastListener() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + assertThat(mFilterArgumentCaptor.getValue().getAction(0)).isEqualTo( + Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + assertThat(mFilterArgumentCaptor.getValue().getAction(1)).isEqualTo( + Intent.ACTION_SCREEN_OFF); + + mBubbleData.dismissBubbleWithKey(mBubbleEntry.getKey(), REASON_APP_CANCEL); + // TODO: not certain why this isn't called normally when tests are run, perhaps because + // it's after an animation in BSV. This calls BubbleController#removeFromWindowManagerMaybe + mBubbleController.onAllBubblesAnimatedOut(); + + verify(mContext).unregisterReceiver(eq(mBroadcastReceiverArgumentCaptor.getValue())); + } + + @Test + public void testBroadcastReceiverCloseDialogs_notGestureNav() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + + assertStackExpanded(); + } + + @Test + public void testBroadcastReceiverCloseDialogs_reasonGestureNav() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + i.putExtra("reason", "gestureNav"); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + assertStackCollapsed(); + } + + @Test + public void testBroadcastReceiver_screenOff() { + spyOn(mContext); + mBubbleController.updateBubble(mBubbleEntry); + mBubbleData.setExpanded(true); + + verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(), + mFilterArgumentCaptor.capture()); + + Intent i = new Intent(Intent.ACTION_SCREEN_OFF); + mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i); + assertStackCollapsed(); + } + /** * Sets the bubble metadata flags for this entry. These flags are normally set by * NotificationManagerService when the notification is sent, however, these tests do not diff --git a/proto/src/camera.proto b/proto/src/camera.proto index 2d62f32d3941..40821185b61a 100644 --- a/proto/src/camera.proto +++ b/proto/src/camera.proto @@ -64,7 +64,7 @@ message CameraStreamProto { repeated int64 histogram_counts = 13; // The dynamic range profile of the stream - optional int32 dynamic_range_profile = 14; + optional int64 dynamic_range_profile = 14; // The stream use case optional int32 stream_use_case = 15; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 59c1461e4abc..aba32ec465b5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -667,6 +667,10 @@ public class AccessibilityWindowManager { return null; } + // Don't need to add the embedded hierarchy windows into the accessibility windows list. + if (mHostEmbeddedMap.size() > 0 && isEmbeddedHierarchyWindowsLocked(windowId)) { + return null; + } final AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain(); reportedWindow.setId(windowId); @@ -699,6 +703,21 @@ public class AccessibilityWindowManager { return reportedWindow; } + private boolean isEmbeddedHierarchyWindowsLocked(int windowId) { + final IBinder leashToken = mWindowIdMap.get(windowId); + if (leashToken == null) { + return false; + } + + for (int i = 0; i < mHostEmbeddedMap.size(); i++) { + if (mHostEmbeddedMap.keyAt(i).equals(leashToken)) { + return true; + } + } + + return false; + } + private int getTypeForWindowManagerWindowType(int windowType) { switch (windowType) { case WindowManager.LayoutParams.TYPE_APPLICATION: diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java index c39b59ae35b3..ec4bfe09e415 100644 --- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java +++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java @@ -118,13 +118,14 @@ class CompanionApplicationController { serviceConnectors = CollectionUtils.map(companionServices, componentName -> new CompanionDeviceServiceConnector(mContext, userId, componentName)); - mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); - } - if (serviceConnectors.isEmpty()) { - Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: " - + packageName + ". Please check if they are correctly declared."); - return; + if (serviceConnectors.isEmpty()) { + Slog.e(TAG, "Can't find CompanionDeviceService implementer in package: " + + packageName + ". Please check if they are correctly declared."); + return; + } + + mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); } // The first connector in the list is always the primary connector: set a listener to it. diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index 12e438d1f2a9..78df983c83f7 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.UserInfo; import android.os.Environment; import android.os.SystemClock; @@ -38,6 +40,7 @@ import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; import com.android.server.SystemService.UserCompletedEventType; import com.android.server.am.EventLogTags; +import com.android.server.pm.ApexManager; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.TimingsTraceAndSlog; @@ -47,6 +50,8 @@ import java.io.File; import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -147,12 +152,31 @@ public final class SystemServiceManager implements Dumpable { * @return The service instance. */ public SystemService startServiceFromJar(String className, String path) { - PathClassLoader pathClassLoader = SystemServerClassLoaderFactory.getOrCreateClassLoader( - path, this.getClass().getClassLoader()); + PathClassLoader pathClassLoader = + SystemServerClassLoaderFactory.getOrCreateClassLoader( + path, this.getClass().getClassLoader(), isJarInTestApex(path)); final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader); return startService(serviceClass); } + /** + * Returns true if the jar is in a test APEX. + */ + private static boolean isJarInTestApex(String pathStr) { + Path path = Paths.get(pathStr); + if (path.getNameCount() >= 2 && path.getName(0).toString().equals("apex")) { + String apexModuleName = path.getName(1).toString(); + ApexManager apexManager = ApexManager.getInstance(); + String packageName = apexManager.getActivePackageNameForApexModuleName(apexModuleName); + PackageInfo packageInfo = apexManager.getPackageInfo( + packageName, ApexManager.MATCH_ACTIVE_PACKAGE); + if (packageInfo != null) { + return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_TEST_ONLY) != 0; + } + } + return false; + } + /* * Loads and initializes a class from the given classLoader. Returns the class. */ diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index efbc4deaf9f5..40ab0c07d166 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -651,100 +651,102 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } private void onMultiSimConfigChanged() { - int oldNumPhones = mNumPhones; - mNumPhones = getTelephonyManager().getActiveModemCount(); - if (oldNumPhones == mNumPhones) return; - - if (DBG) { - log("TelephonyRegistry: activeModemCount changed from " + oldNumPhones - + " to " + mNumPhones); - } - mCallState = copyOf(mCallState, mNumPhones); - mDataActivity = copyOf(mCallState, mNumPhones); - mDataConnectionState = copyOf(mCallState, mNumPhones); - mDataConnectionNetworkType = copyOf(mCallState, mNumPhones); - mCallIncomingNumber = copyOf(mCallIncomingNumber, mNumPhones); - mServiceState = copyOf(mServiceState, mNumPhones); - mVoiceActivationState = copyOf(mVoiceActivationState, mNumPhones); - mDataActivationState = copyOf(mDataActivationState, mNumPhones); - mUserMobileDataState = copyOf(mUserMobileDataState, mNumPhones); - if (mSignalStrength != null) { - mSignalStrength = copyOf(mSignalStrength, mNumPhones); - } else { - mSignalStrength = new SignalStrength[mNumPhones]; - } - mMessageWaiting = copyOf(mMessageWaiting, mNumPhones); - mCallForwarding = copyOf(mCallForwarding, mNumPhones); - mCellIdentity = copyOf(mCellIdentity, mNumPhones); - mSrvccState = copyOf(mSrvccState, mNumPhones); - mPreciseCallState = copyOf(mPreciseCallState, mNumPhones); - mForegroundCallState = copyOf(mForegroundCallState, mNumPhones); - mBackgroundCallState = copyOf(mBackgroundCallState, mNumPhones); - mRingingCallState = copyOf(mRingingCallState, mNumPhones); - mCallDisconnectCause = copyOf(mCallDisconnectCause, mNumPhones); - mCallPreciseDisconnectCause = copyOf(mCallPreciseDisconnectCause, mNumPhones); - mCallQuality = copyOf(mCallQuality, mNumPhones); - mCallNetworkType = copyOf(mCallNetworkType, mNumPhones); - mCallAttributes = copyOf(mCallAttributes, mNumPhones); - mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); - mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); - mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); - mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones); - mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones); - mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones); - mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones); - mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones); - - // ds -> ss switch. - if (mNumPhones < oldNumPhones) { - cutListToSize(mCellInfo, mNumPhones); - cutListToSize(mImsReasonInfo, mNumPhones); - cutListToSize(mPreciseDataConnectionStates, mNumPhones); - cutListToSize(mBarringInfo, mNumPhones); - cutListToSize(mPhysicalChannelConfigs, mNumPhones); - cutListToSize(mLinkCapacityEstimateLists, mNumPhones); - cutListToSize(mCarrierPrivilegeStates, mNumPhones); - return; - } + synchronized (mRecords) { + int oldNumPhones = mNumPhones; + mNumPhones = getTelephonyManager().getActiveModemCount(); + if (oldNumPhones == mNumPhones) return; - // mNumPhones > oldNumPhones: ss -> ds switch - for (int i = oldNumPhones; i < mNumPhones; i++) { - mCallState[i] = TelephonyManager.CALL_STATE_IDLE; - mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; - mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN; - mVoiceActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; - mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; - mCallIncomingNumber[i] = ""; - mServiceState[i] = new ServiceState(); - mSignalStrength[i] = null; - mUserMobileDataState[i] = false; - mMessageWaiting[i] = false; - mCallForwarding[i] = false; - mCellIdentity[i] = null; - mCellInfo.add(i, null); - mImsReasonInfo.add(i, null); - mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; - mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; - mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; - mCallQuality[i] = createCallQuality(); - mCallAttributes[i] = new CallAttributes(createPreciseCallState(), - TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); - mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; - mPreciseCallState[i] = createPreciseCallState(); - mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; - mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; - mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; - mPreciseDataConnectionStates.add(new ArrayMap<>()); - mBarringInfo.add(i, new BarringInfo()); - mCarrierNetworkChangeState[i] = false; - mTelephonyDisplayInfos[i] = null; - mIsDataEnabled[i] = false; - mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; - mPhysicalChannelConfigs.add(i, new ArrayList<>()); - mAllowedNetworkTypeReason[i] = -1; - mAllowedNetworkTypeValue[i] = -1; - mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST); - mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0])); + if (DBG) { + log("TelephonyRegistry: activeModemCount changed from " + oldNumPhones + + " to " + mNumPhones); + } + mCallState = copyOf(mCallState, mNumPhones); + mDataActivity = copyOf(mCallState, mNumPhones); + mDataConnectionState = copyOf(mCallState, mNumPhones); + mDataConnectionNetworkType = copyOf(mCallState, mNumPhones); + mCallIncomingNumber = copyOf(mCallIncomingNumber, mNumPhones); + mServiceState = copyOf(mServiceState, mNumPhones); + mVoiceActivationState = copyOf(mVoiceActivationState, mNumPhones); + mDataActivationState = copyOf(mDataActivationState, mNumPhones); + mUserMobileDataState = copyOf(mUserMobileDataState, mNumPhones); + if (mSignalStrength != null) { + mSignalStrength = copyOf(mSignalStrength, mNumPhones); + } else { + mSignalStrength = new SignalStrength[mNumPhones]; + } + mMessageWaiting = copyOf(mMessageWaiting, mNumPhones); + mCallForwarding = copyOf(mCallForwarding, mNumPhones); + mCellIdentity = copyOf(mCellIdentity, mNumPhones); + mSrvccState = copyOf(mSrvccState, mNumPhones); + mPreciseCallState = copyOf(mPreciseCallState, mNumPhones); + mForegroundCallState = copyOf(mForegroundCallState, mNumPhones); + mBackgroundCallState = copyOf(mBackgroundCallState, mNumPhones); + mRingingCallState = copyOf(mRingingCallState, mNumPhones); + mCallDisconnectCause = copyOf(mCallDisconnectCause, mNumPhones); + mCallPreciseDisconnectCause = copyOf(mCallPreciseDisconnectCause, mNumPhones); + mCallQuality = copyOf(mCallQuality, mNumPhones); + mCallNetworkType = copyOf(mCallNetworkType, mNumPhones); + mCallAttributes = copyOf(mCallAttributes, mNumPhones); + mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones); + mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones); + mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); + mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones); + mIsDataEnabled = copyOf(mIsDataEnabled, mNumPhones); + mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones); + mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones); + mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones); + + // ds -> ss switch. + if (mNumPhones < oldNumPhones) { + cutListToSize(mCellInfo, mNumPhones); + cutListToSize(mImsReasonInfo, mNumPhones); + cutListToSize(mPreciseDataConnectionStates, mNumPhones); + cutListToSize(mBarringInfo, mNumPhones); + cutListToSize(mPhysicalChannelConfigs, mNumPhones); + cutListToSize(mLinkCapacityEstimateLists, mNumPhones); + cutListToSize(mCarrierPrivilegeStates, mNumPhones); + return; + } + + // mNumPhones > oldNumPhones: ss -> ds switch + for (int i = oldNumPhones; i < mNumPhones; i++) { + mCallState[i] = TelephonyManager.CALL_STATE_IDLE; + mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; + mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN; + mVoiceActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; + mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; + mCallIncomingNumber[i] = ""; + mServiceState[i] = new ServiceState(); + mSignalStrength[i] = null; + mUserMobileDataState[i] = false; + mMessageWaiting[i] = false; + mCallForwarding[i] = false; + mCellIdentity[i] = null; + mCellInfo.add(i, null); + mImsReasonInfo.add(i, null); + mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE; + mCallDisconnectCause[i] = DisconnectCause.NOT_VALID; + mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID; + mCallQuality[i] = createCallQuality(); + mCallAttributes[i] = new CallAttributes(createPreciseCallState(), + TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality()); + mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN; + mPreciseCallState[i] = createPreciseCallState(); + mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; + mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; + mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE; + mPreciseDataConnectionStates.add(new ArrayMap<>()); + mBarringInfo.add(i, new BarringInfo()); + mCarrierNetworkChangeState[i] = false; + mTelephonyDisplayInfos[i] = null; + mIsDataEnabled[i] = false; + mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; + mPhysicalChannelConfigs.add(i, new ArrayList<>()); + mAllowedNetworkTypeReason[i] = -1; + mAllowedNetworkTypeValue[i] = -1; + mLinkCapacityEstimateLists.add(i, INVALID_LCE_LIST); + mCarrierPrivilegeStates.add(i, new Pair<>(Collections.emptyList(), new int[0])); + } } } @@ -2802,11 +2804,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + " callback=" + callback + " callback.asBinder=" + callback.asBinder()); } - if (!validatePhoneId(phoneId)) { - throw new IllegalArgumentException("Invalid slot index: " + phoneId); - } synchronized (mRecords) { + if (!validatePhoneId(phoneId)) { + throw new IllegalArgumentException("Invalid slot index: " + phoneId); + } Record r = add( callback.asBinder(), Binder.getCallingUid(), Binder.getCallingPid(), false); @@ -2851,7 +2853,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (!checkNotifyPermission("notifyCarrierPrivilegesChanged")) { return; } - if (!validatePhoneId(phoneId)) return; if (VDBG) { log( "notifyCarrierPrivilegesChanged: phoneId=" + phoneId @@ -2859,6 +2860,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { + ", uids=" + Arrays.toString(privilegedUids) + ">"); } synchronized (mRecords) { + if (!validatePhoneId(phoneId)) { + throw new IllegalArgumentException("Invalid slot index: " + phoneId); + } mCarrierPrivilegeStates.set( phoneId, new Pair<>(privilegedPackageNames, privilegedUids)); for (Record r : mRecords) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bdb3e808b6f1..ebb19cd7e9be 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9176,6 +9176,7 @@ public class ActivityManagerService extends IActivityManager.Stub boolean dumpVisibleStacksOnly = false; boolean dumpFocusedStackOnly = false; String dumpPackage = null; + int dumpUserId = UserHandle.USER_ALL; int opti = 0; while (opti < args.length) { @@ -9207,6 +9208,17 @@ public class ActivityManagerService extends IActivityManager.Stub dumpCheckinFormat = true; } else if ("--normal-priority".equals(opt)) { dumpNormalPriority = true; + } else if ("--user".equals(opt)) { + if (opti < args.length) { + dumpUserId = UserHandle.parseUserArg(args[opti]); + if (dumpUserId == UserHandle.USER_CURRENT) { + dumpUserId = mUserController.getCurrentUserId(); + } + opti++; + } else { + pw.println("Error: --user option requires user id argument"); + return; + } } else if ("-h".equals(opt)) { ActivityManagerShellCommand.dumpHelp(pw, true); return; @@ -9397,29 +9409,17 @@ public class ActivityManagerService extends IActivityManager.Stub } else if ("service".equals(cmd)) { String[] newArgs; String name; - int[] users = null; if (opti >= args.length) { name = null; newArgs = EMPTY_STRING_ARRAY; } else { name = args[opti]; opti++; - if ("--user".equals(name) && opti < args.length) { - int userId = UserHandle.parseUserArg(args[opti]); - opti++; - if (userId != UserHandle.USER_ALL) { - if (userId == UserHandle.USER_CURRENT) { - userId = getCurrentUser().id; - } - users = new int[] { userId }; - } - name = args[opti]; - opti++; - } newArgs = new String[args.length - opti]; if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); } + int[] users = dumpUserId == UserHandle.USER_ALL ? null : new int[] { dumpUserId }; if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); @@ -9480,7 +9480,7 @@ public class ActivityManagerService extends IActivityManager.Stub } else { // Dumping a single activity? if (!mAtmInternal.dumpActivity(fd, pw, cmd, args, opti, dumpAll, - dumpVisibleStacksOnly, dumpFocusedStackOnly)) { + dumpVisibleStacksOnly, dumpFocusedStackOnly, dumpUserId)) { ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true); int res = shell.exec(this, null, fd, null, args, null, new ResultReceiver(null)); @@ -14330,7 +14330,8 @@ public class ActivityManagerService extends IActivityManager.Stub int match = mContext.getPackageManager().checkSignatures( ii.targetPackage, ii.packageName); if (match < 0 && match != PackageManager.SIGNATURE_FIRST_NOT_SIGNED) { - if (Build.IS_DEBUGGABLE && (flags & INSTR_FLAG_ALWAYS_CHECK_SIGNATURE) == 0) { + if (Build.IS_DEBUGGABLE && (callingUid == Process.ROOT_UID) + && (flags & INSTR_FLAG_ALWAYS_CHECK_SIGNATURE) == 0) { Slog.w(TAG, "Instrumentation test " + ii.packageName + " doesn't have a signature matching the target " + ii.targetPackage + ", which would not be allowed on the production Android builds"); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 8d77edabe3bf..6a211d31aa49 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1499,7 +1499,6 @@ public class OomAdjuster { int adj; int schedGroup; int procState; - int cachedAdjSeq; int capability = cycleReEval ? app.mState.getCurCapability() : 0; boolean foregroundActivities = false; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index f50011020694..a7b49f1e51b0 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -204,8 +204,8 @@ public final class DisplayManagerService extends SystemService { private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top"; private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000; // This value needs to be in sync with the threshold - // in RefreshRateConfigs::getFrameRateDivider. - private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.0009f; + // in RefreshRateConfigs::getFrameRateDivisor. + private static final float THRESHOLD_FOR_REFRESH_RATES_DIVISORS = 0.0009f; private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1; private static final int MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS = 2; @@ -895,13 +895,13 @@ public final class DisplayManagerService extends SystemService { return info; } - // Override the refresh rate only if it is a divider of the current + // Override the refresh rate only if it is a divisor of the current // refresh rate. This calculation needs to be in sync with the native code - // in RefreshRateConfigs::getFrameRateDivider + // in RefreshRateConfigs::getFrameRateDivisor Display.Mode currentMode = info.getMode(); float numPeriods = currentMode.getRefreshRate() / frameRateHz; float numPeriodsRound = Math.round(numPeriods); - if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVISORS) { return info; } frameRateHz = currentMode.getRefreshRate() / numPeriodsRound; @@ -913,9 +913,9 @@ public final class DisplayManagerService extends SystemService { continue; } - if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVIDERS + if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVISORS && mode.getRefreshRate() - <= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + <= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVISORS) { if (DEBUG) { Slog.d(TAG, "found matching modeId " + mode.getModeId()); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index d233c5ee2ffc..2e80efb957d4 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -255,10 +255,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo, SurfaceControl.DynamicDisplayInfo dynamicInfo, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { - boolean changed = - updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode); - changed |= updateDisplayModesLocked( - dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs); + boolean changed = updateDisplayModesLocked( + dynamicInfo.supportedDisplayModes, dynamicInfo.preferredBootDisplayMode, + dynamicInfo.activeDisplayModeId, modeSpecs); changed |= updateStaticInfo(staticInfo); changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, dynamicInfo.activeColorMode); @@ -273,10 +272,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } public boolean updateDisplayModesLocked( - SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId, - SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { + SurfaceControl.DisplayMode[] displayModes, int preferredSfDisplayModeId, + int activeSfDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length); - mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId); + mActiveSfDisplayMode = getModeById(displayModes, activeSfDisplayModeId); + SurfaceControl.DisplayMode preferredSfDisplayMode = + getModeById(displayModes, preferredSfDisplayModeId); // Build an updated list of all existing modes. ArrayList<DisplayModeRecord> records = new ArrayList<>(); @@ -335,6 +336,27 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + boolean preferredModeChanged = false; + + if (preferredSfDisplayModeId != INVALID_MODE_ID && preferredSfDisplayMode != null) { + DisplayModeRecord preferredRecord = null; + for (DisplayModeRecord record : records) { + if (record.hasMatchingMode(preferredSfDisplayMode)) { + preferredRecord = record; + break; + } + } + + if (preferredRecord != null) { + int preferredModeId = preferredRecord.mMode.getModeId(); + if (mSurfaceControlProxy.getBootDisplayModeSupport() + && mSystemPreferredModeId != preferredModeId) { + mSystemPreferredModeId = preferredModeId; + preferredModeChanged = true; + } + } + } + boolean activeModeChanged = false; // Check whether SurfaceFlinger or the display device changed the active mode out from @@ -373,7 +395,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { boolean recordsChanged = records.size() != mSupportedModes.size() || modesAdded; // If the records haven't changed then we're done here. if (!recordsChanged) { - return activeModeChanged; + return activeModeChanged || preferredModeChanged; } mSupportedModes.clear(); @@ -386,14 +408,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID ? mSystemPreferredModeId : activeRecord.mMode.getModeId(); mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID - ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group + ? preferredSfDisplayMode.group : mActiveSfDisplayMode.group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); mDefaultModeId = activeRecord.mMode.getModeId(); mDefaultModeGroup = mActiveSfDisplayMode.group; - } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) { + } else if (findSfDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) { Slog.w(TAG, "Default display mode no longer available, using currently" + " active mode as default."); mDefaultModeId = activeRecord.mMode.getModeId(); @@ -545,15 +567,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } - private boolean updateSystemPreferredDisplayMode(int modeId) { - if (!mSurfaceControlProxy.getBootDisplayModeSupport() - || mSystemPreferredModeId == modeId) { - return false; - } - mSystemPreferredModeId = modeId; - return true; - } - private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, int modeId) { for (SurfaceControl.DisplayMode mode : supportedModes) { @@ -887,8 +900,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (mUserPreferredMode == null) { mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked()); } else { + int preferredSfDisplayModeId = findSfDisplayModeIdLocked( + mUserPreferredMode.getModeId(), mDefaultModeGroup); mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(), - mUserPreferredMode.getModeId()); + preferredSfDisplayModeId); } } @@ -923,9 +938,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { // mode based on vendor requirements. // Note: We prefer the default mode group over the current one as this is the mode // group the vendor prefers. - int baseModeId = findDisplayModeIdLocked(displayModeSpecs.baseModeId, + int baseSfModeId = findSfDisplayModeIdLocked(displayModeSpecs.baseModeId, mDefaultModeGroup); - if (baseModeId < 0) { + if (baseSfModeId < 0) { // When a display is hotplugged, it's possible for a mode to be removed that was // previously valid. Because of the way display changes are propagated through the // framework, and the caching of the display mode specs in LogicalDisplay, it's @@ -943,7 +958,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { getHandler().sendMessage(PooledLambda.obtainMessage( LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this, getDisplayTokenLocked(), - new SurfaceControl.DesiredDisplayModeSpecs(baseModeId, + new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId, mDisplayModeSpecs.allowGroupSwitching, mDisplayModeSpecs.primaryRefreshRateRange.min, mDisplayModeSpecs.primaryRefreshRateRange.max, @@ -1090,14 +1105,14 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig); } - private int findDisplayModeIdLocked(int modeId, int modeGroup) { - int matchingModeId = INVALID_MODE_ID; - DisplayModeRecord record = mSupportedModes.get(modeId); + private int findSfDisplayModeIdLocked(int displayModeId, int modeGroup) { + int matchingSfDisplayModeId = INVALID_MODE_ID; + DisplayModeRecord record = mSupportedModes.get(displayModeId); if (record != null) { for (SurfaceControl.DisplayMode mode : mSfDisplayModes) { if (record.hasMatchingMode(mode)) { - if (matchingModeId == INVALID_MODE_ID) { - matchingModeId = mode.id; + if (matchingSfDisplayModeId == INVALID_MODE_ID) { + matchingSfDisplayModeId = mode.id; } // Prefer to return a mode that matches the modeGroup @@ -1107,7 +1122,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } - return matchingModeId; + return matchingSfDisplayModeId; } // Returns a mode with id = modeId. diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java index a290eb3a3e2f..ea3a3d5f1c60 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java @@ -168,6 +168,7 @@ public class RuleBinaryParser implements RuleParser { switch (key) { case AtomicFormula.PACKAGE_NAME: case AtomicFormula.APP_CERTIFICATE: + case AtomicFormula.APP_CERTIFICATE_LINEAGE: case AtomicFormula.INSTALLER_NAME: case AtomicFormula.INSTALLER_CERTIFICATE: case AtomicFormula.STAMP_CERTIFICATE_HASH: diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 45d9822205ec..fac510651878 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -1127,7 +1127,7 @@ public class LocationManagerService extends ILocationManager.Stub implements if (provider != null && !provider.equals(manager.getName())) { continue; } - CallerIdentity identity = manager.getIdentity(); + CallerIdentity identity = manager.getProviderIdentity(); if (identity == null) { continue; } @@ -1149,7 +1149,7 @@ public class LocationManagerService extends ILocationManager.Stub implements return Collections.emptyList(); } - CallerIdentity identity = manager.getIdentity(); + CallerIdentity identity = manager.getProviderIdentity(); if (identity == null) { return Collections.emptyList(); } @@ -1536,7 +1536,7 @@ public class LocationManagerService extends ILocationManager.Stub implements if (!enabled) { PackageTagsList.Builder builder = new PackageTagsList.Builder(); for (LocationProviderManager manager : mProviderManagers) { - CallerIdentity identity = manager.getIdentity(); + CallerIdentity identity = manager.getProviderIdentity(); if (identity != null) { builder.add(identity.getPackageName(), identity.getAttributionTag()); } @@ -1624,7 +1624,7 @@ public class LocationManagerService extends ILocationManager.Stub implements if (provider != null && !provider.equals(manager.getName())) { continue; } - if (identity.equals(manager.getIdentity())) { + if (identity.equals(manager.getProviderIdentity())) { return true; } } @@ -1665,7 +1665,7 @@ public class LocationManagerService extends ILocationManager.Stub implements if (listener != null) { ArraySet<Integer> uids = new ArraySet<>(mProviderManagers.size()); for (LocationProviderManager manager : mProviderManagers) { - CallerIdentity identity = manager.getIdentity(); + CallerIdentity identity = manager.getProviderIdentity(); if (identity != null) { uids.add(identity.getUid()); } diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java index 0b8f94c574c6..acbee11f3b72 100644 --- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java @@ -1468,7 +1468,7 @@ public class LocationProviderManager extends return mProvider.getState(); } - public @Nullable CallerIdentity getIdentity() { + public @Nullable CallerIdentity getProviderIdentity() { return mProvider.getState().identity; } @@ -1607,7 +1607,7 @@ public class LocationProviderManager extends public @Nullable Location getLastLocation(LastLocationRequest request, CallerIdentity identity, @PermissionLevel int permissionLevel) { - request = calculateLastLocationRequest(request); + request = calculateLastLocationRequest(request, identity); if (!isActive(request.isBypass(), identity)) { return null; @@ -1636,15 +1636,16 @@ public class LocationProviderManager extends return location; } - private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest) { + private LastLocationRequest calculateLastLocationRequest(LastLocationRequest baseRequest, + CallerIdentity identity) { LastLocationRequest.Builder builder = new LastLocationRequest.Builder(baseRequest); boolean locationSettingsIgnored = baseRequest.isLocationSettingsIgnored(); if (locationSettingsIgnored) { // if we are not currently allowed use location settings ignored, disable it if (!mSettingsHelper.getIgnoreSettingsAllowlist().contains( - getIdentity().getPackageName(), getIdentity().getAttributionTag()) - && !mLocationManagerInternal.isProvider(null, getIdentity())) { + identity.getPackageName(), identity.getAttributionTag()) + && !mLocationManagerInternal.isProvider(null, identity)) { locationSettingsIgnored = false; } @@ -1658,7 +1659,7 @@ public class LocationProviderManager extends Log.e(TAG, "adas gnss bypass request received in non-gps provider"); adasGnssBypass = false; } else if (!mLocationSettings.getUserSettings( - getIdentity().getUserId()).isAdasGnssLocationEnabled()) { + identity.getUserId()).isAdasGnssLocationEnabled()) { adasGnssBypass = false; } diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 074d891a9974..92703ec86d06 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -61,6 +61,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.NoSuchElementException; /** * This is the system implementation of a Session. Apps will interact with the @@ -792,7 +793,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR } for (ISessionControllerCallbackHolder holder : mControllerCallbackHolders) { try { + holder.mCallback.asBinder().unlinkToDeath(holder.mDeathMonitor, 0); holder.mCallback.onSessionDestroyed(); + } catch (NoSuchElementException e) { + logCallbackException("error unlinking to binder death", holder, e); } catch (DeadObjectException e) { logCallbackException("Removing dead callback in pushSessionDestroyed", holder, e); } catch (RemoteException e) { @@ -1375,12 +1379,22 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR return; } if (getControllerHolderIndexForCb(cb) < 0) { - mControllerCallbackHolders.add(new ISessionControllerCallbackHolder(cb, - packageName, Binder.getCallingUid())); + ISessionControllerCallbackHolder holder = new ISessionControllerCallbackHolder( + cb, packageName, Binder.getCallingUid(), () -> unregisterCallback(cb)); + mControllerCallbackHolders.add(holder); if (DEBUG) { Log.d(TAG, "registering controller callback " + cb + " from controller" + packageName); } + // Avoid callback leaks + try { + // cb is not referenced outside of the MediaSessionRecord, so the death + // handler won't prevent MediaSessionRecord to be garbage collected. + cb.asBinder().linkToDeath(holder.mDeathMonitor, 0); + } catch (RemoteException e) { + unregisterCallback(cb); + Log.w(TAG, "registerCallback failed to linkToDeath", e); + } } } } @@ -1390,6 +1404,12 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR synchronized (mLock) { int index = getControllerHolderIndexForCb(cb); if (index != -1) { + try { + cb.asBinder().unlinkToDeath( + mControllerCallbackHolders.get(index).mDeathMonitor, 0); + } catch (NoSuchElementException e) { + Log.w(TAG, "error unlinking to binder death", e); + } mControllerCallbackHolders.remove(index); } if (DEBUG) { @@ -1600,12 +1620,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR private final ISessionControllerCallback mCallback; private final String mPackageName; private final int mUid; + private final IBinder.DeathRecipient mDeathMonitor; ISessionControllerCallbackHolder(ISessionControllerCallback callback, String packageName, - int uid) { + int uid, IBinder.DeathRecipient deathMonitor) { mCallback = callback; mPackageName = packageName; mUid = uid; + mDeathMonitor = deathMonitor; } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 33ac6cdb269e..c9631549cd76 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -77,6 +77,8 @@ public class NetworkPolicyLogger { private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12; private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; private static final int EVENT_APP_IDLE_WL_CHANGED = 14; + private static final int EVENT_METERED_ALLOWLIST_CHANGED = 15; + private static final int EVENT_METERED_DENYLIST_CHANGED = 16; private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); @@ -89,7 +91,7 @@ public class NetworkPolicyLogger { void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) { synchronized (mLock) { if (LOGD || uid == mDebugUid) { - Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString()); + Slog.d(TAG, "Blocked state of " + uid + ": " + uidBlockedState.toString()); } if (uidBlockedState == null) { mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE, @@ -245,6 +247,24 @@ public class NetworkPolicyLogger { } } + void meteredAllowlistChanged(int uid, boolean added) { + synchronized (mLock) { + if (LOGD || mDebugUid == uid) { + Slog.d(TAG, getMeteredAllowlistChangedLog(uid, added)); + } + mEventsBuffer.meteredAllowlistChanged(uid, added); + } + } + + void meteredDenylistChanged(int uid, boolean added) { + synchronized (mLock) { + if (LOGD || mDebugUid == uid) { + Slog.d(TAG, getMeteredDenylistChangedLog(uid, added)); + } + mEventsBuffer.meteredDenylistChanged(uid, added); + } + } + void setDebugUid(int uid) { mDebugUid = uid; } @@ -320,6 +340,14 @@ public class NetworkPolicyLogger { return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled; } + private static String getMeteredAllowlistChangedLog(int uid, boolean added) { + return "metered-allowlist for " + uid + " changed to " + added; + } + + private static String getMeteredDenylistChangedLog(int uid, boolean added) { + return "metered-denylist for " + uid + " changed to " + added; + } + private static String getFirewallChainName(int chain) { switch (chain) { case FIREWALL_CHAIN_DOZABLE: @@ -520,6 +548,28 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } + public void meteredAllowlistChanged(int uid, boolean added) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_METERED_ALLOWLIST_CHANGED; + data.ifield1 = uid; + data.bfield1 = added; + data.timeStamp = System.currentTimeMillis(); + } + + public void meteredDenylistChanged(int uid, boolean added) { + final Data data = getNextSlot(); + if (data == null) return; + + data.reset(); + data.type = EVENT_METERED_DENYLIST_CHANGED; + data.ifield1 = uid; + data.bfield1 = added; + data.timeStamp = System.currentTimeMillis(); + } + public void reverseDump(IndentingPrintWriter pw) { final Data[] allData = toArray(); for (int i = allData.length - 1; i >= 0; --i) { @@ -567,6 +617,10 @@ public class NetworkPolicyLogger { return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3); case EVENT_FIREWALL_CHAIN_ENABLED: return getFirewallChainEnabledLog(data.ifield1, data.bfield1); + case EVENT_METERED_ALLOWLIST_CHANGED: + return getMeteredAllowlistChangedLog(data.ifield1, data.bfield1); + case EVENT_METERED_DENYLIST_CHANGED: + return getMeteredDenylistChangedLog(data.ifield1, data.bfield1); default: return String.valueOf(data.type); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 60962b122fe7..48ad22cb12ff 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -4864,7 +4864,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", isAllowed=" + isAllowed + ", isRestrictedByAdmin=" + isRestrictedByAdmin + ", oldBlockedState=" + previousUidBlockedState.toString() - + ", newBlockedState=" + + ", newBlockedState=" + uidBlockedState.toString() + ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString( uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) + ", oldBlockedMeteredEffectiveReasons=" @@ -5420,6 +5420,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "setMeteredNetworkDenylist " + uid + ": " + enable); try { mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable); + mLogger.meteredAllowlistChanged(uid, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { @@ -5431,6 +5432,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "setMeteredNetworkAllowlist " + uid + ": " + enable); try { mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable); + mLogger.meteredDenylistChanged(uid, enable); } catch (IllegalStateException e) { Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e); } catch (RemoteException e) { @@ -5563,7 +5565,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); + mLogger.meteredAllowlistChanged(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); + mLogger.meteredDenylistChanged(uid, false); } catch (IllegalStateException e) { Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 16b5fb1e4d7a..a711b442c450 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -775,7 +775,7 @@ public class NotificationManagerService extends SystemService { ArraySet<String> defaultDnds = mConditionProviders.getDefaultPackages(); for (int i = 0; i < defaultDnds.size(); i++) { - allowDndPackage(defaultDnds.valueAt(i)); + allowDndPackage(userId, defaultDnds.valueAt(i)); } setDefaultAssistantForUser(userId); @@ -875,9 +875,9 @@ public class NotificationManagerService extends SystemService { } } - private void allowDndPackage(String packageName) { + private void allowDndPackage(int userId, String packageName) { try { - getBinderService().setNotificationPolicyAccessGranted(packageName, true); + getBinderService().setNotificationPolicyAccessGrantedForUser(packageName, userId, true); } catch (RemoteException e) { e.printStackTrace(); } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 66c7c5080114..bbdea32bed59 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -478,6 +478,7 @@ public final class NotificationRecord { pw.println(prefix + "opPkg=" + getSbn().getOpPkg()); pw.println(prefix + "icon=" + notification.getSmallIcon()); pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags)); + pw.println(prefix + "originalFlags=0x" + Integer.toHexString(mOriginalFlags)); pw.println(prefix + "pri=" + notification.priority); pw.println(prefix + "key=" + getSbn().getKey()); pw.println(prefix + "seen=" + mStats.hasSeen()); @@ -544,6 +545,7 @@ public final class NotificationRecord { if (notification == null) { pw.println(prefix + "None"); return; + } pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent); pw.println(prefix + "contentIntent=" + notification.contentIntent); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 9b1005880e07..6a2b2d582458 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -348,6 +348,13 @@ public abstract class ApexManager { public abstract String getApexModuleNameForPackageName(String apexPackageName); /** + * Returns the package name of the active APEX whose name is {@code apexModuleName}. If not + * found, returns {@code null}. + */ + @Nullable + public abstract String getActivePackageNameForApexModuleName(String apexModuleName); + + /** * Copies the CE apex data directory for the given {@code userId} to a backup location, for use * in case of rollback. * @@ -485,6 +492,12 @@ public abstract class ApexManager { private ArrayMap<String, String> mPackageNameToApexModuleName; /** + * Reverse mapping of {@link #mPackageNameToApexModuleName}, for active packages only. + */ + @GuardedBy("mLock") + private ArrayMap<String, String> mApexModuleNameToActivePackageName; + + /** * Whether an APEX package is active or not. * * @param packageInfo the package to check @@ -552,6 +565,7 @@ public abstract class ApexManager { try { mAllPackagesCache = new ArrayList<>(); mPackageNameToApexModuleName = new ArrayMap<>(); + mApexModuleNameToActivePackageName = new ArrayMap<>(); allPkgs = waitForApexService().getAllPackages(); } catch (RemoteException re) { Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); @@ -634,6 +648,13 @@ public abstract class ApexManager { + packageInfo.packageName); } activePackagesSet.add(packageInfo.packageName); + if (mApexModuleNameToActivePackageName.containsKey(ai.moduleName)) { + throw new IllegalStateException( + "Two active packages have the same APEX module name: " + + ai.moduleName); + } + mApexModuleNameToActivePackageName.put( + ai.moduleName, packageInfo.packageName); } if (ai.isFactory) { // Don't throw when the duplicating APEX is VNDK APEX @@ -967,6 +988,16 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActivePackageNameForApexModuleName(String apexModuleName) { + synchronized (mLock) { + Preconditions.checkState(mApexModuleNameToActivePackageName != null, + "APEX packages have not been scanned"); + return mApexModuleNameToActivePackageName.get(apexModuleName); + } + } + + @Override public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { String apexModuleName; synchronized (mLock) { @@ -1391,6 +1422,12 @@ public abstract class ApexManager { } @Override + @Nullable + public String getActivePackageNameForApexModuleName(String apexModuleName) { + return null; + } + + @Override public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { throw new UnsupportedOperationException(); } diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java index 06405ae32fcf..6dbe9b6f89f6 100644 --- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java +++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java @@ -30,7 +30,6 @@ import static com.android.server.pm.PackageManagerService.SCAN_NO_DEX; import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN; import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS; import static com.android.server.pm.PackageManagerService.TAG; -import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS; import android.annotation.Nullable; import android.content.pm.parsing.ApkLiteParseUtils; @@ -278,9 +277,8 @@ final class InitAndSystemPackageHelper { packageParser, executorService); } - List<File> frameworkSplits = getFrameworkResApkSplitFiles(); - scanDirTracedLI(frameworkDir, frameworkSplits, - mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS, + scanDirTracedLI(frameworkDir, null, + mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 4387249fa39e..cc93d4c1c3ff 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -330,6 +330,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist"; static public final String SYSTEM_DIALOG_REASON_SCREENSHOT = "screenshot"; + static public final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav"; private static final String TALKBACK_LABEL = "TalkBack"; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 66904b106952..e4a969b3ca1d 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -18,9 +18,9 @@ package com.android.server.statusbar; import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS; import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE; -import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; -import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE; -import static android.app.StatusBarManager.NavBarModeOverride; +import static android.app.StatusBarManager.NAV_BAR_MODE_DEFAULT; +import static android.app.StatusBarManager.NAV_BAR_MODE_KIDS; +import static android.app.StatusBarManager.NavBarMode; import static android.app.StatusBarManager.SessionFlags; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY; @@ -1945,26 +1945,25 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } /** - * Sets or removes the navigation bar mode override. + * Sets or removes the navigation bar mode. * - * @param navBarModeOverride the mode of the navigation bar override to be set. + * @param navBarMode the mode of the navigation bar to be set. */ - public void setNavBarModeOverride(@NavBarModeOverride int navBarModeOverride) { + public void setNavBarMode(@NavBarMode int navBarMode) { enforceStatusBar(); - if (navBarModeOverride != NAV_BAR_MODE_OVERRIDE_NONE - && navBarModeOverride != NAV_BAR_MODE_OVERRIDE_KIDS) { + if (navBarMode != NAV_BAR_MODE_DEFAULT && navBarMode != NAV_BAR_MODE_KIDS) { throw new UnsupportedOperationException( - "Supplied navBarModeOverride not supported: " + navBarModeOverride); + "Supplied navBarMode not supported: " + navBarMode); } final int userId = mCurrentUserId; final long userIdentity = Binder.clearCallingIdentity(); try { Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId); + Settings.Secure.NAV_BAR_KIDS_MODE, navBarMode, userId); IOverlayManager overlayManager = getOverlayManager(); - if (overlayManager != null && navBarModeOverride == NAV_BAR_MODE_OVERRIDE_KIDS + if (overlayManager != null && navBarMode == NAV_BAR_MODE_KIDS && isPackageSupported(NAV_BAR_MODE_3BUTTON_OVERLAY)) { overlayManager.setEnabledExclusiveInCategory(NAV_BAR_MODE_3BUTTON_OVERLAY, userId); } @@ -1976,14 +1975,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } /** - * Gets the navigation bar mode override. Returns default value if no override is set. + * Gets the navigation bar mode. Returns default value if no mode is set. * * @hide */ - public @NavBarModeOverride int getNavBarModeOverride() { + public @NavBarMode int getNavBarMode() { enforceStatusBar(); - int navBarKidsMode = NAV_BAR_MODE_OVERRIDE_NONE; + int navBarKidsMode = NAV_BAR_MODE_DEFAULT; final int userId = mCurrentUserId; final long userIdentity = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java index 1dea3d7943d8..fa243c023a87 100644 --- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -17,6 +17,7 @@ package com.android.server.trust; import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_DISPLAY_MESSAGE; +import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE; import android.annotation.TargetApi; import android.app.AlarmManager; @@ -158,7 +159,7 @@ public class TrustAgentWrapper { mMessage = (CharSequence) msg.obj; int flags = msg.arg1; mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0; - if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) { + if ((flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) { mWaitingForTrustableDowngrade = true; } else { mWaitingForTrustableDowngrade = false; @@ -638,6 +639,11 @@ public class TrustAgentWrapper { return mTrustable && mManagingTrust && !mTrustDisabledByDpm; } + /** Set the trustagent as not trustable */ + public void setUntrustable() { + mTrustable = false; + } + public boolean isManagingTrust() { return mManagingTrust && !mTrustDisabledByDpm; } diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index b4c54f9beeb7..bd4b8d15f453 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -16,6 +16,8 @@ package com.android.server.trust; +import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE; + import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; @@ -127,13 +129,16 @@ public class TrustManagerService extends SystemService { private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13; private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14; private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15; - public static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH = 17; private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except"; private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000; private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser"; private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000; + private static final long TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS = 8 * 60 * 60 * 1000; + private static final long TRUSTABLE_TIMEOUT_IN_MILLIS = 24 * 60 * 60 * 1000; private static final String PRIV_NAMESPACE = "http://schemas.android.com/apk/prv/res/android"; @@ -203,9 +208,18 @@ public class TrustManagerService extends SystemService { @GuardedBy("mUsersUnlockedByBiometric") private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray(); - private final ArrayMap<Integer, TrustTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser = + private enum TimeoutType { + TRUSTED, + TRUSTABLE + } + private final ArrayMap<Integer, TrustedTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser = new ArrayMap<>(); + private final SparseArray<TrustableTimeoutAlarmListener> mTrustableTimeoutAlarmListenerForUser = + new SparseArray<>(); + private final SparseArray<TrustableTimeoutAlarmListener> + mIdleTrustableTimeoutAlarmListenerForUser = new SparseArray<>(); private AlarmManager mAlarmManager; + private final Object mAlarmLock = new Object(); private final SettingsObserver mSettingsObserver; private final StrongAuthTracker mStrongAuthTracker; @@ -258,7 +272,7 @@ public class TrustManagerService extends SystemService { private final boolean mIsAutomotive; private final ContentResolver mContentResolver; - private boolean mTrustAgentsExtendUnlock; + private boolean mTrustAgentsNonrenewableTrust; private boolean mLockWhenTrustLost; /** @@ -295,11 +309,11 @@ public class TrustManagerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) { - // Smart lock should only extend unlock. The only exception is for automotive, - // where it can actively unlock the head unit. + // Smart lock should only grant non-renewable trust. The only exception is for + // automotive, where it can actively unlock the head unit. int defaultValue = mIsAutomotive ? 0 : 1; - mTrustAgentsExtendUnlock = + mTrustAgentsNonrenewableTrust = Settings.Secure.getIntForUser( mContentResolver, Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK, @@ -315,8 +329,8 @@ public class TrustManagerService extends SystemService { } } - boolean getTrustAgentsExtendUnlock() { - return mTrustAgentsExtendUnlock; + boolean getTrustAgentsNonrenewableTrust() { + return mTrustAgentsNonrenewableTrust; } boolean getLockWhenTrustLost() { @@ -339,36 +353,53 @@ public class TrustManagerService extends SystemService { // If active unlocking is not allowed, cancel any pending trust timeouts because the // screen is already locked. - TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); - if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) { + TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); + if (alarm != null && mSettingsObserver.getTrustAgentsNonrenewableTrust()) { mAlarmManager.cancel(alarm); alarm.setQueued(false /* isQueued */); } } } - private void scheduleTrustTimeout(int userId, boolean override) { + private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) { int shouldOverride = override ? 1 : 0; - if (override) { - shouldOverride = 1; + int trustableTimeout = isTrustableTimeout ? 1 : 0; + mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, shouldOverride, + trustableTimeout).sendToTarget(); + } + + private void handleScheduleTrustTimeout(boolean shouldOverride, TimeoutType timeoutType) { + int userId = mCurrentUser; + if (timeoutType == TimeoutType.TRUSTABLE) { + // don't override the hard timeout unless biometric or knowledge factor authentication + // occurs which isn't where this is called from. Override the idle timeout what the + // calling function has determined. + handleScheduleTrustableTimeouts(userId, shouldOverride, + false /* overrideHardTimeout */); + } else { + handleScheduleTrustedTimeout(userId, shouldOverride); } - mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget(); } - private void handleScheduleTrustTimeout(int userId, int shouldOverride) { + /* Override both the idle and hard trustable timeouts */ + private void refreshTrustableTimers(int userId) { + handleScheduleTrustableTimeouts(userId, true /* overrideIdleTimeout */, + true /* overrideHardTimeout */); + } + + private void handleScheduleTrustedTimeout(int userId, boolean shouldOverride) { long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS; - userId = mCurrentUser; - TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); + TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); // Cancel existing trust timeouts for this user if needed. if (alarm != null) { - if (shouldOverride == 0 && alarm.isQueued()) { + if (!shouldOverride && alarm.isQueued()) { if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping."); return; } mAlarmManager.cancel(alarm); } else { - alarm = new TrustTimeoutAlarmListener(userId); + alarm = new TrustedTimeoutAlarmListener(userId); mTrustTimeoutAlarmListenerForUser.put(userId, alarm); } @@ -379,6 +410,59 @@ public class TrustManagerService extends SystemService { mHandler); } + private void handleScheduleTrustableTimeouts(int userId, boolean overrideIdleTimeout, + boolean overrideHardTimeout) { + setUpIdleTimeout(userId, overrideIdleTimeout); + setUpHardTimeout(userId, overrideHardTimeout); + } + + private void setUpIdleTimeout(int userId, boolean overrideIdleTimeout) { + long when = SystemClock.elapsedRealtime() + TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS; + TrustableTimeoutAlarmListener alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId); + mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null); + + // Cancel existing trustable timeouts for this user if needed. + if (alarm != null) { + if (!overrideIdleTimeout && alarm.isQueued()) { + if (DEBUG) Slog.d(TAG, "Found existing trustable timeout alarm. Skipping."); + return; + } + mAlarmManager.cancel(alarm); + } else { + alarm = new TrustableTimeoutAlarmListener(userId); + mIdleTrustableTimeoutAlarmListenerForUser.put(userId, alarm); + } + + if (DEBUG) Slog.d(TAG, "\tSetting up trustable idle timeout alarm"); + alarm.setQueued(true /* isQueued */); + mAlarmManager.setExact( + AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm, + mHandler); + } + + private void setUpHardTimeout(int userId, boolean overrideHardTimeout) { + mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null); + TrustableTimeoutAlarmListener alarm = mTrustableTimeoutAlarmListenerForUser.get(userId); + + // if the alarm doesn't exist, or hasn't been queued, or needs to be overridden we need to + // set it + if (alarm == null || !alarm.isQueued() || overrideHardTimeout) { + // schedule hard limit on renewable trust use + long when = SystemClock.elapsedRealtime() + TRUSTABLE_TIMEOUT_IN_MILLIS; + if (alarm == null) { + alarm = new TrustableTimeoutAlarmListener(userId); + mTrustableTimeoutAlarmListenerForUser.put(userId, alarm); + } else if (overrideHardTimeout) { + mAlarmManager.cancel(alarm); + } + if (DEBUG) Slog.d(TAG, "\tSetting up trustable hard timeout alarm"); + alarm.setQueued(true /* isQueued */); + mAlarmManager.setExact( + AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm, + mHandler); + } + } + // Agent management private static final class AgentInfo { @@ -419,11 +503,11 @@ public class TrustManagerService extends SystemService { if (ENABLE_ACTIVE_UNLOCK_FLAG) { updateTrustWithRenewableUnlock(userId, flags, isFromUnlock); } else { - updateTrustWithExtendUnlock(userId, flags, isFromUnlock); + updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock); } } - private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) { + private void updateTrustWithNonrenewableTrust(int userId, int flags, boolean isFromUnlock) { boolean managed = aggregateIsTrustManaged(userId); dispatchOnTrustManagedChanged(managed, userId); if (mStrongAuthTracker.isTrustAllowedForUser(userId) @@ -441,8 +525,8 @@ public class TrustManagerService extends SystemService { boolean changed; synchronized (mUserIsTrusted) { - if (mSettingsObserver.getTrustAgentsExtendUnlock()) { - // In extend unlock trust agents can only set the device to trusted if it already + if (mSettingsObserver.getTrustAgentsNonrenewableTrust()) { + // For non-renewable trust agents can only set the device to trusted if it already // trusted or the device is unlocked. Attempting to set the device as trusted // when the device is locked will be ignored. changed = mUserIsTrusted.get(userId) != trusted; @@ -464,7 +548,7 @@ public class TrustManagerService extends SystemService { if (!trusted) { maybeLockScreen(userId); } else { - scheduleTrustTimeout(userId, false /* override */); + scheduleTrustTimeout(false /* override */, false /* isTrustableTimeout*/); } } } @@ -522,7 +606,12 @@ public class TrustManagerService extends SystemService { if (!isNowTrusted) { maybeLockScreen(userId); } else { - scheduleTrustTimeout(userId, false /* override */); + boolean isTrustableTimeout = + (flags & FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0; + // Every time we grant renewable trust we should override the idle trustable + // timeout. If this is for non-renewable trust, then we shouldn't override. + scheduleTrustTimeout(isTrustableTimeout /* override */, + isTrustableTimeout /* isTrustableTimeout */); } } } @@ -1102,8 +1191,9 @@ public class TrustManagerService extends SystemService { private void dispatchUnlockAttempt(boolean successful, int userId) { if (successful) { mStrongAuthTracker.allowTrustFromUnlock(userId); - // Allow the presence of trust on a successful unlock attempt to extend unlock. + // Allow the presence of trust on a successful unlock attempt to extend unlock updateTrust(userId, 0 /* flags */, true); + mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget(); } for (int i = 0; i < mActiveAgents.size(); i++) { @@ -1520,11 +1610,12 @@ public class TrustManagerService extends SystemService { synchronized(mUsersUnlockedByBiometric) { mUsersUnlockedByBiometric.put(userId, true); } - // In extend unlock mode we need to refresh trust state here, which will call + // In non-renewable trust mode we need to refresh trust state here, which will call // refreshDeviceLockedForUser() - int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0; + int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsNonrenewableTrust() ? 1 : 0; mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId, updateTrustOnUnlock).sendToTarget(); + mHandler.obtainMessage(MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH, userId).sendToTarget(); } @Override @@ -1643,7 +1734,13 @@ public class TrustManagerService extends SystemService { refreshDeviceLockedForUser(msg.arg1, unlockedUser); break; case MSG_SCHEDULE_TRUST_TIMEOUT: - handleScheduleTrustTimeout(msg.arg1, msg.arg2); + boolean shouldOverride = msg.arg1 == 1 ? true : false; + TimeoutType timeoutType = + msg.arg2 == 1 ? TimeoutType.TRUSTABLE : TimeoutType.TRUSTED; + handleScheduleTrustTimeout(shouldOverride, timeoutType); + break; + case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH: + refreshTrustableTimers(msg.arg1); break; } } @@ -1759,10 +1856,11 @@ public class TrustManagerService extends SystemService { // Cancel pending alarms if we require some auth anyway. if (!isTrustAllowedForUser(userId)) { TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId); - if (alarm != null && alarm.isQueued()) { - alarm.setQueued(false /* isQueued */); - mAlarmManager.cancel(alarm); - } + cancelPendingAlarm(alarm); + alarm = mTrustableTimeoutAlarmListenerForUser.get(userId); + cancelPendingAlarm(alarm); + alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId); + cancelPendingAlarm(alarm); } refreshAgentList(userId); @@ -1772,6 +1870,13 @@ public class TrustManagerService extends SystemService { updateTrust(userId, 0 /* flags */); } + private void cancelPendingAlarm(@Nullable TrustTimeoutAlarmListener alarm) { + if (alarm != null && alarm.isQueued()) { + alarm.setQueued(false /* isQueued */); + mAlarmManager.cancel(alarm); + } + } + boolean canAgentsRunForUser(int userId) { return mStartFromSuccessfulUnlock.get(userId) || super.isTrustAllowedForUser(userId); @@ -1804,9 +1909,9 @@ public class TrustManagerService extends SystemService { } } - private class TrustTimeoutAlarmListener implements OnAlarmListener { - private final int mUserId; - private boolean mIsQueued = false; + private abstract class TrustTimeoutAlarmListener implements OnAlarmListener { + protected final int mUserId; + protected boolean mIsQueued = false; TrustTimeoutAlarmListener(int userId) { mUserId = userId; @@ -1815,8 +1920,7 @@ public class TrustManagerService extends SystemService { @Override public void onAlarm() { mIsQueued = false; - int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId); - + handleAlarm(); // Only fire if trust can unlock. if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) { if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout"); @@ -1826,12 +1930,98 @@ public class TrustManagerService extends SystemService { maybeLockScreen(mUserId); } + protected abstract void handleAlarm(); + + public boolean isQueued() { + return mIsQueued; + } + public void setQueued(boolean isQueued) { mIsQueued = isQueued; } + } - public boolean isQueued() { - return mIsQueued; + private class TrustedTimeoutAlarmListener extends TrustTimeoutAlarmListener { + + TrustedTimeoutAlarmListener(int userId) { + super(userId); + } + + @Override + public void handleAlarm() { + TrustableTimeoutAlarmListener otherAlarm; + boolean otherAlarmPresent; + if (ENABLE_ACTIVE_UNLOCK_FLAG) { + otherAlarm = mTrustableTimeoutAlarmListenerForUser.get(mUserId); + otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued(); + if (otherAlarmPresent) { + synchronized (mAlarmLock) { + disableNonrenewableTrustWhileRenewableTrustIsPresent(); + } + return; + } + } + } + + private void disableNonrenewableTrustWhileRenewableTrustIsPresent() { + synchronized (mUserTrustState) { + if (mUserTrustState.get(mUserId) == TrustState.TRUSTED) { + // if we're trusted and we have a trustable alarm, we need to + // downgrade to trustable + mUserTrustState.put(mUserId, TrustState.TRUSTABLE); + updateTrust(mUserId, 0 /* flags */); + } + } + } + } + + private class TrustableTimeoutAlarmListener extends TrustTimeoutAlarmListener { + + TrustableTimeoutAlarmListener(int userId) { + super(userId); + } + + @Override + public void handleAlarm() { + TrustedTimeoutAlarmListener otherAlarm; + boolean otherAlarmPresent; + if (ENABLE_ACTIVE_UNLOCK_FLAG) { + cancelBothTrustableAlarms(); + otherAlarm = mTrustTimeoutAlarmListenerForUser.get(mUserId); + otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued(); + if (otherAlarmPresent) { + synchronized (mAlarmLock) { + disableRenewableTrustWhileNonrenewableTrustIsPresent(); + } + return; + } + } + } + + private void cancelBothTrustableAlarms() { + TrustableTimeoutAlarmListener idleTimeout = + mIdleTrustableTimeoutAlarmListenerForUser.get( + mUserId); + TrustableTimeoutAlarmListener trustableTimeout = + mTrustableTimeoutAlarmListenerForUser.get( + mUserId); + if (idleTimeout != null && idleTimeout.isQueued()) { + idleTimeout.setQueued(false); + mAlarmManager.cancel(idleTimeout); + } + if (trustableTimeout != null && trustableTimeout.isQueued()) { + trustableTimeout.setQueued(false); + mAlarmManager.cancel(trustableTimeout); + } + } + + private void disableRenewableTrustWhileNonrenewableTrustIsPresent() { + // if non-renewable trust is running, we need to temporarily prevent + // renewable trust from being used + for (AgentInfo agentInfo : mActiveAgents) { + agentInfo.agent.setUntrustable(); + } + updateTrust(mUserId, 0 /* flags */); } } } diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index 87c8a79f874a..2da29876da7a 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -62,8 +62,8 @@ import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; -import android.graphics.BitmapRegionDecoder; import android.graphics.Color; +import android.graphics.ImageDecoder; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.display.DisplayManager; @@ -199,6 +199,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub static final String WALLPAPER_LOCK_ORIG = "wallpaper_lock_orig"; static final String WALLPAPER_LOCK_CROP = "wallpaper_lock"; static final String WALLPAPER_INFO = "wallpaper_info.xml"; + private static final String RECORD_FILE = "decode_record"; + private static final String RECORD_LOCK_FILE = "decode_lock_record"; // All the various per-user state files we need to be aware of private static final String[] sPerUserFiles = new String[] { @@ -689,8 +691,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } if (DEBUG) { - // This is just a quick estimation, may be smaller than it is. - long estimateSize = options.outWidth * options.outHeight * 4; + long estimateSize = (long) options.outWidth * options.outHeight * 4; Slog.v(TAG, "Null crop of new wallpaper, estimate size=" + estimateSize + ", success=" + success); } @@ -699,9 +700,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub FileOutputStream f = null; BufferedOutputStream bos = null; try { - BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance( - wallpaper.wallpaperFile.getAbsolutePath(), false); - // This actually downsamples only by powers of two, but that's okay; we do // a proper scaling blit later. This is to minimize transient RAM use. // We calculate the largest power-of-two under the actual ratio rather than @@ -755,8 +753,24 @@ public class WallpaperManagerService extends IWallpaperManager.Stub Slog.v(TAG, " maxTextureSize=" + GLHelper.getMaxTextureSize()); } - Bitmap cropped = decoder.decodeRegion(cropHint, options); - decoder.recycle(); + //Create a record file and will delete if ImageDecoder work well. + final String recordName = + (wallpaper.wallpaperFile.getName().equals(WALLPAPER) + ? RECORD_FILE : RECORD_LOCK_FILE); + final File record = new File(getWallpaperDir(wallpaper.userId), recordName); + record.createNewFile(); + Slog.v(TAG, "record path =" + record.getPath() + + ", record name =" + record.getName()); + + final ImageDecoder.Source srcData = + ImageDecoder.createSource(wallpaper.wallpaperFile); + final int sampleSize = scale; + Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> { + decoder.setTargetSampleSize(sampleSize); + decoder.setCrop(estimateCrop); + }); + + record.delete(); if (cropped == null) { Slog.e(TAG, "Could not decode new wallpaper"); @@ -1819,6 +1833,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub new UserSwitchObserver() { @Override public void onUserSwitching(int newUserId, IRemoteCallback reply) { + errorCheck(newUserId); switchUser(newUserId, reply); } }, TAG); @@ -1856,6 +1871,14 @@ public class WallpaperManagerService extends IWallpaperManager.Stub @Override public void onBootPhase(int phase) { + // If someone set too large jpg file as wallpaper, system_server may be killed by lmk in + // generateCrop(), so we create a file in generateCrop() before ImageDecoder starts working + // and delete this file after ImageDecoder finishing. If the specific file exists, that + // means ImageDecoder can't handle the original wallpaper file, in order to avoid + // system_server restart again and again and rescue party will trigger factory reset, + // so we reset default wallpaper in case system_server is trapped into a restart loop. + errorCheck(UserHandle.USER_SYSTEM); + if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { systemReady(); } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) { @@ -1863,6 +1886,38 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + private static final HashMap<Integer, String> sWallpaperType = new HashMap<Integer, String>() { + { + put(FLAG_SYSTEM, RECORD_FILE); + put(FLAG_LOCK, RECORD_LOCK_FILE); + } + }; + + private void errorCheck(int userID) { + sWallpaperType.forEach((type, filename) -> { + final File record = new File(getWallpaperDir(userID), filename); + if (record.exists()) { + Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type + + ", wallpaper fail detect!! reset to default wallpaper"); + clearWallpaperData(userID, type); + record.delete(); + } + }); + } + + private void clearWallpaperData(int userID, int wallpaperType) { + final WallpaperData wallpaper = new WallpaperData(userID, getWallpaperDir(userID), + (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER, + (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP); + if (wallpaper.sourceExists()) { + wallpaper.wallpaperFile.delete(); + } + if (wallpaper.cropExists()) { + wallpaper.cropFile.delete(); + } + + } + @Override public void onUnlockUser(final int userId) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 0396a1101c64..8f703c5c7761 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -45,7 +45,6 @@ import static com.android.server.accessibility.AccessibilityTraceProto.WINDOW_MA import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowTracing.WINSCOPE_EXT; -import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.accessibilityservice.AccessibilityTrace; import android.animation.ObjectAnimator; @@ -101,6 +100,7 @@ import com.android.internal.util.TraceBuffer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.wm.AccessibilityWindowsPopulator.AccessibilityWindow; import com.android.server.wm.WindowManagerInternal.AccessibilityControllerInternal; import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; @@ -133,19 +133,22 @@ final class AccessibilityController { private static final Rect EMPTY_RECT = new Rect(); private static final float[] sTempFloats = new float[9]; - private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); - private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = + private final SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); + private final SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = new SparseArray<>(); private SparseArray<IBinder> mFocusedWindow = new SparseArray<>(); private int mFocusedDisplay = -1; private boolean mIsImeVisible = false; // Set to true if initializing window population complete. private boolean mAllObserversInitialized = true; + private final AccessibilityWindowsPopulator mAccessibilityWindowsPopulator; AccessibilityController(WindowManagerService service) { mService = service; mAccessibilityTracing = AccessibilityController.getAccessibilityControllerInternal(service); + + mAccessibilityWindowsPopulator = new AccessibilityWindowsPopulator(mService, this); } boolean setMagnificationCallbacks(int displayId, MagnificationCallbacks callbacks) { @@ -209,7 +212,9 @@ final class AccessibilityController { } mWindowsForAccessibilityObserver.remove(displayId); } - observer = new WindowsForAccessibilityObserver(mService, displayId, callback); + mAccessibilityWindowsPopulator.setWindowsNotification(true); + observer = new WindowsForAccessibilityObserver(mService, displayId, callback, + mAccessibilityWindowsPopulator); mWindowsForAccessibilityObserver.put(displayId, observer); mAllObserversInitialized &= observer.mInitialized; } else { @@ -224,6 +229,10 @@ final class AccessibilityController { } } mWindowsForAccessibilityObserver.remove(displayId); + + if (mWindowsForAccessibilityObserver.size() <= 0) { + mAccessibilityWindowsPopulator.setWindowsNotification(false); + } } } @@ -309,11 +318,6 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onDisplaySizeChanged(displayContent); } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } } void onAppWindowTransition(int displayId, int transition) { @@ -341,11 +345,6 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onWindowTransition(windowState, transition); } - final WindowsForAccessibilityObserver windowsForA11yObserver = - mWindowsForAccessibilityObserver.get(displayId); - if (windowsForA11yObserver != null) { - windowsForA11yObserver.scheduleComputeChangedWindows(); - } } void onWindowFocusChangedNot(int displayId) { @@ -455,6 +454,19 @@ final class AccessibilityController { return null; } + boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay", + FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId); + } + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier == null) { + return false; + } + + return displayMagnifier.getMagnificationSpec(outSpec); + } + boolean hasCallbacks() { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { @@ -756,6 +768,25 @@ final class AccessibilityController { return spec; } + boolean getMagnificationSpec(MagnificationSpec outSpec) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK); + } + MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec(); + if (spec == null) { + return false; + } + + outSpec.setTo(spec); + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec", + FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}"); + } + + return true; + } + void getMagnificationRegion(Region outMagnificationRegion) { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion", @@ -1403,20 +1434,18 @@ final class AccessibilityController { private static final boolean DEBUG = false; - private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>(); + private final List<AccessibilityWindow> mTempA11yWindows = new ArrayList<>(); private final Set<IBinder> mTempBinderSet = new ArraySet<>(); - private final RectF mTempRectF = new RectF(); - - private final Matrix mTempMatrix = new Matrix(); - private final Point mTempPoint = new Point(); private final Region mTempRegion = new Region(); private final Region mTempRegion1 = new Region(); + private final Region mTempRegion2 = new Region(); + private final WindowManagerService mService; private final Handler mHandler; @@ -1431,10 +1460,11 @@ final class AccessibilityController { // Set to true if initializing window population complete. private boolean mInitialized; + private final AccessibilityWindowsPopulator mA11yWindowsPopulator; WindowsForAccessibilityObserver(WindowManagerService windowManagerService, - int displayId, - WindowsForAccessibilityCallback callback) { + int displayId, WindowsForAccessibilityCallback callback, + AccessibilityWindowsPopulator accessibilityWindowsPopulator) { mService = windowManagerService; mCallback = callback; mDisplayId = displayId; @@ -1443,6 +1473,7 @@ final class AccessibilityController { AccessibilityController.getAccessibilityControllerInternal(mService); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); + mA11yWindowsPopulator = accessibilityWindowsPopulator; computeChangedWindows(true); } @@ -1466,52 +1497,6 @@ final class AccessibilityController { } } - boolean shellRootIsAbove(WindowState windowState, ShellRoot shellRoot) { - int wsLayer = mService.mPolicy.getWindowLayerLw(windowState); - int shellLayer = mService.mPolicy.getWindowLayerFromTypeLw(shellRoot.getWindowType(), - true); - return shellLayer >= wsLayer; - } - - int addShellRootsIfAbove(WindowState windowState, ArrayList<ShellRoot> shellRoots, - int shellRootIndex, List<WindowInfo> windows, Set<IBinder> addedWindows, - Region unaccountedSpace, boolean focusedWindowAdded) { - while (shellRootIndex < shellRoots.size() - && shellRootIsAbove(windowState, shellRoots.get(shellRootIndex))) { - ShellRoot shellRoot = shellRoots.get(shellRootIndex); - shellRootIndex++; - final WindowInfo info = shellRoot.getWindowInfo(); - if (info == null) { - continue; - } - - info.layer = addedWindows.size(); - windows.add(info); - addedWindows.add(info.token); - unaccountedSpace.op(info.regionInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - if (unaccountedSpace.isEmpty() && focusedWindowAdded) { - break; - } - } - return shellRootIndex; - } - - private ArrayList<ShellRoot> getSortedShellRoots( - SparseArray<ShellRoot> originalShellRoots) { - ArrayList<ShellRoot> sortedShellRoots = new ArrayList<>(originalShellRoots.size()); - for (int i = originalShellRoots.size() - 1; i >= 0; --i) { - sortedShellRoots.add(originalShellRoots.valueAt(i)); - } - - sortedShellRoots.sort((left, right) -> - mService.mPolicy.getWindowLayerFromTypeLw(right.getWindowType(), true) - - mService.mPolicy.getWindowLayerFromTypeLw(left.getWindowType(), - true)); - - return sortedShellRoots; - } - /** * Check if windows have changed, and send them to the accessibility subsystem if they have. * @@ -1561,44 +1546,29 @@ final class AccessibilityController { Region unaccountedSpace = mTempRegion; unaccountedSpace.set(0, 0, screenWidth, screenHeight); - final SparseArray<WindowState> visibleWindows = mTempWindowStates; - populateVisibleWindowsOnScreen(visibleWindows); + final List<AccessibilityWindow> visibleWindows = mTempA11yWindows; + mA11yWindowsPopulator.populateVisibleWindowsOnScreenLocked( + mDisplayId, visibleWindows); Set<IBinder> addedWindows = mTempBinderSet; addedWindows.clear(); boolean focusedWindowAdded = false; final int visibleWindowCount = visibleWindows.size(); - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments = new ArrayList<>(); - - ArrayList<ShellRoot> shellRoots = getSortedShellRoots(dc.mShellRoots); // Iterate until we figure out what is touchable for the entire screen. - int shellRootIndex = 0; - for (int i = visibleWindowCount - 1; i >= 0; i--) { - final WindowState windowState = visibleWindows.valueAt(i); - int prevShellRootIndex = shellRootIndex; - shellRootIndex = addShellRootsIfAbove(windowState, shellRoots, shellRootIndex, - windows, addedWindows, unaccountedSpace, focusedWindowAdded); - - // If a Shell Root was added, it could have accounted for all the space already. - if (shellRootIndex > prevShellRootIndex && unaccountedSpace.isEmpty() - && focusedWindowAdded) { - break; - } - - final Region regionInScreen = new Region(); - computeWindowRegionInScreen(windowState, regionInScreen); - if (windowMattersToAccessibility(windowState, - regionInScreen, unaccountedSpace, - skipRemainingWindowsForTaskFragments)) { - addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows); - if (windowMattersToUnaccountedSpaceComputation(windowState)) { - updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace, - skipRemainingWindowsForTaskFragments); + for (int i = 0; i < visibleWindowCount; i++) { + final AccessibilityWindow a11yWindow = visibleWindows.get(i); + final Region regionInWindow = new Region(); + a11yWindow.getTouchableRegionInWindow(regionInWindow); + if (windowMattersToAccessibility(a11yWindow, regionInWindow, + unaccountedSpace)) { + addPopulatedWindowInfo(a11yWindow, regionInWindow, windows, addedWindows); + if (windowMattersToUnaccountedSpaceComputation(a11yWindow)) { + updateUnaccountedSpace(a11yWindow, unaccountedSpace); } - focusedWindowAdded |= windowState.isFocused(); - } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) { + focusedWindowAdded |= a11yWindow.isFocused(); + } else if (a11yWindow.isUntouchableNavigationBar()) { // If this widow is navigation bar without touchable region, accounting the // region of navigation bar inset because all touch events from this region // would be received by launcher, i.e. this region is a un-touchable one @@ -1647,47 +1617,39 @@ final class AccessibilityController { // Some windows should be excluded from unaccounted space computation, though they still // should be reported - private boolean windowMattersToUnaccountedSpaceComputation(WindowState windowState) { + private boolean windowMattersToUnaccountedSpaceComputation(AccessibilityWindow a11yWindow) { // Do not account space of trusted non-touchable windows, except the split-screen // divider. // If it's not trusted, touch events are not sent to the windows behind it. - if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) - && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER) - && windowState.isTrustedOverlay()) { + if (((a11yWindow.getFlags() & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) + && (a11yWindow.getType() != TYPE_DOCK_DIVIDER) + && a11yWindow.isTrustedOverlay()) { return false; } - if (windowState.mAttrs.type - == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { + if (a11yWindow.getType() == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { return false; } return true; } - private boolean windowMattersToAccessibility(WindowState windowState, - Region regionInScreen, Region unaccountedSpace, - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) { - final RecentsAnimationController controller = mService.getRecentsAnimationController(); - if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) { + private boolean windowMattersToAccessibility(AccessibilityWindow a11yWindow, + Region regionInScreen, Region unaccountedSpace) { + if (a11yWindow.ignoreRecentsAnimationForAccessibility()) { return false; } - if (windowState.isFocused()) { + if (a11yWindow.isFocused()) { return true; } - // If the window is part of a task that we're finished with - ignore. - final TaskFragment taskFragment = windowState.getTaskFragment(); - if (taskFragment != null - && skipRemainingWindowsForTaskFragments.contains(taskFragment)) { - return false; - } - // Ignore non-touchable windows, except the split-screen divider, which is // occasionally non-touchable but still useful for identifying split-screen - // mode. - if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) - && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { + // mode and the PIP menu. + if (((a11yWindow.getFlags() + & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) + && (a11yWindow.getType() != TYPE_DOCK_DIVIDER + && !a11yWindow.isPIPMenu())) { return false; } @@ -1697,88 +1659,36 @@ final class AccessibilityController { } // Add windows of certain types not covered by modal windows. - if (isReportedWindowType(windowState.mAttrs.type)) { + if (isReportedWindowType(a11yWindow.getType())) { return true; } return false; } - private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen, - Region unaccountedSpace, - ArrayList<TaskFragment> skipRemainingWindowsForTaskFragments) { - // Account for the space this window takes if the window - // is not an accessibility overlay which does not change - // the reported windows. - unaccountedSpace.op(regionInScreen, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - - // If a window is modal it prevents other windows from being touched - if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { - if (!windowState.hasTapExcludeRegion()) { - // Account for all space in the task, whether the windows in it are - // touchable or not. The modal window blocks all touches from the task's - // area. - unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } else { - // If a window has tap exclude region, we need to account it. - final Region displayRegion = new Region(windowState.getDisplayFrame()); - final Region tapExcludeRegion = new Region(); - windowState.getTapExcludeRegion(tapExcludeRegion); - displayRegion.op(tapExcludeRegion, displayRegion, - Region.Op.REVERSE_DIFFERENCE); - unaccountedSpace.op(displayRegion, unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); - } - - final TaskFragment taskFragment = windowState.getTaskFragment(); - if (taskFragment != null) { - // If the window is associated with a particular task, we can skip the - // rest of the windows for that task. - skipRemainingWindowsForTaskFragments.add(taskFragment); - } else if (!windowState.hasTapExcludeRegion()) { - // If the window is not associated with a particular task, then it is - // globally modal. In this case we can skip all remaining windows when - // it doesn't has tap exclude region. - unaccountedSpace.setEmpty(); - } - } - - // Account for the space of letterbox. - if (windowState.areAppWindowBoundsLetterboxed()) { - unaccountedSpace.op(getLetterboxBounds(windowState), unaccountedSpace, + private void updateUnaccountedSpace(AccessibilityWindow a11yWindow, + Region unaccountedSpace) { + if (a11yWindow.getType() + != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { + // Account for the space this window takes if the window + // is not an accessibility overlay which does not change + // the reported windows. + final Region touchableRegion = mTempRegion2; + a11yWindow.getTouchableRegionInScreen(touchableRegion); + unaccountedSpace.op(touchableRegion, unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); + // Account for the space of letterbox. + final Region letterboxBounds = mTempRegion1; + if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) { + unaccountedSpace.op(letterboxBounds, + unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); + } } } - private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) { - // Get the touchable frame. - Region touchableRegion = mTempRegion1; - windowState.getTouchableRegion(touchableRegion); - - // Map the frame to get what appears on the screen. - Matrix matrix = mTempMatrix; - populateTransformationMatrix(windowState, matrix); - - forEachRect(touchableRegion, rect -> { - // Move to origin as all transforms are captured by the matrix. - RectF windowFrame = mTempRectF; - windowFrame.set(rect); - windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top); - - matrix.mapRect(windowFrame); - - // Union all rects. - outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, - (int) windowFrame.right, (int) windowFrame.bottom)); - }); - } - - private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, - List<WindowInfo> out, Set<IBinder> tokenOut) { - final WindowInfo window = windowState.getWindowInfo(); + private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow, + Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) { + final WindowInfo window = a11yWindow.getWindowInfo(); window.regionInScreen.set(regionInScreen); window.layer = tokenOut.size(); out.add(window); @@ -1805,23 +1715,6 @@ final class AccessibilityController { && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); } - private void populateVisibleWindowsOnScreen(SparseArray<WindowState> outWindows) { - final List<WindowState> tempWindowStatesList = new ArrayList<>(); - final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); - if (dc == null) { - return; - } - - dc.forAllWindows(w -> { - if (w.isVisible()) { - tempWindowStatesList.add(w); - } - }, false /* traverseTopToBottom */); - for (int i = 0; i < tempWindowStatesList.size(); i++) { - outWindows.put(i, tempWindowStatesList.get(i)); - } - } - private WindowState getTopFocusWindow() { return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; } diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java new file mode 100644 index 000000000000..c0fb83ba294c --- /dev/null +++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; + +import static com.android.server.wm.utils.RegionUtils.forEachRect; + +import android.annotation.NonNull; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Region; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.util.Slog; +import android.util.SparseArray; +import android.view.IWindow; +import android.view.InputWindowHandle; +import android.view.MagnificationSpec; +import android.view.WindowInfo; +import android.view.WindowManager; +import android.window.WindowInfosListener; + +import com.android.internal.annotations.GuardedBy; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class is the accessibility windows population adapter. + */ +public final class AccessibilityWindowsPopulator extends WindowInfosListener { + + private static final String TAG = AccessibilityWindowsPopulator.class.getSimpleName(); + // If the surface flinger callback is not coming within in 2 frames time, i.e. about + // 35ms, then assuming the windows become stable. + private static final int SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS = 35; + // To avoid the surface flinger callbacks always comes within in 2 frames, then no windows + // are reported to the A11y framework, and the animation duration time is 500ms, so setting + // this value as the max timeout value to force computing changed windows. + private static final int WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS = 500; + + private static final float[] sTempFloats = new float[9]; + + private final WindowManagerService mService; + private final AccessibilityController mAccessibilityController; + @GuardedBy("mLock") + private final SparseArray<List<InputWindowHandle>> mInputWindowHandlesOnDisplays = + new SparseArray<>(); + @GuardedBy("mLock") + private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>(); + @GuardedBy("mLock") + private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>(); + @GuardedBy("mLock") + private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>(); + @GuardedBy("mLock") + private boolean mWindowsNotificationEnabled = false; + private final Object mLock = new Object(); + private final Handler mHandler; + + AccessibilityWindowsPopulator(WindowManagerService service, + AccessibilityController accessibilityController) { + mService = service; + mAccessibilityController = accessibilityController; + mHandler = new MyHandler(mService.mH.getLooper()); + + register(); + } + + /** + * Gets the visible windows list with the window layer on the specified display. + * + * @param displayId The display. + * @param outWindows The visible windows list. The z-order of each window in the list + * is from the top to bottom. + */ + public void populateVisibleWindowsOnScreenLocked(int displayId, + List<AccessibilityWindow> outWindows) { + List<InputWindowHandle> inputWindowHandles; + final Matrix inverseMatrix = new Matrix(); + final Matrix displayMatrix = new Matrix(); + + synchronized (mLock) { + inputWindowHandles = mInputWindowHandlesOnDisplays.get(displayId); + if (inputWindowHandles == null) { + outWindows.clear(); + + return; + } + inverseMatrix.set(mMagnificationSpecInverseMatrix.get(displayId)); + + final DisplayInfo displayInfo = mDisplayInfos.get(displayId); + if (displayInfo != null) { + displayMatrix.set(displayInfo.mTransform); + } else { + Slog.w(TAG, "The displayInfo of this displayId (" + displayId + ") called " + + "back from the surface fligner is null"); + } + } + + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + final ShellRoot shellroot = dc.mShellRoots.get(WindowManager.SHELL_ROOT_LAYER_PIP); + final IBinder pipMenuIBinder = + shellroot != null ? shellroot.getAccessibilityWindowToken() : null; + + for (final InputWindowHandle windowHandle : inputWindowHandles) { + final AccessibilityWindow accessibilityWindow = + AccessibilityWindow.initializeData(mService, windowHandle, inverseMatrix, + pipMenuIBinder, displayMatrix); + + outWindows.add(accessibilityWindow); + } + } + + @Override + public void onWindowInfosChanged(InputWindowHandle[] windowHandles, + DisplayInfo[] displayInfos) { + synchronized (mLock) { + mVisibleWindows.clear(); + for (InputWindowHandle window : windowHandles) { + if (window.visible && window.getWindow() != null) { + mVisibleWindows.add(window); + } + } + + mDisplayInfos.clear(); + for (final DisplayInfo displayInfo : displayInfos) { + mDisplayInfos.put(displayInfo.mDisplayId, displayInfo); + } + + if (mWindowsNotificationEnabled) { + if (!mHandler.hasMessages( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) { + mHandler.sendEmptyMessageDelayed( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT, + WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS); + } + populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked(); + } + } + } + + /** + * Sets to notify the accessibilityController to compute changed windows on + * the display after populating the visible windows if the windows reported + * from the surface flinger changes. + * + * @param register {@code true} means starting windows population. + */ + public void setWindowsNotification(boolean register) { + synchronized (mLock) { + if (mWindowsNotificationEnabled == register) { + return; + } + mWindowsNotificationEnabled = register; + if (mWindowsNotificationEnabled) { + populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked(); + } else { + releaseResources(); + } + } + } + + private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() { + final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>(); + + for (final InputWindowHandle windowHandle : mVisibleWindows) { + List<InputWindowHandle> inputWindowHandles = tempWindowHandleList.get( + windowHandle.displayId); + + if (inputWindowHandles == null) { + inputWindowHandles = new ArrayList<>(); + tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles); + generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId); + } + inputWindowHandles.add(windowHandle); + } + + final List<Integer> displayIdsForWindowsChanged = new ArrayList<>(); + + getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList, + mInputWindowHandlesOnDisplays); + // Clones all windows from the callback of the surface flinger. + mInputWindowHandlesOnDisplays.clear(); + for (int i = 0; i < tempWindowHandleList.size(); i++) { + final int displayId = tempWindowHandleList.keyAt(i); + mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId)); + } + + if (displayIdsForWindowsChanged.size() > 0) { + if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) { + mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED, + displayIdsForWindowsChanged).sendToTarget(); + } + + return; + } + mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE); + mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE, + SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS); + } + + private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged, + SparseArray<List<InputWindowHandle>> newWindowsList, + SparseArray<List<InputWindowHandle>> oldWindowsList) { + for (int i = 0; i < newWindowsList.size(); i++) { + final int displayId = newWindowsList.keyAt(i); + final List<InputWindowHandle> newWindows = newWindowsList.get(displayId); + final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId); + + if (hasWindowsChangedLocked(newWindows, oldWindows)) { + outDisplayIdsForWindowsChanged.add(displayId); + } + } + } + + private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows, + List<InputWindowHandle> oldWindows) { + if (oldWindows == null || oldWindows.size() != newWindows.size()) { + return true; + } + + final int windowsCount = newWindows.size(); + // Since we always traverse windows from high to low layer, + // the old and new windows at the same index should be the + // same, otherwise something changed. + for (int i = 0; i < windowsCount; i++) { + final InputWindowHandle newWindow = newWindows.get(i); + final InputWindowHandle oldWindow = oldWindows.get(i); + + if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) { + return true; + } + } + + return false; + } + + private void generateMagnificationSpecInverseMatrixLocked(int displayId) { + MagnificationSpec spec = new MagnificationSpec(); + if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) { + mMagnificationSpecInverseMatrix.remove(displayId); + return; + } + sTempFloats[Matrix.MSCALE_X] = spec.scale; + sTempFloats[Matrix.MSKEW_Y] = 0; + sTempFloats[Matrix.MSKEW_X] = 0; + sTempFloats[Matrix.MSCALE_Y] = spec.scale; + sTempFloats[Matrix.MTRANS_X] = spec.offsetX; + sTempFloats[Matrix.MTRANS_Y] = spec.offsetY; + sTempFloats[Matrix.MPERSP_0] = 0; + sTempFloats[Matrix.MPERSP_1] = 0; + sTempFloats[Matrix.MPERSP_2] = 1; + + final Matrix tempMatrix = new Matrix(); + tempMatrix.setValues(sTempFloats); + + final Matrix inverseMatrix = new Matrix(); + final boolean result = tempMatrix.invert(inverseMatrix); + + if (!result) { + Slog.e(TAG, "Can't inverse the magnification spec matrix with the " + + "magnification spec = " + spec + " on the displayId = " + displayId); + return; + } + mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix); + } + + private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) { + mHandler.removeMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT); + + for (int i = 0; i < displayIdsForWindowsChanged.size(); i++) { + mAccessibilityController.performComputeChangedWindowsNot( + displayIdsForWindowsChanged.get(i), false); + } + } + + private void forceUpdateWindows() { + final List<Integer> displayIdsForWindowsChanged = new ArrayList<>(); + + synchronized (mLock) { + for (int i = 0; i < mInputWindowHandlesOnDisplays.size(); i++) { + final int displayId = mInputWindowHandlesOnDisplays.keyAt(i); + displayIdsForWindowsChanged.add(displayId); + } + } + notifyWindowsChanged(displayIdsForWindowsChanged); + } + + @GuardedBy("mLock") + private void releaseResources() { + mInputWindowHandlesOnDisplays.clear(); + mMagnificationSpecInverseMatrix.clear(); + mVisibleWindows.clear(); + mDisplayInfos.clear(); + mWindowsNotificationEnabled = false; + mHandler.removeCallbacksAndMessages(null); + } + + private class MyHandler extends Handler { + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1; + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE = 2; + public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT = 3; + + MyHandler(Looper looper) { + super(looper, null, false); + } + + @Override + public void handleMessage(Message message) { + switch (message.what) { + case MESSAGE_NOTIFY_WINDOWS_CHANGED: { + final List<Integer> displayIdsForWindowsChanged = (List<Integer>) message.obj; + notifyWindowsChanged(displayIdsForWindowsChanged); + } break; + + case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE: { + forceUpdateWindows(); + } break; + + case MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT: { + Slog.w(TAG, "Windows change within in 2 frames continuously over 500 ms " + + "and notify windows changed immediately"); + mHandler.removeMessages( + MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_UI_STABLE); + + forceUpdateWindows(); + } break; + } + } + } + + /** + * This class represents information about a window from the + * surface flinger to the accessibility framework. + */ + public static class AccessibilityWindow { + private static final Region TEMP_REGION = new Region(); + private static final RectF TEMP_RECTF = new RectF(); + // Data + private IWindow mWindow; + private int mDisplayId; + private int mFlags; + private int mType; + private int mPrivateFlags; + private boolean mIsPIPMenu; + private boolean mIsFocused; + private boolean mShouldMagnify; + private boolean mIgnoreDuetoRecentsAnimation; + private boolean mIsTrustedOverlay; + private final Region mTouchableRegionInScreen = new Region(); + private final Region mTouchableRegionInWindow = new Region(); + private final Region mLetterBoxBounds = new Region(); + private WindowInfo mWindowInfo; + + /** + * Returns the instance after initializing the internal data. + * @param service The window manager service. + * @param inputWindowHandle The window from the surface flinger. + * @param inverseMatrix The magnification spec inverse matrix. + */ + public static AccessibilityWindow initializeData(WindowManagerService service, + InputWindowHandle inputWindowHandle, Matrix inverseMatrix, IBinder pipIBinder, + Matrix displayMatrix) { + final IWindow window = inputWindowHandle.getWindow(); + final WindowState windowState = window != null ? service.mWindowMap.get( + window.asBinder()) : null; + + final AccessibilityWindow instance = new AccessibilityWindow(); + + instance.mWindow = inputWindowHandle.getWindow(); + instance.mDisplayId = inputWindowHandle.displayId; + instance.mFlags = inputWindowHandle.layoutParamsFlags; + instance.mType = inputWindowHandle.layoutParamsType; + instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder); + + // TODO (b/199357848): gets the private flag of the window from other way. + instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0; + // TODO (b/199358208) : using new way to implement the focused window. + instance.mIsFocused = windowState != null && windowState.isFocused(); + instance.mShouldMagnify = windowState == null || windowState.shouldMagnify(); + + final RecentsAnimationController controller = service.getRecentsAnimationController(); + instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null + && controller.shouldIgnoreForAccessibility(windowState); + instance.mIsTrustedOverlay = inputWindowHandle.trustedOverlay; + + // TODO (b/199358388) : gets the letterbox bounds of the window from other way. + if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) { + getLetterBoxBounds(windowState, instance.mLetterBoxBounds); + } + + final Rect windowFrame = new Rect(inputWindowHandle.frameLeft, + inputWindowHandle.frameTop, inputWindowHandle.frameRight, + inputWindowHandle.frameBottom); + getTouchableRegionInWindow(instance.mShouldMagnify, inputWindowHandle.touchableRegion, + instance.mTouchableRegionInWindow, windowFrame, inverseMatrix, displayMatrix); + getUnMagnifiedTouchableRegion(instance.mShouldMagnify, + inputWindowHandle.touchableRegion, instance.mTouchableRegionInScreen, + inverseMatrix, displayMatrix); + instance.mWindowInfo = windowState != null + ? windowState.getWindowInfo() : getWindowInfoForWindowlessWindows(instance); + + return instance; + } + + /** + * Returns the touchable region in the screen. + * @param outRegion The touchable region. + */ + public void getTouchableRegionInScreen(Region outRegion) { + outRegion.set(mTouchableRegionInScreen); + } + + /** + * Returns the touchable region in the window. + * @param outRegion The touchable region. + */ + public void getTouchableRegionInWindow(Region outRegion) { + outRegion.set(mTouchableRegionInWindow); + } + + /** + * @return the layout parameter flag {@link android.view.WindowManager.LayoutParams#flags}. + */ + public int getFlags() { + return mFlags; + } + + /** + * @return the layout parameter type {@link android.view.WindowManager.LayoutParams#type}. + */ + public int getType() { + return mType; + } + + /** + * @return the layout parameter private flag + * {@link android.view.WindowManager.LayoutParams#privateFlags}. + */ + public int getPrivateFlag() { + return mPrivateFlags; + } + + /** + * @return the windowInfo {@link WindowInfo}. + */ + public WindowInfo getWindowInfo() { + return mWindowInfo; + } + + /** + * Gets the letter box bounds if activity bounds are letterboxed + * or letterboxed for display cutout. + * + * @return {@code true} there's a letter box bounds. + */ + public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) { + if (mLetterBoxBounds.isEmpty()) { + return false; + } + + outBounds.set(mLetterBoxBounds); + return true; + } + + /** + * @return true if this window should be magnified. + */ + public boolean shouldMagnify() { + return mShouldMagnify; + } + + /** + * @return true if this window is focused. + */ + public boolean isFocused() { + return mIsFocused; + } + + /** + * @return true if it's running the recent animation but not the target app. + */ + public boolean ignoreRecentsAnimationForAccessibility() { + return mIgnoreDuetoRecentsAnimation; + } + + /** + * @return true if this window is the trusted overlay. + */ + public boolean isTrustedOverlay() { + return mIsTrustedOverlay; + } + + /** + * @return true if this window is the navigation bar with the gesture mode. + */ + public boolean isUntouchableNavigationBar() { + if (mType != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) { + return false; + } + + return mTouchableRegionInScreen.isEmpty(); + } + + /** + * @return true if this window is PIP menu. + */ + public boolean isPIPMenu() { + return mIsPIPMenu; + } + + private static void getTouchableRegionInWindow(boolean shouldMagnify, Region inRegion, + Region outRegion, Rect frame, Matrix inverseMatrix, Matrix displayMatrix) { + // Some modal windows, like the activity with Theme.dialog, has the full screen + // as its touchable region, but its window frame is smaller than the touchable + // region. The region we report should be the touchable area in the window frame + // for the consistency and match developers expectation. + // So we need to make the intersection between the frame and touchable region to + // obtain the real touch region in the screen. + Region touchRegion = TEMP_REGION; + touchRegion.set(inRegion); + touchRegion.op(frame, Region.Op.INTERSECT); + + getUnMagnifiedTouchableRegion(shouldMagnify, touchRegion, outRegion, inverseMatrix, + displayMatrix); + } + + /** + * Gets the un-magnified touchable region. If this window can be magnified and magnifying, + * we will transform the input touchable region by applying the inverse matrix of the + * magnification spec to get the un-magnified touchable region. + * @param shouldMagnify The window can be magnified. + * @param inRegion The touchable region of this window. + * @param outRegion The un-magnified touchable region of this window. + * @param inverseMatrix The inverse matrix of the magnification spec. + * @param displayMatrix The display transform matrix which takes display coordinates to + * logical display coordinates. + */ + private static void getUnMagnifiedTouchableRegion(boolean shouldMagnify, Region inRegion, + Region outRegion, Matrix inverseMatrix, Matrix displayMatrix) { + if ((!shouldMagnify || inverseMatrix.isIdentity()) && displayMatrix.isIdentity()) { + outRegion.set(inRegion); + return; + } + + forEachRect(inRegion, rect -> { + // Move to origin as all transforms are captured by the matrix. + RectF windowFrame = TEMP_RECTF; + windowFrame.set(rect); + + inverseMatrix.mapRect(windowFrame); + displayMatrix.mapRect(windowFrame); + // Union all rects. + outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom)); + }); + } + + private static WindowInfo getWindowInfoForWindowlessWindows(AccessibilityWindow window) { + WindowInfo windowInfo = WindowInfo.obtain(); + windowInfo.displayId = window.mDisplayId; + windowInfo.type = window.mType; + windowInfo.token = window.mWindow.asBinder(); + windowInfo.hasFlagWatchOutsideTouch = (window.mFlags + & WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH) != 0; + windowInfo.inPictureInPicture = false; + + // There only are two windowless windows now, one is split window, and the other + // one is PIP. + if (windowInfo.type == TYPE_DOCK_DIVIDER) { + windowInfo.title = "Splitscreen Divider"; + } else if (window.mIsPIPMenu) { + windowInfo.title = "Picture-in-Picture menu"; + } + return windowInfo; + } + + private static void getLetterBoxBounds(WindowState windowState, Region outRegion) { + final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets(); + final Rect nonLetterboxRect = windowState.getBounds(); + + nonLetterboxRect.inset(letterboxInsets); + outRegion.set(windowState.getBounds()); + outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE); + } + + @Override + public String toString() { + String builder = "A11yWindow=[" + mWindow.asBinder() + + ", displayId=" + mDisplayId + + ", flag=0x" + Integer.toHexString(mFlags) + + ", type=" + mType + + ", privateFlag=0x" + Integer.toHexString(mPrivateFlags) + + ", focused=" + mIsFocused + + ", shouldMagnify=" + mShouldMagnify + + ", ignoreDuetoRecentsAnimation=" + mIgnoreDuetoRecentsAnimation + + ", isTrustedOverlay=" + mIsTrustedOverlay + + ", regionInScreen=" + mTouchableRegionInScreen + + ", touchableRegion=" + mTouchableRegionInWindow + + ", letterBoxBounds=" + mLetterBoxBounds + + ", isPIPMenu=" + mIsPIPMenu + + ", windowInfo=" + mWindowInfo + + "]"; + + return builder; + } + } +} diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index ef0ee1206208..47bec3081dc3 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2196,8 +2196,9 @@ class ActivityStarter { // removed from calling performClearTaskLocked (For example, if it is being brought out // of history or if it is finished immediately), thus disassociating the task. Also note // that mReuseTask is reset as a result of {@link Task#performClearTaskLocked} - // launching another activity. - targetTask.performClearTaskLocked(); + // launching another activity. Keep the task-overlay activity because the targetTask + // will be reused to launch new activity. + targetTask.performClearTaskForReuse(true /* excludingTaskOverlay*/); targetTask.setIntent(mStartActivity); mAddingToTask = true; } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0 @@ -2207,8 +2208,7 @@ class ActivityStarter { // In this situation we want to remove all activities from the task up to the one // being started. In most cases this means we are resetting the task to its initial // state. - final ActivityRecord top = targetTask.performClearTaskForReuseLocked(mStartActivity, - mLaunchFlags); + final ActivityRecord top = targetTask.performClearTop(mStartActivity, mLaunchFlags); if (top != null) { if (top.isRootOfTask()) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 25c4d200d76b..cecfccd1f836 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -471,7 +471,7 @@ public abstract class ActivityTaskManagerInternal { /** Dump the current activities state. */ public abstract boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly, - boolean dumpFocusedRootTaskOnly); + boolean dumpFocusedRootTaskOnly, @UserIdInt int userId); /** Dump the current state for inclusion in oom dump. */ public abstract void dumpForOom(PrintWriter pw); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 049747728ade..fe4eae915422 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -4062,12 +4062,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { */ protected boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly, - boolean dumpFocusedRootTaskOnly) { + boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) { ArrayList<ActivityRecord> activities; synchronized (mGlobalLock) { activities = mRootWindowContainer.getDumpActivities(name, dumpVisibleRootTasksOnly, - dumpFocusedRootTaskOnly); + dumpFocusedRootTaskOnly, userId); } if (activities.size() <= 0) { @@ -6410,9 +6410,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll, boolean dumpVisibleRootTasksOnly, - boolean dumpFocusedRootTaskOnly) { + boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) { return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll, - dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly); + dumpVisibleRootTasksOnly, dumpFocusedRootTaskOnly, userId); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 3d7dead7d4c0..5573f161d1c1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1605,7 +1605,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { task.mTransitionController.requestCloseTransitionIfNeeded(task); task.mInRemoveTask = true; try { - task.performClearTask(reason); + task.removeActivities(reason, false /* excludingTaskOverlay */); cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents); mService.getLockTaskController().clearLockedTask(task); mService.getTaskChangeNotificationController().notifyTaskStackChanged(); diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index 9893f68c535b..487aff63b555 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -98,20 +98,33 @@ class BackNavigationController { HardwareBuffer screenshotBuffer = null; int prevTaskId; int prevUserId; - IOnBackInvokedCallback applicationCallback = null; - IOnBackInvokedCallback systemCallback = null; RemoteAnimationTarget topAppTarget; SurfaceControl animLeash; + IOnBackInvokedCallback callback = null; synchronized (task.mWmService.mGlobalLock) { - activityRecord = task.topRunningActivity(); - removedWindowContainer = activityRecord; - taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; - WindowState window = task.getWindow(WindowState::isFocused); + // TODO Temp workaround for Sysui until b/221071505 is fixed + WindowState window = task.mWmService.getFocusedWindowLocked(); + if (window == null) { + activityRecord = task.topRunningActivity(); + removedWindowContainer = activityRecord; + taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; + window = task.getWindow(WindowState::isFocused); + } else { + activityRecord = window.mActivityRecord; + removedWindowContainer = activityRecord; + taskWindowConfiguration = window.getWindowConfiguration(); + } + IOnBackInvokedCallback applicationCallback = null; + IOnBackInvokedCallback systemCallback = null; if (window != null) { applicationCallback = window.getApplicationOnBackInvokedCallback(); - systemCallback = window.getSystemOnBackInvokedCallback(); + callback = applicationCallback; + if (callback == null) { + systemCallback = window.getSystemOnBackInvokedCallback(); + callback = systemCallback; + } } ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " @@ -119,16 +132,25 @@ class BackNavigationController { + "systemBackCallback=%s", task, activityRecord, applicationCallback, systemCallback); + // TODO Temp workaround for Sysui until b/221071505 is fixed + if (activityRecord == null && callback != null) { + return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, + null /* topWindowLeash */, null /* screenshotSurface */, + null /* screenshotBuffer */, null /* taskWindowConfiguration */, + null /* onBackNavigationDone */, + callback /* onBackInvokedCallback */); + } + // For IME and Home, either a callback is registered, or we do nothing. In both cases, // we don't need to pass the leashes below. - if (task.getDisplayContent().getImeContainer().isVisible() + if (activityRecord == null || task.getDisplayContent().getImeContainer().isVisible() || activityRecord.isActivityTypeHome()) { - if (applicationCallback != null) { + if (callback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, null /* onBackNavigationDone */, - applicationCallback /* onBackInvokedCallback */); + callback /* onBackInvokedCallback */); } else { return null; } @@ -137,12 +159,12 @@ class BackNavigationController { prev = task.getActivity( (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity()); - if (applicationCallback != null) { + if (callback != null) { return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK, null /* topWindowLeash */, null /* screenshotSurface */, null /* screenshotBuffer */, null /* taskWindowConfiguration */, null /* onBackNavigationDone */, - applicationCallback /* onBackInvokedCallback */); + callback /* onBackInvokedCallback */); } else if (prev != null) { backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; } else if (task.returnsToHomeRootTask()) { @@ -239,8 +261,6 @@ class BackNavigationController { return null; } - final IOnBackInvokedCallback callback = - applicationCallback != null ? applicationCallback : systemCallback; RemoteCallback onBackNavigationDone = new RemoteCallback( result -> resetSurfaces(finalRemovedWindowContainer )); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index fd24565798fb..83ff2f07a06f 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5009,6 +5009,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final boolean canImeTargetSetRelativeLayer = imeTarget.getSurfaceControl() != null && imeTarget.mToken == imeControlTargetToken && !imeTarget.inMultiWindowMode() + // We don't need to set relative layer if the IME target in non-multi-window + // mode is the activity main window since updateImeParent will ensure the IME + // surface be attached on the fullscreen activity. + && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null; if (canImeTargetSetRelativeLayer) { diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 44818a8c9ee4..31ae864fc090 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -414,6 +414,17 @@ final class InputMonitor { final IBinder focusToken = focus != null ? focus.mInputChannelToken : null; if (focusToken == null) { mInputFocus = null; + // When an app is focused, but its window is not showing yet, remove the input focus + // from the current window. + if (mDisplayContent.mFocusedApp != null) { + ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "App %s is focused," + + " but the window is not ready. Start a transaction to remove focus from" + + " the window of non-focused apps.", + mDisplayContent.mFocusedApp.getName()); + EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Requesting to set focus to null window", + "reason=UpdateInputWindows"); + mInputTransaction.removeCurrentInputFocus(mDisplayId); + } return; } diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java index 8a2d11636fe3..160fc95f3f7c 100644 --- a/services/core/java/com/android/server/wm/LockTaskController.java +++ b/services/core/java/com/android/server/wm/LockTaskController.java @@ -542,7 +542,7 @@ public class LockTaskController { if (mLockTaskModeTasks.isEmpty()) { return; } - task.performClearTaskLocked(); + task.performClearTaskForReuse(false /* excludingTaskOverlay*/); mSupervisor.mRootWindowContainer.resumeFocusedTasksTopActivities(); } @@ -740,7 +740,7 @@ public class LockTaskController { ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: removing %s" + " mLockTaskAuth()=%s", lockedTask, lockedTask.lockTaskAuthToString()); removeLockedTask(lockedTask); - lockedTask.performClearTaskLocked(); + lockedTask.performClearTaskForReuse(false /* excludingTaskOverlay*/); taskChanged = true; } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 8ab2ee03a1e3..b9cd657da0e2 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -18,15 +18,9 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.KeyguardManager.ACTION_CONFIRM_DEVICE_CREDENTIAL_WITH_USER; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; @@ -2791,35 +2785,17 @@ class RootWindowContainer extends WindowContainer<DisplayContent> Task rootTask = null; // Next preference for root task goes to the taskDisplayArea candidate. - if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) { + if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null + && canLaunchOnDisplay(r, launchParams.mPreferredTaskDisplayArea.getDisplayId())) { taskDisplayArea = launchParams.mPreferredTaskDisplayArea; } - - if (taskDisplayArea == null && displayId != INVALID_DISPLAY) { - final DisplayContent displayContent = getDisplayContent(displayId); - if (displayContent != null) { - taskDisplayArea = displayContent.getDefaultTaskDisplayArea(); - } + if (taskDisplayArea == null && displayId != INVALID_DISPLAY + && canLaunchOnDisplay(r, displayId)) { + taskDisplayArea = getDisplayContent(displayId).getDefaultTaskDisplayArea(); } - if (taskDisplayArea != null) { - final int tdaDisplayId = taskDisplayArea.getDisplayId(); - if (canLaunchOnDisplay(r, tdaDisplayId)) { - if (r != null) { - final Task result = getValidLaunchRootTaskInTaskDisplayArea( - taskDisplayArea, r, candidateTask, options, launchParams); - if (result != null) { - return result; - } - } - // Falling back to default task container - taskDisplayArea = taskDisplayArea.mDisplayContent.getDefaultTaskDisplayArea(); - rootTask = taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, - sourceTask, launchParams, launchFlags, activityType, onTop); - if (rootTask != null) { - return rootTask; - } - } + return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, + sourceTask, launchParams, launchFlags, activityType, onTop); } // Give preference to the root task and display of the input task and activity if they @@ -2869,103 +2845,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> return r.canBeLaunchedOnDisplay(displayId); } - /** - * Get a topmost root task on the display area, that is a valid launch root task for - * specified activity. If there is no such root task, new dynamic root task can be created. - * - * @param taskDisplayArea Target display area. - * @param r Activity that should be launched there. - * @param candidateTask The possible task the activity might be put in. - * @return Existing root task if there is a valid one, new dynamic root task if it is valid - * or null. - */ - @VisibleForTesting - Task getValidLaunchRootTaskInTaskDisplayArea(@NonNull TaskDisplayArea taskDisplayArea, - @NonNull ActivityRecord r, @Nullable Task candidateTask, - @Nullable ActivityOptions options, - @Nullable LaunchParamsController.LaunchParams launchParams) { - if (!r.canBeLaunchedOnDisplay(taskDisplayArea.getDisplayId())) { - return null; - } - - // If {@code r} is already in target display area and its task is the same as the candidate - // task, the intention should be getting a launch root task for the reusable activity, so we - // can use the existing root task. - if (candidateTask != null) { - final TaskDisplayArea attachedTaskDisplayArea = candidateTask.getDisplayArea(); - if (attachedTaskDisplayArea == null || attachedTaskDisplayArea == taskDisplayArea) { - return candidateTask.getRootTask(); - } - // Or the candidate task is already a root task that can be reused by reparenting - // it to the target display. - if (candidateTask.isRootTask()) { - final Task rootTask = candidateTask.getRootTask(); - rootTask.reparent(taskDisplayArea, true /* onTop */); - return rootTask; - } - } - - int windowingMode; - if (launchParams != null) { - // When launch params is not null, we always defer to its windowing mode. Sometimes - // it could be unspecified, which indicates it should inherit windowing mode from - // display. - windowingMode = launchParams.mWindowingMode; - } else { - windowingMode = options != null ? options.getLaunchWindowingMode() - : r.getWindowingMode(); - } - windowingMode = taskDisplayArea.validateWindowingMode(windowingMode, r, candidateTask); - - // Return the topmost valid root task on the display. - final int targetWindowingMode = windowingMode; - final Task topmostValidRootTask = taskDisplayArea.getRootTask(rootTask -> - isValidLaunchRootTask(rootTask, r, targetWindowingMode)); - if (topmostValidRootTask != null) { - return topmostValidRootTask; - } - - // If there is no valid root task on the secondary display area - check if new dynamic root - // task will do. - if (taskDisplayArea != getDisplayContent(taskDisplayArea.getDisplayId()) - .getDefaultTaskDisplayArea()) { - final int activityType = - options != null && options.getLaunchActivityType() != ACTIVITY_TYPE_UNDEFINED - ? options.getLaunchActivityType() : r.getActivityType(); - return taskDisplayArea.createRootTask( - windowingMode, activityType, true /*onTop*/, options); - } - - return null; - } - - // TODO: Can probably be consolidated into getLaunchRootTask()... - private boolean isValidLaunchRootTask(Task task, ActivityRecord r, int windowingMode) { - switch (task.getActivityType()) { - case ACTIVITY_TYPE_HOME: - return r.isActivityTypeHome(); - case ACTIVITY_TYPE_RECENTS: - return r.isActivityTypeRecents(); - case ACTIVITY_TYPE_ASSISTANT: - return r.isActivityTypeAssistant(); - case ACTIVITY_TYPE_DREAM: - return r.isActivityTypeDream(); - } - if (task.mCreatedByOrganizer) { - // Don't launch directly into task created by organizer...but why can't we? - return false; - } - // There is a 1-to-1 relationship between root task and task when not in - // primary split-windowing mode. - if (task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - && r.supportsSplitScreenWindowingModeInDisplayArea(task.getDisplayArea()) - && (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || windowingMode == WINDOWING_MODE_UNDEFINED)) { - return true; - } - return false; - } - int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options, @Nullable Task task) { // Preference is given to the activity type for the activity then the task since the type @@ -3434,11 +3313,11 @@ class RootWindowContainer extends WindowContainer<DisplayContent> * or all visible root tasks if {@param dumpVisibleRootTasksOnly} is true. */ ArrayList<ActivityRecord> getDumpActivities(String name, boolean dumpVisibleRootTasksOnly, - boolean dumpFocusedRootTaskOnly) { + boolean dumpFocusedRootTaskOnly, @UserIdInt int userId) { if (dumpFocusedRootTaskOnly) { final Task topFocusedRootTask = getTopDisplayFocusedRootTask(); if (topFocusedRootTask != null) { - return topFocusedRootTask.getDumpActivitiesLocked(name); + return topFocusedRootTask.getDumpActivitiesLocked(name, userId); } else { return new ArrayList<>(); } @@ -3446,7 +3325,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final ArrayList<ActivityRecord> activities = new ArrayList<>(); forAllRootTasks(rootTask -> { if (!dumpVisibleRootTasksOnly || rootTask.shouldBeVisible(null)) { - activities.addAll(rootTask.getDumpActivitiesLocked(name)); + activities.addAll(rootTask.getDumpActivitiesLocked(name, userId)); } }); return activities; diff --git a/services/core/java/com/android/server/wm/ShellRoot.java b/services/core/java/com/android/server/wm/ShellRoot.java index 2eab3bab0bd3..f9d7b53e4e78 100644 --- a/services/core/java/com/android/server/wm/ShellRoot.java +++ b/services/core/java/com/android/server/wm/ShellRoot.java @@ -25,15 +25,14 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMAT import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; import android.annotation.NonNull; +import android.annotation.Nullable; import android.graphics.Point; -import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import android.view.DisplayInfo; import android.view.IWindow; import android.view.SurfaceControl; -import android.view.WindowInfo; import android.view.WindowManager; import android.view.animation.Animation; @@ -136,46 +135,12 @@ public class ShellRoot { ANIMATION_TYPE_WINDOW_ANIMATION); } - WindowInfo getWindowInfo() { - if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER - && mShellRootLayer != SHELL_ROOT_LAYER_PIP) { - return null; - } - if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) { - return null; - } - if (mShellRootLayer == SHELL_ROOT_LAYER_PIP - && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) { - return null; - } - if (mAccessibilityWindow == null) { - return null; - } - WindowInfo windowInfo = WindowInfo.obtain(); - windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId; - windowInfo.type = mToken.windowType; - windowInfo.layer = mToken.getWindowLayerFromType(); - windowInfo.token = mAccessibilityWindow.asBinder(); - windowInfo.focused = false; - windowInfo.hasFlagWatchOutsideTouch = false; - final Rect regionRect = new Rect(); - - - // DividerView - if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) { - windowInfo.inPictureInPicture = false; - mDisplayContent.getDockedDividerController().getTouchRegion(regionRect); - windowInfo.regionInScreen.set(regionRect); - windowInfo.title = "Splitscreen Divider"; - } - // PipMenuView - if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) { - windowInfo.inPictureInPicture = true; - mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect); - windowInfo.regionInScreen.set(regionRect); - windowInfo.title = "Picture-in-Picture menu"; + @Nullable + IBinder getAccessibilityWindowToken() { + if (mAccessibilityWindow != null) { + return mAccessibilityWindow.asBinder(); } - return windowInfo; + return null; } void setAccessibilityWindow(IWindow window) { @@ -196,9 +161,5 @@ public class ShellRoot { mAccessibilityWindow = null; } } - if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) { - mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved( - mDisplayContent.getDisplayId()); - } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 98c74f8ad81d..cc03c60aa6d6 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -137,6 +137,7 @@ import static java.lang.Integer.MAX_VALUE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData; @@ -1583,19 +1584,23 @@ class Task extends TaskFragment { } /** Completely remove all activities associated with an existing task. */ - void performClearTask(String reason) { + void removeActivities(String reason, boolean excludingTaskOverlay) { clearPinnedTaskIfNeed(); // Broken down into to cases to avoid object create due to capturing mStack. if (getRootTask() == null) { forAllActivities((r) -> { - if (r.finishing) return; + if (r.finishing || (excludingTaskOverlay && r.isTaskOverlay())) { + return; + } // Task was restored from persistent storage. r.takeFromHistory(); removeChild(r, reason); }); } else { forAllActivities((r) -> { - if (r.finishing) return; + if (r.finishing || (excludingTaskOverlay && r.isTaskOverlay())) { + return; + } // Prevent the transition from being executed too early if the top activity is // resumed but the mVisibleRequested of any other activity is true, the transition // should wait until next activity resumed. @@ -1612,26 +1617,24 @@ class Task extends TaskFragment { /** * Completely remove all activities associated with an existing task. */ - void performClearTaskLocked() { + void performClearTaskForReuse(boolean excludingTaskOverlay) { mReuseTask = true; mTaskSupervisor.beginDeferResume(); try { - performClearTask("clear-task-all"); + removeActivities("clear-task-all", excludingTaskOverlay); } finally { mTaskSupervisor.endDeferResume(); mReuseTask = false; } } - ActivityRecord performClearTaskForReuseLocked(ActivityRecord newR, int launchFlags) { - mReuseTask = true; + ActivityRecord performClearTop(ActivityRecord newR, int launchFlags) { mTaskSupervisor.beginDeferResume(); final ActivityRecord result; try { - result = performClearTaskLocked(newR, launchFlags); + result = clearTopActivities(newR, launchFlags); } finally { mTaskSupervisor.endDeferResume(); - mReuseTask = false; } return result; } @@ -1647,7 +1650,7 @@ class Task extends TaskFragment { * @return Returns the old activity that should be continued to be used, * or {@code null} if none was found. */ - private ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) { + private ActivityRecord clearTopActivities(ActivityRecord newR, int launchFlags) { final ActivityRecord r = findActivityInHistory(newR.mActivityComponent); if (r == null) return null; @@ -1674,7 +1677,7 @@ class Task extends TaskFragment { // Stop operation once we reach the boundary activity. if (r == boundaryActivity) return true; - if (!r.finishing) { + if (!r.finishing && !r.isTaskOverlay()) { final ActivityOptions opts = r.getOptions(); if (opts != null) { r.clearOptionsAnimation(); @@ -5691,7 +5694,7 @@ class Task extends TaskFragment { } } - ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) { + ArrayList<ActivityRecord> getDumpActivitiesLocked(String name, @UserIdInt int userId) { ArrayList<ActivityRecord> activities = new ArrayList<>(); if ("all".equals(name)) { @@ -5711,7 +5714,13 @@ class Task extends TaskFragment { } }); } - + if (userId != UserHandle.USER_ALL) { + for (int i = activities.size() - 1; i >= 0; --i) { + if (activities.get(i).mUserId != userId) { + activities.remove(i); + } + } + } return activities; } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index f0cca18eca99..2f50b14968d5 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -941,36 +941,32 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop, @Nullable Task candidateTask, @Nullable Task sourceTask, @Nullable ActivityOptions options, int launchFlags) { + final int resolvedWindowingMode = + windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode; // Need to pass in a determined windowing mode to see if a new root task should be created, // so use its parent's windowing mode if it is undefined. - if (!alwaysCreateRootTask( - windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : getWindowingMode(), - activityType)) { - Task rootTask = getRootTask(windowingMode, activityType); + if (!alwaysCreateRootTask(resolvedWindowingMode, activityType)) { + Task rootTask = getRootTask(resolvedWindowingMode, activityType); if (rootTask != null) { return rootTask; } } else if (candidateTask != null) { final int position = onTop ? POSITION_TOP : POSITION_BOTTOM; - final Task launchRootTask = getLaunchRootTask(windowingMode, activityType, options, - sourceTask, launchFlags); + final Task launchRootTask = getLaunchRootTask(resolvedWindowingMode, activityType, + options, sourceTask, launchFlags); if (launchRootTask != null) { if (candidateTask.getParent() == null) { launchRootTask.addChild(candidateTask, position); } else if (candidateTask.getParent() != launchRootTask) { candidateTask.reparent(launchRootTask, position); } - } else if (candidateTask.getDisplayArea() != this || !candidateTask.isRootTask()) { + } else if (candidateTask.getDisplayArea() != this) { if (candidateTask.getParent() == null) { addChild(candidateTask, position); } else { candidateTask.reparent(this, onTop); } } - // Update windowing mode if necessary, e.g. moving a pinned task to fullscreen. - if (candidateTask.getWindowingMode() != windowingMode) { - candidateTask.setWindowingMode(windowingMode); - } return candidateTask.getRootTask(); } return new Task.Builder(mAtmService) diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index b8ceb4a4f421..9bb02710a5bc 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -157,12 +157,26 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // display. if (launchMode == WINDOWING_MODE_UNDEFINED && canInheritWindowingModeFromSource(display, source)) { - launchMode = source.getWindowingMode(); + // The source's windowing mode may be different from its task, e.g. activity is set + // to fullscreen and its task is pinned windowing mode when the activity is entering + // pip. + launchMode = source.getTask().getWindowingMode(); if (DEBUG) { appendLog("inherit-from-source=" + WindowConfiguration.windowingModeToString(launchMode)); } } + // If the launch windowing mode is still undefined, inherit from the target task if the + // task is already on the right display area (otherwise, the task may be on a different + // display area that has incompatible windowing mode). + if (launchMode == WINDOWING_MODE_UNDEFINED + && task != null && task.getTaskDisplayArea() == suggestedDisplayArea) { + launchMode = task.getWindowingMode(); + if (DEBUG) { + appendLog("inherit-from-task=" + + WindowConfiguration.windowingModeToString(launchMode)); + } + } // hasInitialBounds is set if either activity options or layout has specified bounds. If // that's set we'll skip some adjustments later to avoid overriding the initial bounds. boolean hasInitialBounds = false; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 3a3103e752ad..bf33f86ac672 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1262,7 +1262,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe change.setAllowEnterPip(topMostActivity != null && topMostActivity.checkEnterPictureInPictureAppOpsState()); final ActivityRecord topRunningActivity = task.topRunningActivity(); - if (topRunningActivity != null && task.mDisplayContent != null) { + if (topRunningActivity != null && task.mDisplayContent != null + // Display won't be rotated for multi window Task, so the fixed rotation + // won't be applied. This can happen when the windowing mode is changed + // before the previous fixed rotation is applied. + && !task.inMultiWindowMode()) { // If Activity is in fixed rotation, its will be applied with the next rotation, // when the Task is still in the previous rotation. final int taskRotation = task.getWindowConfiguration().getDisplayRotation(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 709f885db776..5db72ee722d7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4900,7 +4900,7 @@ public class WindowManagerService extends IWindowManager.Stub } } - private WindowState getFocusedWindowLocked() { + WindowState getFocusedWindowLocked() { // Return the focused window in the focused display. return mRoot.getTopFocusedDisplayContent().mCurrentFocus; } diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index a967ea8fbf8c..de87ab9dcce0 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -1167,6 +1167,10 @@ public abstract class WindowOrientationListener { if (mRotationResolverService == null) { mRotationResolverService = LocalServices.getService( RotationResolverInternal.class); + if (mRotationResolverService == null) { + finalizeRotation(reportedRotation); + return; + } } String packageName = null; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 87ef09ee5982..26acf43cf9ed 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4992,9 +4992,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isAnimating()) { return; } - if (mWmService.mAccessibilityController.hasCallbacks()) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(getDisplayId()); - } if (!isSelfOrAncestorWindowAnimatingExit()) { return; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5aa3dfea3ea4..97ef490184d9 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -45,7 +45,8 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER; -import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID; +import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_IDS; +import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_STRING; import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; @@ -18692,10 +18693,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { sendResourceUpdatedBroadcast(EXTRA_RESOURCE_TYPE_STRING, stringIds); } - private void sendResourceUpdatedBroadcast(String resourceType, String[] resourceIds) { + private void sendResourceUpdatedBroadcast(int resourceType, String[] resourceIds) { final Intent intent = new Intent(ACTION_DEVICE_POLICY_RESOURCE_UPDATED); - intent.putExtra(EXTRA_RESOURCE_ID, resourceIds); - intent.putExtra(resourceType, /* value= */ true); + intent.putExtra(EXTRA_RESOURCE_IDS, resourceIds); + intent.putExtra(EXTRA_RESOURCE_TYPE, resourceType); intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 2d082e157692..ca67bcb7f38c 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -50,6 +50,7 @@ import android.os.UserHandle; import android.util.EventLog; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.content.PackageMonitor; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; @@ -136,10 +137,12 @@ public class MidiService extends IMidiManager.Stub { private final Object mUsbMidiLock = new Object(); // Number of times a USB MIDI 1.0 device has opened, based on the device name. + @GuardedBy("mUsbMidiLock") private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount = new HashMap<String, Integer>(); // Whether a USB MIDI device has opened, based on the device name. + @GuardedBy("mUsbMidiLock") private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>(); // UID of BluetoothMidiService @@ -768,7 +771,6 @@ public class MidiService extends IMidiManager.Stub { synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { if (device.isUidAllowed(uid)) { - deviceInfos.add(device.getDeviceInfo()); // UMP devices have protocols that are not PROTOCOL_UNKNOWN if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { if (device.getDeviceInfo().getDefaultProtocol() @@ -1247,7 +1249,7 @@ public class MidiService extends IMidiManager.Stub { pw.decreaseIndent(); } - // hold mUsbMidiLock before calling this + @GuardedBy("mUsbMidiLock") private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); if (name.length() < MIDI_LEGACY_STRING.length()) { @@ -1266,7 +1268,7 @@ public class MidiService extends IMidiManager.Stub { return false; } - // hold mUsbMidiLock before calling this + @GuardedBy("mUsbMidiLock") void addUsbMidiDeviceLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); if (name.length() < MIDI_LEGACY_STRING.length()) { @@ -1283,7 +1285,7 @@ public class MidiService extends IMidiManager.Stub { } } - // hold mUsbMidiLock before calling this + @GuardedBy("mUsbMidiLock") void removeUsbMidiDeviceLocked(MidiDeviceInfo info) { String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); if (name.length() < MIDI_LEGACY_STRING.length()) { diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java index 4e4854c6688d..d8f409dfce66 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java @@ -213,7 +213,7 @@ public class LocationProviderManagerTest { public void testProperties() { assertThat(mManager.getName()).isEqualTo(NAME); assertThat(mManager.getProperties()).isEqualTo(PROPERTIES); - assertThat(mManager.getIdentity()).isEqualTo(IDENTITY); + assertThat(mManager.getProviderIdentity()).isEqualTo(IDENTITY); assertThat(mManager.hasProvider()).isTrue(); ProviderProperties newProperties = new ProviderProperties.Builder() @@ -230,7 +230,7 @@ public class LocationProviderManagerTest { CallerIdentity newIdentity = CallerIdentity.forTest(OTHER_USER, 1, "otherpackage", "otherattribution"); mProvider.setIdentity(newIdentity); - assertThat(mManager.getIdentity()).isEqualTo(newIdentity); + assertThat(mManager.getProviderIdentity()).isEqualTo(newIdentity); mManager.setRealProvider(null); assertThat(mManager.hasProvider()).isFalse(); diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java index ab21ab05ab5f..03363a100841 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java @@ -71,9 +71,11 @@ public class RuleBinaryParserTest { private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS); private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS); + private static final String APP_CERTIFICATE_LINEAGE = + getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS); private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS); private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS); - private static final int INVALID_KEY_VALUE = 8; + private static final int INVALID_KEY_VALUE = 9; private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS); private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS); @@ -337,6 +339,40 @@ public class RuleBinaryParserTest { } @Test + public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception { + String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + String ruleBits = + START_BIT + + ATOMIC_FORMULA_START_BITS + + APP_CERTIFICATE_LINEAGE + + EQ + + IS_HASHED + + getBits(appCertificate.length(), VALUE_SIZE_BITS) + + getValueBits(appCertificate) + + DENY + + END_BIT; + byte[] ruleBytes = getBytes(ruleBits); + ByteBuffer rule = + ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); + rule.put(DEFAULT_FORMAT_VERSION_BYTES); + rule.put(ruleBytes); + + RuleParser binaryParser = new RuleBinaryParser(); + Rule expectedRule = + new Rule( + new AtomicFormula.StringAtomicFormula( + AtomicFormula.APP_CERTIFICATE_LINEAGE, + IntegrityUtils.getHexDigest( + appCertificate.getBytes(StandardCharsets.UTF_8)), + /* isHashedValue= */ true), + Rule.DENY); + + List<Rule> rules = binaryParser.parse(rule.array()); + + assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); + } + + @Test public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception { int versionCode = 1; String ruleBits = diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 5d3da43c5327..c7a903be3bd2 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -489,6 +489,20 @@ public class ApexManagerTest { assertThat(e).hasMessageThat().contains("Failed to collect certificates from "); } + @Test + public void testGetActivePackageNameForApexModuleName() throws Exception { + final String moduleName = "com.android.module_name"; + + ApexInfo[] apexInfo = createApexInfoForTestPkg(true, false); + apexInfo[0].moduleName = moduleName; + when(mApexService.getAllPackages()).thenReturn(apexInfo); + mApexManager.scanApexPackagesTraced(mPackageParser2, + ParallelPackageParser.makeExecutorService()); + + assertThat(mApexManager.getActivePackageNameForApexModuleName(moduleName)) + .isEqualTo(TEST_APEX_PKG); + } + private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) { File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); ApexInfo apexInfo = new ApexInfo(); diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java index 0e98b5e79aa0..1442f1c82d42 100644 --- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java @@ -584,55 +584,55 @@ public class StatusBarManagerServiceTest { } @Test - public void testSetNavBarModeOverride_setsOverrideModeKids() throws RemoteException { - int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; + public void testSetNavBarMode_setsModeKids() throws RemoteException { + int navBarModeKids = StatusBarManager.NAV_BAR_MODE_KIDS; - mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids); + mStatusBarManagerService.setNavBarMode(navBarModeKids); - assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride()); + assertEquals(navBarModeKids, mStatusBarManagerService.getNavBarMode()); verify(mOverlayManager).setEnabledExclusiveInCategory(anyString(), anyInt()); } @Test - public void testSetNavBarModeOverride_setsOverrideModeNone() throws RemoteException { - int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE; + public void testSetNavBarMode_setsModeNone() throws RemoteException { + int navBarModeNone = StatusBarManager.NAV_BAR_MODE_DEFAULT; - mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone); + mStatusBarManagerService.setNavBarMode(navBarModeNone); - assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride()); + assertEquals(navBarModeNone, mStatusBarManagerService.getNavBarMode()); verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); } @Test - public void testSetNavBarModeOverride_invalidInputThrowsError() throws RemoteException { - int navBarModeOverrideInvalid = -1; + public void testSetNavBarMode_invalidInputThrowsError() throws RemoteException { + int navBarModeInvalid = -1; assertThrows(UnsupportedOperationException.class, - () -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid)); + () -> mStatusBarManagerService.setNavBarMode(navBarModeInvalid)); verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); } @Test - public void testSetNavBarModeOverride_noOverlayManagerDoesNotEnable() throws RemoteException { + public void testSetNavBarMode_noOverlayManagerDoesNotEnable() throws RemoteException { mOverlayManager = null; - int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; + int navBarModeKids = StatusBarManager.NAV_BAR_MODE_KIDS; - mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids); + mStatusBarManagerService.setNavBarMode(navBarModeKids); - assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride()); + assertEquals(navBarModeKids, mStatusBarManagerService.getNavBarMode()); verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); } @Test - public void testSetNavBarModeOverride_noPackageDoesNotEnable() throws Exception { + public void testSetNavBarMode_noPackageDoesNotEnable() throws Exception { mContext.setMockPackageManager(mPackageManager); when(mPackageManager.getPackageInfo(anyString(), any(PackageManager.PackageInfoFlags.class))).thenReturn(null); - int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS; + int navBarModeKids = StatusBarManager.NAV_BAR_MODE_KIDS; - mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids); + mStatusBarManagerService.setNavBarMode(navBarModeKids); - assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride()); + assertEquals(navBarModeKids, mStatusBarManagerService.getNavBarMode()); verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt()); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java index 2794d4837101..c64ff9e128e6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java @@ -114,7 +114,7 @@ public class WindowOrientationListenerTest { } @Test - public void testSensorChanged_normalCase2() { + public void testOnSensorChanged_normalCase2() { mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); mFakeRotationResolverInternal.callbackWithFailureResult( @@ -123,6 +123,15 @@ public class WindowOrientationListenerTest { assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION); } + @Test + public void testOnSensorChanged_rotationResolverServiceIsNull_useSensorResult() { + mWindowOrientationListener.mRotationResolverService = null; + + mWindowOrientationListener.mOrientationJudge.onSensorChanged(mFakeSensorEvent); + + assertThat(mFinalizedRotation).isEqualTo(DEFAULT_SENSOR_ROTATION); + } + static final class TestableRotationResolver extends RotationResolverInternal { @Surface.Rotation RotationResolverCallbackInternal mCallback; diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 87abc53bfc5a..16c5bfec76ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -925,14 +925,10 @@ public class ActivityStarterTests extends WindowTestsBase { any(), anyBoolean(), anyBoolean(), eq(false)); } - private ActivityRecord createSingleTaskActivityOn(Task stack) { + private ActivityRecord createSingleTaskActivityOn(Task task) { final ComponentName componentName = ComponentName.createRelative( DEFAULT_COMPONENT_PACKAGE_NAME, DEFAULT_COMPONENT_PACKAGE_NAME + ".SingleTaskActivity"); - final Task task = new TaskBuilder(mSupervisor) - .setComponent(componentName) - .setParentTaskFragment(stack) - .build(); return new ActivityBuilder(mAtm) .setComponent(componentName) .setLaunchMode(LAUNCH_SINGLE_TASK) diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 0d6794685f09..72521fd06245 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -120,7 +120,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { behind.setState(ActivityRecord.State.STARTED, "test"); behind.mVisibleRequested = true; - task.performClearTask("test"); + task.removeActivities("test", false /* excludingTaskOverlay */); assertFalse(mDisplayContent.mAppTransition.isReady()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java index cc1869e72b34..9fc9489e3c2e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java @@ -557,7 +557,7 @@ public class LockTaskControllerTest { mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the task running that package should be stopped - verify(tr2).performClearTaskLocked(); + verify(tr2).performClearTaskForReuse(false /* excludingTaskOverlay*/); assertFalse(mLockTaskController.isTaskLocked(tr2)); // THEN the other task should remain locked assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); @@ -569,7 +569,7 @@ public class LockTaskControllerTest { mLockTaskController.updateLockTaskPackages(TEST_USER_ID, allowlist); // THEN the last task should be cleared, and the system should quit LockTask mode - verify(tr1).performClearTaskLocked(); + verify(tr1).performClearTaskForReuse(false /* excludingTaskOverlay*/); assertFalse(mLockTaskController.isTaskLocked(tr1)); assertEquals(LOCK_TASK_MODE_NONE, mLockTaskController.getLockTaskModeState()); verifyLockTaskStopped(times(1)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index ba6510440747..acceadf8c499 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -938,7 +938,33 @@ public class RootWindowContainerTests extends WindowTestsBase { } @Test - public void testGetValidLaunchRootTaskOnDisplayWithCandidateRootTask() { + public void testGetLaunchRootTaskOnSecondaryTaskDisplayArea() { + // Adding another TaskDisplayArea to the default display. + final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); + final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(display, + mWm, "TDA", FEATURE_VENDOR_FIRST); + display.addChild(taskDisplayArea, POSITION_BOTTOM); + + // Making sure getting the root task from the preferred TDA + LaunchParamsController.LaunchParams launchParams = + new LaunchParamsController.LaunchParams(); + launchParams.mPreferredTaskDisplayArea = taskDisplayArea; + Task root = mRootWindowContainer.getLaunchRootTask(null /* r */, null /* options */, + null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams, + 0 /* launchParams */); + assertEquals(taskDisplayArea, root.getTaskDisplayArea()); + + // Making sure still getting the root task from the preferred TDA when passing in a + // launching activity. + ActivityRecord r = new ActivityBuilder(mAtm).build(); + root = mRootWindowContainer.getLaunchRootTask(r, null /* options */, + null /* candidateTask */, null /* sourceTask */, true /* onTop */, launchParams, + 0 /* launchParams */); + assertEquals(taskDisplayArea, root.getTaskDisplayArea()); + } + + @Test + public void testGetOrCreateRootTaskOnDisplayWithCandidateRootTask() { // Create a root task with an activity on secondary display. final TestDisplayContent secondaryDisplay = new TestDisplayContent.Builder(mAtm, 300, 600).build(); @@ -947,9 +973,9 @@ public class RootWindowContainerTests extends WindowTestsBase { final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); // Make sure the root task is valid and can be reused on default display. - final Task rootTask = mRootWindowContainer.getValidLaunchRootTaskInTaskDisplayArea( - mRootWindowContainer.getDefaultTaskDisplayArea(), activity, task, - null /* options */, null /* launchParams */); + final Task rootTask = mRootWindowContainer.getDefaultTaskDisplayArea().getOrCreateRootTask( + activity, null /* options */, task, null /* sourceTask */, null /* launchParams */, + 0 /* launchFlags */, ACTIVITY_TYPE_STANDARD, true /* onTop */); assertEquals(task, rootTask); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 168c250a8c93..c0759c110039 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -575,7 +575,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { final TestDisplayContent fullscreenDisplay = createNewDisplayContent( WINDOWING_MODE_FULLSCREEN); final ActivityRecord source = createSourceActivity(fullscreenDisplay); - source.setWindowingMode(WINDOWING_MODE_FREEFORM); + source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM); assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setSource(source).calculate()); @@ -951,7 +951,7 @@ public class TaskLaunchParamsModifierTests extends WindowTestsBase { final TestDisplayContent fullscreenDisplay = createNewDisplayContent( WINDOWING_MODE_FULLSCREEN); final ActivityRecord source = createSourceActivity(fullscreenDisplay); - source.setWindowingMode(WINDOWING_MODE_FREEFORM); + source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM); final ActivityOptions options = ActivityOptions.makeBasic(); final Rect expected = new Rect(0, 0, 150, 150); diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 0f223ca037ee..eea3f844b40f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -293,7 +293,8 @@ public class ZOrderingTests extends WindowTestsBase { public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() { final WindowState appBelowImeTarget = createWindow("appBelowImeTarget"); final WindowState imeAppTarget = createWindow("imeAppTarget"); - final WindowState appAboveImeTarget = createWindow("appAboveImeTarget"); + final WindowState appAboveImeTarget = createWindow(imeAppTarget, TYPE_APPLICATION, + "appAboveImeTarget"); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.setImeControlTarget(imeAppTarget); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 0394a546388d..1eb391d60e4d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -48,6 +48,7 @@ import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; +import android.content.ContextParams; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; @@ -143,6 +144,7 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; @@ -388,16 +390,8 @@ public class TelephonyManager { @UnsupportedAppUsage public TelephonyManager(Context context, int subId) { mSubId = subId; - Context appContext = context.getApplicationContext(); - if (appContext != null) { - if (Objects.equals(context.getAttributionTag(), appContext.getAttributionTag())) { - mContext = appContext; - } else { - mContext = appContext.createAttributionContext(context.getAttributionTag()); - } - } else { - mContext = context; - } + mContext = mergeAttributionAndRenouncedPermissions(context.getApplicationContext(), + context); mSubscriptionManager = SubscriptionManager.from(mContext); } @@ -418,6 +412,34 @@ public class TelephonyManager { return sInstance; } + // This method takes the Application context and adds the attributionTag + // and renouncedPermissions from the given context. + private Context mergeAttributionAndRenouncedPermissions(Context to, Context from) { + Context contextToReturn = from; + if (to != null) { + if (!Objects.equals(from.getAttributionTag(), to.getAttributionTag())) { + contextToReturn = to.createAttributionContext(from.getAttributionTag()); + } else { + contextToReturn = to; + } + + Set<String> renouncedPermissions = + from.getAttributionSource().getRenouncedPermissions(); + if (!renouncedPermissions.isEmpty()) { + if (to.getParams() != null) { + contextToReturn = contextToReturn.createContext( + new ContextParams.Builder(to.getParams()) + .setRenouncedPermissions(renouncedPermissions).build()); + } else { + contextToReturn = contextToReturn.createContext( + new ContextParams.Builder() + .setRenouncedPermissions(renouncedPermissions).build()); + } + } + } + return contextToReturn; + } + private String getOpPackageName() { // For legacy reasons the TelephonyManager has API for getting // a static instance with no context set preventing us from @@ -448,6 +470,16 @@ public class TelephonyManager { return null; } + private Set<String> getRenouncedPermissions() { + // For legacy reasons the TelephonyManager has API for getting + // a static instance with no context set preventing us from + // getting the attribution source. + if (mContext != null) { + return mContext.getAttributionSource().getRenouncedPermissions(); + } + return Collections.emptySet(); + } + /** * Post a runnable to the BackgroundThread. * @@ -6308,8 +6340,14 @@ public class TelephonyManager { (TelephonyRegistryManager) mContext.getSystemService(Context.TELEPHONY_REGISTRY_SERVICE); if (telephonyRegistry != null) { - telephonyRegistry.listenFromListener(mSubId, getOpPackageName(), - getAttributionTag(), listener, events, notifyNow); + Set<String> renouncedPermissions = getRenouncedPermissions(); + boolean renounceFineLocationAccess = renouncedPermissions + .contains(Manifest.permission.ACCESS_FINE_LOCATION); + boolean renounceCoarseLocationAccess = renouncedPermissions + .contains(Manifest.permission.ACCESS_COARSE_LOCATION); + telephonyRegistry.listenFromListener(mSubId, renounceFineLocationAccess, + renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag(), + listener, events, notifyNow); } else { Rlog.w(TAG, "telephony registry not ready."); } @@ -12132,7 +12170,10 @@ public class TelephonyManager { }) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public @Nullable ServiceState getServiceState() { - return getServiceState(false, false); + return getServiceState(getRenouncedPermissions() + .contains(Manifest.permission.ACCESS_FINE_LOCATION), + getRenouncedPermissions() + .contains(Manifest.permission.ACCESS_COARSE_LOCATION)); } /** @@ -12144,6 +12185,11 @@ public class TelephonyManager { * If you want continuous updates of service state info, register a {@link PhoneStateListener} * via {@link #listen} with the {@link PhoneStateListener#LISTEN_SERVICE_STATE} event. * + * There's another way to renounce permissions with a custom context + * {@code AttributionSource.Builder#setRenouncedPermissions(Set<String>)} but only for system + * apps. To avoid confusion, calling this method supersede renouncing permissions with a + * custom context. + * * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}. @@ -12187,8 +12233,7 @@ public class TelephonyManager { ITelephony service = getITelephony(); if (service != null) { return service.getServiceStateForSubscriber(subId, renounceFineLocationAccess, - renounceCoarseLocationAccess, - getOpPackageName(), getAttributionTag()); + renounceCoarseLocationAccess, getOpPackageName(), getAttributionTag()); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e); @@ -16123,7 +16168,10 @@ public class TelephonyManager { */ public void registerTelephonyCallback(@NonNull @CallbackExecutor Executor executor, @NonNull TelephonyCallback callback) { - registerTelephonyCallback(false, false, executor, callback); + registerTelephonyCallback( + getRenouncedPermissions().contains(Manifest.permission.ACCESS_FINE_LOCATION), + getRenouncedPermissions().contains(Manifest.permission.ACCESS_COARSE_LOCATION), + executor, callback); } /** @@ -16153,6 +16201,12 @@ public class TelephonyManager { * instability. If a process has registered too many callbacks without unregistering them, it * may encounter an {@link IllegalStateException} when trying to register more callbacks. * + * <p> + * There's another way to renounce permissions with a custom context + * {@code AttributionSource.Builder#setRenouncedPermissions(Set<String>)} but only for system + * apps. To avoid confusion, calling this method supersede renouncing permissions with a + * custom context. + * * @param renounceFineLocationAccess Set this to true if the caller would not like to receive * location related information which will be sent if the caller already possess * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and do not renounce the permissions. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index f21b1d63d477..c89e6a44ab6c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.quickswitch import android.app.Instrumentation -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.platform.test.annotations.RequiresDevice import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -66,6 +66,7 @@ import org.junit.runners.Parameterized @Group1 open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val taplInstrumentation = LauncherInstrumentation() private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -81,6 +82,10 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { + test { + taplInstrumentation.setExpectedRotation(testSpec.startRotation) + } + eachRun { testApp1.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(testApp1.component) @@ -90,20 +95,10 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa } } transitions { - // Swipe right from bottom to quick switch back - // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle - // as to not accidentally trigger a swipe back or forward action which would result - // in the same behavior but not testing quick swap. - device.swipe( - startDisplayBounds.bounds.right / 3, - startDisplayBounds.bounds.bottom, - 2 * startDisplayBounds.bounds.right / 3, - startDisplayBounds.bounds.bottom, - if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30 - ) - + taplInstrumentation.launchedAppState.quickSwitchToPreviousApp() wmHelper.waitForFullScreenApp(testApp1.component) wmHelper.waitForAppTransitionIdle() + wmHelper.waitForNavBarStatusBarVisible() } teardown { @@ -119,7 +114,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that the transition starts with [testApp2]'s windows filling/covering exactly the * entirety of the display. */ - @Postsubmit + @Presubmit @Test fun startsWithApp2WindowsCoverFullScreen() { testSpec.assertWmStart { @@ -131,7 +126,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that the transition starts with [testApp2]'s layers filling/covering exactly the * entirety of the display. */ - @Postsubmit + @Presubmit @Test fun startsWithApp2LayersCoverFullScreen() { testSpec.assertLayersStart { @@ -154,7 +149,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp1] windows fill the entire screen (i.e. is "fullscreen") at the end of the * transition once we have fully quick switched from [testApp2] back to the [testApp1]. */ - @Postsubmit + @Presubmit @Test fun endsWithApp1WindowsCoveringFullScreen() { testSpec.assertWmEnd { @@ -166,7 +161,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp1] layers fill the entire screen (i.e. is "fullscreen") at the end of the * transition once we have fully quick switched from [testApp2] back to the [testApp1]. */ - @Postsubmit + @Presubmit @Test fun endsWithApp1LayersCoveringFullScreen() { testSpec.assertLayersEnd { @@ -178,7 +173,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp1] is the top window at the end of the transition once we have fully quick * switched from [testApp2] back to the [testApp1]. */ - @Postsubmit + @Presubmit @Test fun endsWithApp1BeingOnTop() { testSpec.assertWmEnd { @@ -190,7 +185,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp1]'s window starts off invisible and becomes visible at some point before * the end of the transition and then stays visible until the end of the transition. */ - @Postsubmit + @Presubmit @Test fun app1WindowBecomesAndStaysVisible() { testSpec.assertWm { @@ -206,7 +201,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp1]'s layer starts off invisible and becomes visible at some point before * the end of the transition and then stays visible until the end of the transition. */ - @Postsubmit + @Presubmit @Test fun app1LayerBecomesAndStaysVisible() { testSpec.assertLayers { @@ -220,7 +215,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp2]'s window starts off visible and becomes invisible at some point before * the end of the transition and then stays invisible until the end of the transition. */ - @Postsubmit + @Presubmit @Test fun app2WindowBecomesAndStaysInvisible() { testSpec.assertWm { @@ -234,7 +229,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Checks that [testApp2]'s layer starts off visible and becomes invisible at some point before * the end of the transition and then stays invisible until the end of the transition. */ - @Postsubmit + @Presubmit @Test fun app2LayerBecomesAndStaysInvisible() { testSpec.assertLayers { @@ -249,7 +244,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially * visible. */ - @Postsubmit + @Presubmit @Test fun app1WindowIsVisibleOnceApp2WindowIsInvisible() { testSpec.assertWm { @@ -269,7 +264,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa * Ensures that at any point, either [testApp1] or [testApp2]'s windows are at least partially * visible. */ - @Postsubmit + @Presubmit @Test fun app1LayerIsVisibleOnceApp2LayerIsInvisible() { testSpec.assertLayers { @@ -286,7 +281,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(private val testSpec: FlickerTestPa /** * Checks that the navbar window is visible throughout the entire transition. */ - @Postsubmit + @Presubmit @Test fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsVisible() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index ce6a3837ad01..5d172e2c1b14 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -22,6 +22,7 @@ import android.platform.test.annotations.RequiresDevice import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -65,6 +66,7 @@ import org.junit.runners.Parameterized @Group1 open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val taplInstrumentation = LauncherInstrumentation() private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) @@ -73,6 +75,10 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { + test { + taplInstrumentation.setExpectedRotation(testSpec.startRotation) + } + eachRun { testApp1.launchViaIntent(wmHelper) wmHelper.waitForFullScreenApp(testApp1.component) @@ -85,43 +91,24 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes ?.layerStackSpace ?: error("Display not found") - // Swipe right from bottom to quick switch back - // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the - // middle as to not accidentally trigger a swipe back or forward action which - // would result in the same behavior but not testing quick swap. - device.swipe( - startDisplayBounds.right / 3, - startDisplayBounds.bottom, - 2 * startDisplayBounds.right / 3, - startDisplayBounds.bottom, - if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30 - ) + taplInstrumentation.launchedAppState.quickSwitchToPreviousApp() wmHelper.waitForFullScreenApp(testApp1.component) wmHelper.waitForAppTransitionIdle() } } transitions { - // Swipe left from bottom to quick switch forward - // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle - // as to not accidentally trigger a swipe back or forward action which would result - // in the same behavior but not testing quick swap. - device.swipe( - 2 * startDisplayBounds.right / 3, - startDisplayBounds.bottom, - startDisplayBounds.right / 3, - startDisplayBounds.bottom, - if (testSpec.isLandscapeOrSeascapeAtStart) 75 else 30 - ) + taplInstrumentation.launchedAppState.quickSwitchToPreviousAppSwipeLeft() wmHelper.waitForFullScreenApp(testApp2.component) wmHelper.waitForAppTransitionIdle() + wmHelper.waitForNavBarStatusBarVisible() } teardown { test { - testApp1.exit() - testApp2.exit() + testApp1.exit(wmHelper) + testApp2.exit(wmHelper) } } } @@ -365,4 +352,4 @@ open class QuickSwitchBetweenTwoAppsForwardTest(private val testSpec: FlickerTes ) } } -}
\ No newline at end of file +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index 1a762bfd97c5..e5e240485240 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -22,6 +22,7 @@ import android.platform.test.annotations.RequiresDevice import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -62,13 +63,20 @@ import org.junit.runners.Parameterized @Group4 class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + private val taplInstrumentation = LauncherInstrumentation() + private val testApp = SimpleAppHelper(instrumentation) + private val startDisplayBounds = WindowUtils.getDisplayBounds(testSpec.startRotation) @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { + test { + taplInstrumentation.setExpectedRotation(testSpec.startRotation) + } + eachRun { testApp.launchViaIntent(wmHelper) device.pressHome() @@ -77,20 +85,10 @@ class QuickSwitchFromLauncherTest(private val testSpec: FlickerTestParameter) { } } transitions { - // Swipe right from bottom to quick switch back - // NOTE: We don't perform an edge-to-edge swipe but instead only swipe in the middle - // as to not accidentally trigger a swipe back or forward action which would result - // in the same behavior but not testing quick swap. - device.swipe( - startDisplayBounds.bounds.right / 3, - startDisplayBounds.bounds.bottom, - 2 * startDisplayBounds.bounds.right / 3, - startDisplayBounds.bounds.bottom, - 50 - ) - + taplInstrumentation.workspace.quickSwitchToPreviousApp() wmHelper.waitForFullScreenApp(testApp.component) wmHelper.waitForAppTransitionIdle() + wmHelper.waitForNavBarStatusBarVisible() } teardown { diff --git a/tests/InputMethodStressTest/AndroidTest.xml b/tests/InputMethodStressTest/AndroidTest.xml index b194010a985a..fc54ca645a2e 100644 --- a/tests/InputMethodStressTest/AndroidTest.xml +++ b/tests/InputMethodStressTest/AndroidTest.xml @@ -18,6 +18,11 @@ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="adb shell settings put secure show_ime_with_hard_keyboard 1" /> + <option name="teardown-command" value="adb shell settings delete secure show_ime_with_hard_keyboard" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="InputMethodStressTest.apk" /> diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp index f95ea117a96e..6c68e0b0ff6b 100644 --- a/tools/bit/command.cpp +++ b/tools/bit/command.cpp @@ -192,10 +192,11 @@ exec_with_path_search(const char* prog, char const* const* argv, char const* con if (strchr(prog, '/') != NULL) { return execve(prog, (char*const*)argv, (char*const*)envp); } else { - char* pathEnv = strdup(getenv("PATH")); - if (pathEnv == NULL) { + const char* pathEnvRaw = getenv("PATH"); + if (pathEnvRaw == NULL) { return 1; } + char* pathEnv = strdup(pathEnvRaw); char* dir = pathEnv; while (dir) { char* next = strchr(dir, ':'); |