Merge "Add conflate to the sudio tream flow so it doesn't get overwhelmed by the old changes" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 8591a9c..6ecd38f 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -21,6 +21,7 @@
// !!! KEEP THIS LIST ALPHABETICAL !!!
"aconfig_mediacodec_flags_java_lib",
"android.adaptiveauth.flags-aconfig-java",
+ "android.app.contextualsearch.flags-aconfig-java",
"android.app.flags-aconfig-java",
"android.app.ondeviceintelligence-aconfig-java",
"android.app.smartspace.flags-aconfig-java",
@@ -974,6 +975,19 @@
],
}
+// Contextual Search
+aconfig_declarations {
+ name: "android.app.contextualsearch.flags-aconfig",
+ package: "android.app.contextualsearch.flags",
+ srcs: ["core/java/android/app/contextualsearch/flags.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.contextualsearch.flags-aconfig-java",
+ aconfig_declarations: "android.app.contextualsearch.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Smartspace
aconfig_declarations {
name: "android.app.smartspace.flags-aconfig",
diff --git a/core/api/current.txt b/core/api/current.txt
index 1478377..925cf4c 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -293,7 +293,7 @@
field public static final String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
field public static final String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
- field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_LOGO = "android.permission.SET_BIOMETRIC_DIALOG_LOGO";
+ field @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public static final String SET_BIOMETRIC_DIALOG_ADVANCED = "android.permission.SET_BIOMETRIC_DIALOG_ADVANCED";
field public static final String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
field @Deprecated public static final String SET_PREFERRED_APPLICATIONS = "android.permission.SET_PREFERRED_APPLICATIONS";
field public static final String SET_PROCESS_LIMIT = "android.permission.SET_PROCESS_LIMIT";
@@ -12487,33 +12487,33 @@
}
public class LauncherApps {
- method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
- method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
+ method @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions();
method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.IntentSender getAppMarketActivityIntent(@Nullable String, @NonNull android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @FlaggedApi("android.os.allow_private_profile") @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public final android.content.pm.LauncherUserInfo getLauncherUserInfo(@NonNull android.os.UserHandle);
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method @FlaggedApi("android.os.allow_private_profile") @NonNull @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<java.lang.String> getPreInstalledSystemPackages(@NonNull android.os.UserHandle);
- method public java.util.List<android.os.UserHandle> getProfiles();
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
method @Nullable public android.content.IntentSender getShortcutConfigActivityIntent(@NonNull android.content.pm.LauncherActivityInfo);
method public java.util.List<android.content.pm.LauncherActivityInfo> getShortcutConfigActivityList(@Nullable String, @NonNull android.os.UserHandle);
method public android.graphics.drawable.Drawable getShortcutIconDrawable(@NonNull android.content.pm.ShortcutInfo, int);
method @Nullable public android.app.PendingIntent getShortcutIntent(@NonNull String, @NonNull String, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
method @Nullable public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(@NonNull android.content.pm.LauncherApps.ShortcutQuery, @NonNull android.os.UserHandle);
- method @Nullable public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle);
+ method @Nullable @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.os.Bundle getSuspendedPackageLauncherExtras(String, android.os.UserHandle);
method public boolean hasShortcutHostPermission();
- method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
- method public boolean isPackageEnabled(String, android.os.UserHandle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean isPackageEnabled(String, android.os.UserHandle);
method public void pinShortcuts(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull android.os.UserHandle);
- method public void registerCallback(android.content.pm.LauncherApps.Callback);
- method public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void registerCallback(android.content.pm.LauncherApps.Callback);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void registerCallback(android.content.pm.LauncherApps.Callback, android.os.Handler);
method public void registerPackageInstallerSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.content.pm.PackageInstaller.SessionCallback);
- method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
method @FlaggedApi("android.content.pm.archiving") public void setArchiveCompatibility(@NonNull android.content.pm.LauncherApps.ArchiveCompatibilityParams);
- method public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle);
- method public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
- method public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public boolean shouldHideFromSuggestions(@NonNull String, @NonNull android.os.UserHandle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void startAppDetailsActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
+ method @RequiresPermission(conditional=true, anyOf={"android.permission.ACCESS_HIDDEN_PROFILES_FULL", android.Manifest.permission.ACCESS_HIDDEN_PROFILES}) public void startMainActivity(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
method public void startPackageInstallerSessionDetailsActivity(@NonNull android.content.pm.PackageInstaller.SessionInfo, @Nullable android.graphics.Rect, @Nullable android.os.Bundle);
method public void startShortcut(@NonNull String, @NonNull String, @Nullable android.graphics.Rect, @Nullable android.os.Bundle, @NonNull android.os.UserHandle);
method public void startShortcut(@NonNull android.content.pm.ShortcutInfo, @Nullable android.graphics.Rect, @Nullable android.os.Bundle);
@@ -18904,9 +18904,9 @@
method @Nullable public int getAllowedAuthenticators();
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable public android.hardware.biometrics.PromptContentView getContentView();
method @Nullable public CharSequence getDescription();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.graphics.Bitmap getLogoBitmap();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public String getLogoDescription();
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public int getLogoRes();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.graphics.Bitmap getLogoBitmap();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getLogoDescription();
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @DrawableRes @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public int getLogoRes();
method @Nullable public CharSequence getNegativeButtonText();
method @Nullable public CharSequence getSubtitle();
method @NonNull public CharSequence getTitle();
@@ -18956,9 +18956,9 @@
method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setContentView(@NonNull android.hardware.biometrics.PromptContentView);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDescription(@NonNull CharSequence);
method @Deprecated @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setDeviceCredentialAllowed(boolean);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
- method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoBitmap(@NonNull android.graphics.Bitmap);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoDescription(@NonNull String);
+ method @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.BiometricPrompt.Builder setLogoRes(@DrawableRes int);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(@NonNull CharSequence, @NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(@NonNull CharSequence);
method @NonNull public android.hardware.biometrics.BiometricPrompt.Builder setTitle(@NonNull CharSequence);
@@ -18971,6 +18971,7 @@
ctor @Deprecated public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential);
ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.PresentationSession);
ctor @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") public BiometricPrompt.CryptoObject(@NonNull javax.crypto.KeyAgreement);
+ ctor @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public BiometricPrompt.CryptoObject(long);
method @Nullable public javax.crypto.Cipher getCipher();
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
@@ -19000,6 +19001,21 @@
@FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public interface PromptContentView {
}
+ @FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptContentViewWithMoreOptionsButton implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
+ method public int describeContents();
+ method @Nullable @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public String getDescription();
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.content.DialogInterface.OnClickListener getMoreOptionsButtonListener();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.biometrics.PromptContentViewWithMoreOptionsButton> CREATOR;
+ }
+
+ public static final class PromptContentViewWithMoreOptionsButton.Builder {
+ ctor public PromptContentViewWithMoreOptionsButton.Builder();
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton build();
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setDescription(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED) public android.hardware.biometrics.PromptContentViewWithMoreOptionsButton.Builder setMoreOptionsButtonListener(@NonNull java.util.concurrent.Executor, @NonNull android.content.DialogInterface.OnClickListener);
+ }
+
@FlaggedApi("android.hardware.biometrics.custom_biometric_prompt") public final class PromptVerticalListContentView implements android.os.Parcelable android.hardware.biometrics.PromptContentView {
method public int describeContents();
method @Nullable public String getDescription();
@@ -20121,9 +20137,14 @@
ctor public OutputConfiguration(@NonNull android.view.Surface);
ctor public OutputConfiguration(int, @NonNull android.view.Surface);
ctor public <T> OutputConfiguration(@NonNull android.util.Size, @NonNull Class<T>);
+ ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, @NonNull android.util.Size);
+ ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, int, @NonNull android.util.Size);
+ ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, @NonNull android.util.Size, long);
+ ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public OutputConfiguration(int, int, @NonNull android.util.Size, long);
method public void addSensorPixelModeUsed(int);
method public void addSurface(@NonNull android.view.Surface);
method @NonNull public static java.util.Collection<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull android.hardware.camera2.MultiResolutionImageReader);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") @NonNull public static java.util.List<android.hardware.camera2.params.OutputConfiguration> createInstancesForMultiResolutionOutput(@NonNull java.util.Collection<android.hardware.camera2.params.MultiResolutionStreamInfo>, int);
method public int describeContents();
method public void enableSurfaceSharing();
method public long getDynamicRangeProfile();
@@ -20142,6 +20163,7 @@
method public void setPhysicalCameraId(@Nullable String);
method public void setReadoutTimestampEnabled(boolean);
method public void setStreamUseCase(long);
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public static void setSurfacesForMultiResolutionOutput(@NonNull java.util.Collection<android.hardware.camera2.params.OutputConfiguration>, @NonNull android.hardware.camera2.MultiResolutionImageReader);
method public void setTimestampBase(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
@@ -20203,6 +20225,7 @@
public final class SessionConfiguration implements android.os.Parcelable {
ctor public SessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, @NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback);
+ ctor @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public SessionConfiguration(int, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>);
method public void clearColorSpace();
method public int describeContents();
method @Nullable public android.graphics.ColorSpace getColorSpace();
@@ -20212,6 +20235,7 @@
method public android.hardware.camera2.CaptureRequest getSessionParameters();
method public int getSessionType();
method public android.hardware.camera2.CameraCaptureSession.StateCallback getStateCallback();
+ method @FlaggedApi("com.android.internal.camera.flags.camera_device_setup") public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback);
method public void setColorSpace(@NonNull android.graphics.ColorSpace.Named);
method public void setInputConfiguration(@NonNull android.hardware.camera2.params.InputConfiguration);
method public void setSessionParameters(android.hardware.camera2.CaptureRequest);
@@ -33960,6 +33984,7 @@
method public static boolean supportsMultipleUsers();
field public static final String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
field @Deprecated public static final String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
+ field @FlaggedApi("android.os.allow_private_profile") public static final String DISALLOW_ADD_PRIVATE_PROFILE = "no_add_private_profile";
field public static final String DISALLOW_ADD_USER = "no_add_user";
field public static final String DISALLOW_ADD_WIFI_CONFIG = "no_add_wifi_config";
field public static final String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
@@ -52193,7 +52218,7 @@
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction addTransactionCompletedListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.view.SurfaceControl.TransactionStats>);
method public void apply();
method @NonNull public android.view.SurfaceControl.Transaction clearFrameRate(@NonNull android.view.SurfaceControl);
- method @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl);
+ method @Deprecated @NonNull public android.view.SurfaceControl.Transaction clearTrustedPresentationCallback(@NonNull android.view.SurfaceControl);
method public void close();
method public int describeContents();
method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
@@ -52218,7 +52243,7 @@
method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
- method @NonNull public android.view.SurfaceControl.Transaction setTrustedPresentationCallback(@NonNull android.view.SurfaceControl, @NonNull android.view.SurfaceControl.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @Deprecated @NonNull public android.view.SurfaceControl.Transaction setTrustedPresentationCallback(@NonNull android.view.SurfaceControl, @NonNull android.view.SurfaceControl.TrustedPresentationThresholds, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
@@ -52233,8 +52258,8 @@
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.hardware.SyncFence getPresentFence();
}
- public static final class SurfaceControl.TrustedPresentationThresholds {
- ctor public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
+ @Deprecated public static final class SurfaceControl.TrustedPresentationThresholds {
+ ctor @Deprecated public SurfaceControl.TrustedPresentationThresholds(@FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @FloatRange(from=0.0f, fromInclusive=false, to=1.0f) float, @IntRange(from=1) int);
}
@FlaggedApi("com.android.window.flags.surface_control_input_receiver") public interface SurfaceControlInputReceiver {
@@ -52249,7 +52274,7 @@
method public void relayout(int, int);
method public void release();
method public void setView(@NonNull android.view.View, int, int);
- method public boolean transferTouchGestureToHost();
+ method @Deprecated public boolean transferTouchGestureToHost();
}
public static final class SurfaceControlViewHost.SurfacePackage implements android.os.Parcelable {
@@ -52308,7 +52333,7 @@
ctor public SurfaceView(android.content.Context, android.util.AttributeSet, int, int);
method public void applyTransactionToFrame(@NonNull android.view.SurfaceControl.Transaction);
method public android.view.SurfaceHolder getHolder();
- method @Nullable public android.os.IBinder getHostToken();
+ method @Deprecated @Nullable public android.os.IBinder getHostToken();
method public android.view.SurfaceControl getSurfaceControl();
method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 78ac774..9039bf1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7,6 +7,7 @@
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_BROADCAST_RESPONSE_STATS = "android.permission.ACCESS_BROADCAST_RESPONSE_STATS";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
+ field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String ACCESS_CONTEXTUAL_SEARCH = "android.permission.ACCESS_CONTEXTUAL_SEARCH";
field public static final String ACCESS_CONTEXT_HUB = "android.permission.ACCESS_CONTEXT_HUB";
field public static final String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
field @Deprecated public static final String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
@@ -198,7 +199,6 @@
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
field @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") public static final String MANAGE_DEVICE_POLICY_AUDIT_LOGGING = "android.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING";
- field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String MANAGE_DEVICE_POLICY_THEFT_DETECTION = "android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION";
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String MANAGE_ENHANCED_CONFIRMATION_STATES = "android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES";
field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
@@ -282,6 +282,7 @@
field @FlaggedApi("android.content.pm.quarantined_enabled") public static final String QUARANTINE_APPS = "android.permission.QUARANTINE_APPS";
field public static final String QUERY_ADMIN_POLICY = "android.permission.QUERY_ADMIN_POLICY";
field public static final String QUERY_CLONED_APPS = "android.permission.QUERY_CLONED_APPS";
+ field @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") public static final String QUERY_DEVICE_STOLEN_STATE = "android.permission.QUERY_DEVICE_STOLEN_STATE";
field @Deprecated public static final String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES";
field public static final String QUERY_USERS = "android.permission.QUERY_USERS";
field public static final String RADIO_SCAN_WITHOUT_LOCATION = "android.permission.RADIO_SCAN_WITHOUT_LOCATION";
@@ -1328,12 +1329,12 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public int getUserProvisioningState();
method @FlaggedApi("android.app.admin.flags.security_log_v2_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public boolean isAuditLogEnabled();
method public boolean isDeviceManaged();
+ method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE) public boolean isDevicePotentiallyStolen();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
- method @FlaggedApi("android.app.admin.flags.device_theft_api_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION) public boolean isTheftDetectionTriggered();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
@@ -2176,6 +2177,39 @@
}
+package android.app.contextualsearch {
+
+ @FlaggedApi("android.app.contextualsearch.flags.enable_service") public class ContextualSearchManager {
+ method public void getContextualSearchState(@NonNull android.os.IBinder, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.contextualsearch.ContextualSearchState,java.lang.Throwable>);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH) public void startContextualSearch(int);
+ field public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH = "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
+ field public static final int ENTRYPOINT_LONG_PRESS_HOME = 2; // 0x2
+ field public static final int ENTRYPOINT_LONG_PRESS_META = 10; // 0xa
+ field public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1; // 0x1
+ field public static final int ENTRYPOINT_LONG_PRESS_OVERVIEW = 3; // 0x3
+ field public static final int ENTRYPOINT_OVERVIEW_ACTION = 4; // 0x4
+ field public static final int ENTRYPOINT_OVERVIEW_MENU = 5; // 0x5
+ field public static final int ENTRYPOINT_SYSTEM_ACTION = 9; // 0x9
+ field public static final String EXTRA_ENTRYPOINT = "android.app.contextualsearch.extra.ENTRYPOINT";
+ field public static final String EXTRA_FLAG_SECURE_FOUND = "android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
+ field public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE = "android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
+ field public static final String EXTRA_SCREENSHOT = "android.app.contextualsearch.extra.SCREENSHOT";
+ field public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+ field public static final String EXTRA_VISIBLE_PACKAGE_NAMES = "android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
+ }
+
+ @FlaggedApi("android.app.contextualsearch.flags.enable_service") public final class ContextualSearchState implements android.os.Parcelable {
+ ctor public ContextualSearchState(@Nullable android.app.assist.AssistStructure, @Nullable android.app.assist.AssistContent, @NonNull android.os.Bundle);
+ method public int describeContents();
+ method @Nullable public android.app.assist.AssistContent getContent();
+ method @NonNull public android.os.Bundle getExtras();
+ method @Nullable public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.contextualsearch.ContextualSearchState> CREATOR;
+ }
+
+}
+
package android.app.job {
public abstract class JobScheduler {
@@ -3747,6 +3781,7 @@
field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
+ field @FlaggedApi("android.app.contextualsearch.flags.enable_service") public static final String CONTEXTUAL_SEARCH_SERVICE = "contextual_search";
field @FlaggedApi("android.permission.flags.enhanced_confirmation_mode_apis_enabled") public static final String ECM_ENHANCED_CONFIRMATION_SERVICE = "ecm_enhanced_confirmation";
field public static final String ETHERNET_SERVICE = "ethernet";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
@@ -5006,7 +5041,7 @@
}
public static interface DeviceStateManager.DeviceStateCallback {
- method public default void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
+ method public void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
method public default void onSupportedStatesChanged(@NonNull java.util.List<android.hardware.devicestate.DeviceState>);
}
@@ -12979,11 +13014,11 @@
method public long getMaximumDataBlockSize();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_PDB_STATE) public String getPersistentDataPackageName();
- method public byte[] read();
+ method @Nullable public byte[] read();
method @FlaggedApi("android.security.frp_enforcement") public boolean setFactoryResetProtectionSecret(@NonNull byte[]);
method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
- method public int write(byte[]);
+ method public int write(@Nullable byte[]);
field public static final int FLASH_LOCK_LOCKED = 1; // 0x1
field public static final int FLASH_LOCK_UNKNOWN = -1; // 0xffffffff
field public static final int FLASH_LOCK_UNLOCKED = 0; // 0x0
@@ -13936,9 +13971,8 @@
}
@FlaggedApi("com.android.server.telecom.flags.telecom_resolve_hidden_dependencies") public static final class DisconnectCause.Builder {
- ctor public DisconnectCause.Builder();
+ ctor public DisconnectCause.Builder(int);
method @NonNull public android.telecom.DisconnectCause build();
- method @NonNull public android.telecom.DisconnectCause.Builder setCode(int);
method @NonNull public android.telecom.DisconnectCause.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.telecom.DisconnectCause.Builder setImsReasonInfo(@Nullable android.telephony.ims.ImsReasonInfo);
method @NonNull public android.telecom.DisconnectCause.Builder setLabel(@Nullable CharSequence);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index bc45a76..c1af719 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1625,25 +1625,37 @@
package android.hardware.devicestate {
@FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+ ctor public DeviceState(@NonNull android.hardware.devicestate.DeviceState.Configuration);
field public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8; // 0x8
}
+ public static final class DeviceState.Configuration implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getIdentifier();
+ method @NonNull public String getName();
+ method @NonNull public java.util.Set<java.lang.Integer> getPhysicalProperties();
+ method @NonNull public java.util.Set<java.lang.Integer> getSystemProperties();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.hardware.devicestate.DeviceState.Configuration> CREATOR;
+ }
+
+ public static final class DeviceState.Configuration.Builder {
+ ctor public DeviceState.Configuration.Builder(int, @NonNull String);
+ method @NonNull public android.hardware.devicestate.DeviceState.Configuration build();
+ method @NonNull public android.hardware.devicestate.DeviceState.Configuration.Builder setPhysicalProperties(@NonNull java.util.Set<java.lang.Integer>);
+ method @NonNull public android.hardware.devicestate.DeviceState.Configuration.Builder setSystemProperties(@NonNull java.util.Set<java.lang.Integer>);
+ }
+
@FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
- method @Deprecated @NonNull public int[] getSupportedStates();
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
+ field public static final int INVALID_DEVICE_STATE_IDENTIFIER = -1; // 0xffffffff
field public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000; // 0x2710
field public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0; // 0x0
}
- public static interface DeviceStateManager.DeviceStateCallback {
- method @Deprecated public default void onBaseStateChanged(int);
- method @Deprecated public void onStateChanged(int);
- method @Deprecated public default void onSupportedStatesChanged(@NonNull int[]);
- }
-
public final class DeviceStateRequest {
method public int getFlags();
method public int getState();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index df566db..6f6e091 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -272,10 +272,15 @@
@UnsupportedAppUsage
private Context mOuterContext;
+
+ private final Object mThemeLock = new Object();
@UnsupportedAppUsage
+ @GuardedBy("mThemeLock")
private int mThemeResource = 0;
@UnsupportedAppUsage
+ @GuardedBy("mThemeLock")
private Resources.Theme mTheme = null;
+
@UnsupportedAppUsage
private PackageManager mPackageManager;
private Context mReceiverRestrictedContext = null;
@@ -288,7 +293,6 @@
private ContentCaptureOptions mContentCaptureOptions = null;
- private final Object mSync = new Object();
/**
* Indicates this {@link Context} can not handle UI components properly and is not associated
* with a {@link Display} instance.
@@ -340,20 +344,21 @@
*/
private boolean mOwnsToken = false;
- @GuardedBy("mSync")
+ private final Object mDirsLock = new Object();
+ @GuardedBy("mDirsLock")
private File mDatabasesDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
@UnsupportedAppUsage
private File mPreferencesDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
private File mFilesDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
private File mCratesDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
private File mNoBackupFilesDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
private File mCacheDir;
- @GuardedBy("mSync")
+ @GuardedBy("mDirsLock")
private File mCodeCacheDir;
// The system service cache for the system services that are cached per-ContextImpl.
@@ -458,7 +463,7 @@
@Override
public void setTheme(int resId) {
- synchronized (mSync) {
+ synchronized (mThemeLock) {
if (mThemeResource != resId) {
mThemeResource = resId;
initializeTheme();
@@ -468,14 +473,14 @@
@Override
public int getThemeResId() {
- synchronized (mSync) {
+ synchronized (mThemeLock) {
return mThemeResource;
}
}
@Override
public Resources.Theme getTheme() {
- synchronized (mSync) {
+ synchronized (mThemeLock) {
if (mTheme != null) {
return mTheme;
}
@@ -737,7 +742,7 @@
@UnsupportedAppUsage
private File getPreferencesDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mPreferencesDir == null) {
mPreferencesDir = new File(getDataDir(), "shared_prefs");
}
@@ -826,7 +831,7 @@
@Override
public File getFilesDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mFilesDir == null) {
mFilesDir = new File(getDataDir(), "files");
}
@@ -841,7 +846,7 @@
final Path absoluteNormalizedCratePath = cratesRootPath.resolve(crateId)
.toAbsolutePath().normalize();
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mCratesDir == null) {
mCratesDir = cratesRootPath.toFile();
}
@@ -854,7 +859,7 @@
@Override
public File getNoBackupFilesDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mNoBackupFilesDir == null) {
mNoBackupFilesDir = new File(getDataDir(), "no_backup");
}
@@ -871,7 +876,7 @@
@Override
public File[] getExternalFilesDirs(String type) {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
File[] dirs = Environment.buildExternalStorageAppFilesDirs(getPackageName());
if (type != null) {
dirs = Environment.buildPaths(dirs, type);
@@ -889,7 +894,7 @@
@Override
public File[] getObbDirs() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
File[] dirs = Environment.buildExternalStorageAppObbDirs(getPackageName());
return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
@@ -897,7 +902,7 @@
@Override
public File getCacheDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mCacheDir == null) {
mCacheDir = new File(getDataDir(), "cache");
}
@@ -907,7 +912,7 @@
@Override
public File getCodeCacheDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mCodeCacheDir == null) {
mCodeCacheDir = getCodeCacheDirBeforeBind(getDataDir());
}
@@ -933,7 +938,7 @@
@Override
public File[] getExternalCacheDirs() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
File[] dirs = Environment.buildExternalStorageAppCacheDirs(getPackageName());
// We don't try to create cache directories in-process, because they need special
// setup for accurate quota tracking. This ensures the cache dirs are always
@@ -944,7 +949,7 @@
@Override
public File[] getExternalMediaDirs() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
File[] dirs = Environment.buildExternalStorageAppMediaDirs(getPackageName());
return ensureExternalDirsExistOrFilter(dirs, true /* tryCreateInProcess */);
}
@@ -1046,7 +1051,7 @@
}
private File getDatabasesDir() {
- synchronized (mSync) {
+ synchronized (mDirsLock) {
if (mDatabasesDir == null) {
if ("android".equals(getPackageName())) {
mDatabasesDir = new File("/data/system");
diff --git a/core/java/android/app/ForegroundServiceDelegationOptions.java b/core/java/android/app/ForegroundServiceDelegationOptions.java
index 875e01f..d6b6a58 100644
--- a/core/java/android/app/ForegroundServiceDelegationOptions.java
+++ b/core/java/android/app/ForegroundServiceDelegationOptions.java
@@ -92,6 +92,16 @@
*/
public final @DelegationService int mDelegationService;
+ /**
+ * The optional notification Id of the foreground service delegation.
+ */
+ public final int mClientNotificationId;
+
+ /**
+ * The optional notification of the foreground service delegation.
+ */
+ public final @Nullable Notification mClientNotification;
+
public ForegroundServiceDelegationOptions(int clientPid,
int clientUid,
@NonNull String clientPackageName,
@@ -100,6 +110,21 @@
@NonNull String clientInstanceName,
int foregroundServiceTypes,
@DelegationService int delegationService) {
+ this(clientPid, clientUid, clientPackageName, clientAppThread, isSticky,
+ clientInstanceName, foregroundServiceTypes, delegationService,
+ 0 /* notificationId */, null /* notification */);
+ }
+
+ public ForegroundServiceDelegationOptions(int clientPid,
+ int clientUid,
+ @NonNull String clientPackageName,
+ @NonNull IApplicationThread clientAppThread,
+ boolean isSticky,
+ @NonNull String clientInstanceName,
+ int foregroundServiceTypes,
+ @DelegationService int delegationService,
+ int clientNotificationId,
+ @Nullable Notification clientNotification) {
mClientPid = clientPid;
mClientUid = clientUid;
mClientPackageName = clientPackageName;
@@ -108,6 +133,8 @@
mClientInstanceName = clientInstanceName;
mForegroundServiceTypes = foregroundServiceTypes;
mDelegationService = delegationService;
+ mClientNotificationId = clientNotificationId;
+ mClientNotification = clientNotification;
}
/**
@@ -201,7 +228,8 @@
int mClientPid; // The actual app PID
int mClientUid; // The actual app UID
String mClientPackageName; // The actual app's package name
- int mClientNotificationId; // The actual app's notification
+ int mClientNotificationId; // The actual app's notification id
+ Notification mClientNotification; // The actual app's notification
IApplicationThread mClientAppThread; // The actual app's app thread
boolean mSticky; // Is it a sticky service
String mClientInstanceName; // The delegation service instance name
@@ -233,10 +261,12 @@
}
/**
- * Set the notification ID from the client app.
+ * Set the notification from the client app.
*/
- public Builder setClientNotificationId(int clientNotificationId) {
+ public Builder setClientNotification(int clientNotificationId,
+ @Nullable Notification clientNotification) {
mClientNotificationId = clientNotificationId;
+ mClientNotification = clientNotification;
return this;
}
@@ -291,7 +321,9 @@
mSticky,
mClientInstanceName,
mForegroundServiceTypes,
- mDelegationService
+ mDelegationService,
+ mClientNotificationId,
+ mClientNotification
);
}
}
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 9f2e473..7c803eb 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -776,7 +776,8 @@
/**
* Whether or not notifications posted to this channel can bypass the Do Not Disturb
- * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode.
+ * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY} mode when the active policy allows
+ * priority channels to bypass notification filtering.
*/
public boolean canBypassDnd() {
return mBypassDnd;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fa4a400..66269a5 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -32,6 +32,7 @@
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IContentSuggestionsManager;
+import android.app.contextualsearch.ContextualSearchManager;
import android.app.ecm.EnhancedConfirmationFrameworkInitializer;
import android.app.job.JobSchedulerFrameworkInitializer;
import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
@@ -1282,6 +1283,16 @@
}
});
+ registerService(Context.CONTEXTUAL_SEARCH_SERVICE, ContextualSearchManager.class,
+ new CachedServiceFetcher<>() {
+ @Override
+ public ContextualSearchManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE);
+ return b == null ? null : new ContextualSearchManager();
+ }
+ });
+
registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
new CachedServiceFetcher<AppPredictionManager>() {
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cb4ed058..5b28f50 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -45,11 +45,11 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_STATUS_BAR;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIFI;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_WIPE_DATA;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
+import static android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
@@ -5952,7 +5952,8 @@
* <p>
* This method can be called on the {@link DevicePolicyManager} instance returned by
* {@link #getParentProfileInstance(ComponentName)} in order to set a value on the parent
- * profile.
+ * profile. This allows a profile wipe after too many incorrect device-unlock password have
+ * been entered on the parent profile even if each profile has a separate challenge.
* <p>On devices not supporting {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature, the
* password is always empty and this method has no effect - i.e. the policy is not set.
*
@@ -9979,17 +9980,22 @@
/**
* Called by a profile owner or device owner or holder of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}. to set a default activity
- * that the system selects to handle intents that match the given {@link IntentFilter}.
+ * that the system selects to handle intents that match the given {@link IntentFilter} instead
+ * of showing the default disambiguation mechanism.
* This activity will remain the default intent handler even if the set of potential event
* handlers for the intent filter changes and if the intent preferences are reset.
* <p>
- * Note that the caller should still declare the activity in the manifest, the API just sets
- * the activity to be the default one to handle the given intent filter.
+ * Note that the target application should still declare the activity in the manifest, the API
+ * just sets the activity to be the default one to handle the given intent filter.
* <p>
* The default disambiguation mechanism takes over if the activity is not installed (anymore).
* When the activity is (re)installed, it is automatically reset as default intent handler for
* the filter.
* <p>
+ * Note that calling this API to set a default intent handler, only allow to avoid the default
+ * disambiguation mechanism. Implicit intents that do not trigger this mechanism (like invoking
+ * the browser) cannot be configured as they are controlled by other configurations.
+ * <p>
* The calling device admin must be a profile owner or device owner. If it is not, a security
* exception will be thrown.
* <p>
@@ -17152,15 +17158,15 @@
* @hide
*/
@SystemApi
- @RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION)
+ @RequiresPermission(value = QUERY_DEVICE_STOLEN_STATE)
@FlaggedApi(FLAG_DEVICE_THEFT_API_ENABLED)
- public boolean isTheftDetectionTriggered() {
- throwIfParentInstance("isTheftDetectionTriggered");
+ public boolean isDevicePotentiallyStolen() {
+ throwIfParentInstance("isDevicePotentiallyStolen");
if (mService == null) {
return false;
}
try {
- return mService.isTheftDetectionTriggered(mContext.getPackageName());
+ return mService.isDevicePotentiallyStolen(mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 03d0b0f..aea0246 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -581,7 +581,7 @@
void setWifiSsidPolicy(String callerPackageName, in WifiSsidPolicy policy);
WifiSsidPolicy getWifiSsidPolicy(String callerPackageName);
- boolean isTheftDetectionTriggered(String callerPackageName);
+ boolean isDevicePotentiallyStolen(String callerPackageName);
List<UserHandle> listForegroundAffiliatedUsers();
void setDrawables(in List<DevicePolicyDrawableResource> drawables);
diff --git a/core/java/android/app/contextualsearch/ContextualSearchManager.java b/core/java/android/app/contextualsearch/ContextualSearchManager.java
new file mode 100644
index 0000000..693de21
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchManager.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.contextualsearch;
+
+import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.app.contextualsearch.flags.Flags;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.OutcomeReceiver;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.Executor;
+
+/**
+ * {@link ContextualSearchManager} is a system service to facilitate contextual search experience on
+ * configured Android devices.
+ * <p>
+ * This class lets
+ * <ul>
+ * <li> a caller start contextual search by calling {@link #startContextualSearch} method.
+ * <li> a handler request {@link ContextualSearchState} by calling the
+ * {@link #getContextualSearchState} method.
+ * </ul>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_SERVICE)
+public class ContextualSearchManager {
+
+ /**
+ * Key to get the entrypoint from the extras of the activity launched by contextual search.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_ENTRYPOINT =
+ "android.app.contextualsearch.extra.ENTRYPOINT";
+ /**
+ * Key to get the flag_secure value from the extras of the activity launched by contextual
+ * search. The value will be true if flag_secure is found in any of the visible activities.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_FLAG_SECURE_FOUND =
+ "android.app.contextualsearch.extra.FLAG_SECURE_FOUND";
+ /**
+ * Key to get the screenshot from the extras of the activity launched by contextual search.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_SCREENSHOT =
+ "android.app.contextualsearch.extra.SCREENSHOT";
+ /**
+ * Key to check whether managed profile is visible from the extras of the activity launched by
+ * contextual search. The value will be true if any one of the visible apps is managed.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_IS_MANAGED_PROFILE_VISIBLE =
+ "android.app.contextualsearch.extra.IS_MANAGED_PROFILE_VISIBLE";
+ /**
+ * Key to get the list of visible packages from the extras of the activity launched by
+ * contextual search.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_VISIBLE_PACKAGE_NAMES =
+ "android.app.contextualsearch.extra.VISIBLE_PACKAGE_NAMES";
+
+ /**
+ * Key to get the binder token from the extras of the activity launched by contextual search.
+ * This token is needed to invoke {@link #getContextualSearchState} method.
+ * Only supposed to be used with ACTON_LAUNCH_CONTEXTUAL_SEARCH.
+ */
+ public static final String EXTRA_TOKEN = "android.app.contextualsearch.extra.TOKEN";
+ /**
+ * Intent action for contextual search invocation. The app providing the contextual search
+ * experience must add this intent filter action to the activity it wants to be launched.
+ * <br>
+ * <b>Note</b> This activity must not be exported.
+ */
+ public static final String ACTION_LAUNCH_CONTEXTUAL_SEARCH =
+ "android.app.contextualsearch.action.LAUNCH_CONTEXTUAL_SEARCH";
+
+ /** Entrypoint to be used when a user long presses on the nav handle. */
+ public static final int ENTRYPOINT_LONG_PRESS_NAV_HANDLE = 1;
+ /** Entrypoint to be used when a user long presses on the home button. */
+ public static final int ENTRYPOINT_LONG_PRESS_HOME = 2;
+ /** Entrypoint to be used when a user long presses on the overview button. */
+ public static final int ENTRYPOINT_LONG_PRESS_OVERVIEW = 3;
+ /** Entrypoint to be used when a user presses the action button in overview. */
+ public static final int ENTRYPOINT_OVERVIEW_ACTION = 4;
+ /** Entrypoint to be used when a user presses the context menu button in overview. */
+ public static final int ENTRYPOINT_OVERVIEW_MENU = 5;
+ /** Entrypoint to be used by system actions like TalkBack, Accessibility etc. */
+ public static final int ENTRYPOINT_SYSTEM_ACTION = 9;
+ /** Entrypoint to be used when a user long presses on the meta key. */
+ public static final int ENTRYPOINT_LONG_PRESS_META = 10;
+ /**
+ * The {@link Entrypoint} annotation is used to standardize the entrypoints supported by
+ * {@link #startContextualSearch} method.
+ *
+ * @hide
+ */
+ @IntDef(prefix = {"ENTRYPOINT_"}, value = {
+ ENTRYPOINT_LONG_PRESS_NAV_HANDLE,
+ ENTRYPOINT_LONG_PRESS_HOME,
+ ENTRYPOINT_LONG_PRESS_OVERVIEW,
+ ENTRYPOINT_OVERVIEW_ACTION,
+ ENTRYPOINT_OVERVIEW_MENU,
+ ENTRYPOINT_SYSTEM_ACTION,
+ ENTRYPOINT_LONG_PRESS_META
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Entrypoint {
+ }
+ private static final String TAG = ContextualSearchManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final IContextualSearchManager mService;
+
+ /** @hide */
+ public ContextualSearchManager() {
+ if (DEBUG) Log.d(TAG, "ContextualSearchManager created");
+ IBinder b = ServiceManager.getService(Context.CONTEXTUAL_SEARCH_SERVICE);
+ mService = IContextualSearchManager.Stub.asInterface(b);
+ }
+
+ /**
+ * Used to start contextual search.
+ * <p>
+ * When {@link #startContextualSearch} is called, the system server does the following:
+ * <ul>
+ * <li>Resolves the activity using the package name and intent filter. The package name
+ * is fetched from the config specified in ContextualSearchManagerService.
+ * The activity must have ACTION_LAUNCH_CONTEXTUAL_SEARCH specified in its manifest.
+ * <li>Puts the required extras in the launch intent.
+ * <li>Launches the activity.
+ * </ul>
+ * </p>
+ *
+ * @param entrypoint the invocation entrypoint
+ */
+ @RequiresPermission(ACCESS_CONTEXTUAL_SEARCH)
+ public void startContextualSearch(@Entrypoint int entrypoint) {
+ if (DEBUG) Log.d(TAG, "startContextualSearch for entrypoint: " + entrypoint);
+ try {
+ mService.startContextualSearch(entrypoint);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.d(TAG, "Failed to startContextualSearch", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the {@link ContextualSearchState} to the handler via the provided callback.
+ *
+ * @param token The caller is expected to get the token from the launch extras of the handling
+ * activity using {@link Bundle#getIBinder} with {@link #EXTRA_TOKEN} key.
+ * <br>
+ * <b>Note</b> This token is for one time use only. Subsequent uses will invoke
+ * callback's {@link OutcomeReceiver#onError}.
+ * @param executor The executor which will be used to invoke the callback.
+ * @param callback The callback which will be used to return {@link ContextualSearchState}
+ * if/when it is available via {@link OutcomeReceiver#onResult}. It will also be
+ * used to return errors via {@link OutcomeReceiver#onError}.
+ */
+ public void getContextualSearchState(@NonNull IBinder token,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) {
+ if (DEBUG) Log.d(TAG, "getContextualSearchState for token:" + token);
+ try {
+ final CallbackWrapper wrapper = new CallbackWrapper(executor, callback);
+ mService.getContextualSearchState(token, wrapper);
+ } catch (RemoteException e) {
+ if (DEBUG) Log.d(TAG, "Failed to getContextualSearchState", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private static class CallbackWrapper extends IContextualSearchCallback.Stub {
+ private final OutcomeReceiver<ContextualSearchState, Throwable> mCallback;
+ private final Executor mExecutor;
+
+ CallbackWrapper(@NonNull Executor callbackExecutor,
+ @NonNull OutcomeReceiver<ContextualSearchState, Throwable> callback) {
+ mCallback = callback;
+ mExecutor = callbackExecutor;
+ }
+
+ @Override
+ public void onResult(ContextualSearchState state) {
+ Binder.withCleanCallingIdentity(() -> {
+ if (DEBUG) Log.d(TAG, "onResult state:" + state);
+ mExecutor.execute(() -> mCallback.onResult(state));
+ });
+ }
+
+ @Override
+ public void onError(ParcelableException error) {
+ Binder.withCleanCallingIdentity(() -> {
+ if (DEBUG) Log.w(TAG, "onError", error);
+ mExecutor.execute(() -> mCallback.onError(error));
+ });
+ }
+ }
+}
diff --git a/core/java/android/app/contextualsearch/ContextualSearchState.aidl b/core/java/android/app/contextualsearch/ContextualSearchState.aidl
new file mode 100644
index 0000000..7f64484
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchState.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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 android.app.contextualsearch;
+
+parcelable ContextualSearchState;
\ No newline at end of file
diff --git a/core/java/android/app/contextualsearch/ContextualSearchState.java b/core/java/android/app/contextualsearch/ContextualSearchState.java
new file mode 100644
index 0000000..5c04bc89
--- /dev/null
+++ b/core/java/android/app/contextualsearch/ContextualSearchState.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.contextualsearch;
+
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.contextualsearch.flags.Flags;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+/**
+ * {@link ContextualSearchState} contains additional data a contextual search handler can request
+ * via {@link ContextualSearchManager#getContextualSearchState} method.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_ENABLE_SERVICE)
+@SystemApi
+public final class ContextualSearchState implements Parcelable {
+ private final @NonNull Bundle mExtras;
+ private final @Nullable AssistStructure mStructure;
+ private final @Nullable AssistContent mContent;
+
+ public ContextualSearchState(@Nullable AssistStructure structure,
+ @Nullable AssistContent content, @NonNull Bundle extras) {
+ mStructure = structure;
+ mContent = content;
+ mExtras = extras;
+ }
+
+ private ContextualSearchState(Parcel source) {
+ this.mStructure = source.readTypedObject(AssistStructure.CREATOR);
+ this.mContent = source.readTypedObject(AssistContent.CREATOR);
+ Bundle extras = source.readBundle(getClass().getClassLoader());
+ this.mExtras = extras != null ? extras : Bundle.EMPTY;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(this.mStructure, flags);
+ dest.writeTypedObject(this.mContent, flags);
+ dest.writeBundle(this.mExtras);
+ }
+
+ /** Gets an instance of {@link AssistContent}. */
+ @Nullable
+ public AssistContent getContent() {
+ return mContent;
+ }
+
+ /** Gets an instance of {@link AssistStructure}. */
+ @Nullable
+ public AssistStructure getStructure() {
+ return mStructure;
+ }
+
+ /**
+ * Gets an instance of {@link Bundle} containing the extras added by the system server.
+ * The contents of this bundle vary by usecase. When Contextual is invoked via Launcher, this
+ * bundle is empty.
+ */
+ @NonNull
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ @NonNull
+ public static final Creator<ContextualSearchState> CREATOR = new Creator<>() {
+ @Override
+ public ContextualSearchState createFromParcel(Parcel source) {
+ return new ContextualSearchState(source);
+ }
+
+ @Override
+ public ContextualSearchState[] newArray(int size) {
+ return new ContextualSearchState[size];
+ }
+ };
+}
diff --git a/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl b/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl
new file mode 100644
index 0000000..5fe5fd2
--- /dev/null
+++ b/core/java/android/app/contextualsearch/IContextualSearchCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.app.contextualsearch;
+
+import android.os.ParcelableException;
+import android.app.contextualsearch.ContextualSearchState;
+/**
+ * @hide
+ */
+oneway interface IContextualSearchCallback {
+ void onResult(in ContextualSearchState state);
+ void onError(in ParcelableException error);
+}
diff --git a/core/java/android/app/contextualsearch/IContextualSearchManager.aidl b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl
new file mode 100644
index 0000000..1735a71
--- /dev/null
+++ b/core/java/android/app/contextualsearch/IContextualSearchManager.aidl
@@ -0,0 +1,11 @@
+package android.app.contextualsearch;
+
+
+import android.app.contextualsearch.IContextualSearchCallback;
+/**
+ * @hide
+ */
+oneway interface IContextualSearchManager {
+ void startContextualSearch(int entrypoint);
+ void getContextualSearchState(in IBinder token, in IContextualSearchCallback callback);
+}
diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig
new file mode 100644
index 0000000..5ab0762
--- /dev/null
+++ b/core/java/android/app/contextualsearch/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.app.contextualsearch.flags"
+
+flag {
+ name: "enable_service"
+ namespace: "machine_learning"
+ description: "Flag to enable the service"
+ bug: "309689654"
+}
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
index 3e543d2..c275cc7 100644
--- a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
+++ b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
@@ -75,7 +75,11 @@
/**
- * Sends a custom signal with the provided parameters. It also signals the remote callback
+ * Sends a custom signal with the provided parameters. If there are multiple concurrent
+ * requests to this method, the actionParams are queued in a blocking fashion, in the order they
+ * are received.
+ *
+ * It also signals the remote callback
* with the same params if already configured, if not the action is queued to be sent when a
* remote is configured. Similarly, on the receiver side, the callback will be invoked if
* already set, if not all actions are queued to be sent to callback when it is set.
@@ -159,9 +163,9 @@
* Sets the remote transport.
*
* If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also
- * sequentially sent to the remote.
+ * sequentially sent to the configured remote.
*
- * This method is guaranteed that the remote transport will not be called after it
+ * This method guarantees that the remote transport will not be called after it
* has been removed.
*
* @param remote The remote transport, or null to remove.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7f2ec53..1653bf5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5396,6 +5396,19 @@
public static final String SMARTSPACE_SERVICE = "smartspace";
/**
+ * Used for getting the contextual search service.
+ *
+ * <p><b>NOTE: </b> this service is optional; callers of
+ * {@code Context.getSystemServiceName(CONTEXTUAL_SEARCH_SERVICE)} must check for {@code null}.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ @FlaggedApi(android.app.contextualsearch.flags.Flags.FLAG_ENABLE_SERVICE)
+ @SystemApi
+ public static final String CONTEXTUAL_SEARCH_SERVICE = "contextual_search";
+
+ /**
* Used for getting the cloudsearch service.
*
* <p><b>NOTE: </b> this service is optional; callers of
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 443aadd..8bc5e8c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6129,10 +6129,14 @@
* the selection changes made by the user.
* Applications may implement this method to change any of the following Chooser arguments by
* returning new values in the result bundle:
- * {@link #EXTRA_CHOOSER_TARGETS}, {@link #EXTRA_ALTERNATE_INTENTS},
+ * {@link #EXTRA_CHOOSER_TARGETS},
+ * {@link #EXTRA_ALTERNATE_INTENTS},
* {@link #EXTRA_CHOOSER_CUSTOM_ACTIONS},
* {@link #EXTRA_CHOOSER_MODIFY_SHARE_ACTION},
- * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER}.</p>
+ * {@link #EXTRA_METADATA_TEXT},
+ * {@link #EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER},
+ * {@link #EXTRA_CHOOSER_RESULT_INTENT_SENDER}.
+ * </p>
*/
@FlaggedApi(android.service.chooser.Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING)
public static final String EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI =
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 535cebb..bd04634 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -1280,26 +1280,6 @@
264301586L; // buganizer id
/**
- * Excludes the packages the override is applied to from the camera compatibility treatment
- * in free-form windowing mode for fixed-orientation apps.
- *
- * <p>In free-form windowing mode, the compatibility treatment emulates running on a portrait
- * device by letterboxing the app window and changing the camera characteristics to what apps
- * commonly expect in a portrait device: 90 and 270 degree sensor rotation for back and front
- * cameras, respectively, and setting display rotation to 0.
- *
- * <p>Use this flag to disable the compatibility treatment for apps that do not respond well to
- * the treatment.
- *
- * @hide
- */
- @ChangeId
- @Overridable
- @Disabled
- public static final long OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT =
- 314961188L;
-
- /**
* This change id forces the packages it is applied to sandbox {@link android.view.View} API to
* an activity bounds for:
*
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 3a5383d..39b9149 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -693,7 +693,12 @@
*
* <p>If the caller is running on a managed profile, it'll return only the current profile.
* Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would.
+ *
+ * <p> To get hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public List<UserHandle> getProfiles() {
if (mUserManager.isManagedProfile()
|| (android.multiuser.Flags.enableLauncherAppsHiddenProfileChecks()
@@ -751,11 +756,17 @@
* list.</li>
* </ul>
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param packageName The specific package to query. If null, it checks all installed packages
* in the profile.
* @param user The UserHandle of the profile.
* @return List of launchable activities. Can be an empty list but will not be null.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
@@ -925,10 +936,16 @@
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
* returns null.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param intent The intent to find a match for.
* @param user The profile to look in for a match.
* @return An activity info object if there is a match.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
@@ -978,11 +995,17 @@
/**
* Starts a Main activity in the specified profile.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param component The ComponentName of the activity to launch
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public void startMainActivity(ComponentName component, UserHandle user, Rect sourceBounds,
Bundle opts) {
logErrorForInvalidProfileAccess(user);
@@ -1020,11 +1043,17 @@
* Starts the settings activity to show the application details for a
* package in the specified profile.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param component The ComponentName of the package to launch settings for.
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public void startAppDetailsActivity(ComponentName component, UserHandle user,
Rect sourceBounds, Bundle opts) {
logErrorForInvalidProfileAccess(user);
@@ -1135,11 +1164,17 @@
/**
* Checks if the package is installed and enabled for a profile.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param packageName The package to check.
* @param user The UserHandle of the profile.
*
* @return true if the package exists and is enabled.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public boolean isPackageEnabled(String packageName, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
@@ -1157,6 +1192,10 @@
* <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
* app and the launcher.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* <p>Note: This just returns whatever extras were provided to the system, <em>which might
* even be {@code null}.</em>
*
@@ -1168,6 +1207,8 @@
* @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
* @see PackageManager#isPackageSuspended()
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
@@ -1182,10 +1223,16 @@
* could be done because the package was marked as distracting to the user via
* {@code PackageManager.setDistractingPackageRestrictions(String[], int)}.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param packageName The package for which to check.
* @param user the {@link UserHandle} of the profile.
* @return
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public boolean shouldHideFromSuggestions(@NonNull String packageName,
@NonNull UserHandle user) {
Objects.requireNonNull(packageName, "packageName");
@@ -1200,6 +1247,10 @@
/**
* Returns {@link ApplicationInfo} about an application installed for a specific user profile.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param packageName The package name of the application
* @param flags Additional option flags {@link PackageManager#getApplicationInfo}
* @param user The UserHandle of the profile.
@@ -1208,6 +1259,8 @@
* {@code null} if the package isn't installed for the given profile, or the profile
* isn't enabled.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public ApplicationInfo getApplicationInfo(@NonNull String packageName,
@ApplicationInfoFlagsBits int flags, @NonNull UserHandle user)
throws PackageManager.NameNotFoundException {
@@ -1257,11 +1310,17 @@
* <p>The activity may still not be exported, in which case {@link #startMainActivity} will
* throw a {@link SecurityException} unless the caller has the same UID as the target app's.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @param component The activity to check.
* @param user The UserHandle of the profile.
*
* @return true if the activity exists and is enabled.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public boolean isActivityEnabled(ComponentName component, UserHandle user) {
logErrorForInvalidProfileAccess(user);
try {
@@ -1816,8 +1875,14 @@
/**
* Registers a callback for changes to packages in this user and managed profiles.
*
+ * <p>To receive callbacks for hidden profile{@link UserManager.USER_TYPE_PROFILE_PRIVATE},
+ * caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+ * permissions required.
+ *
* @param callback The callback to register.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public void registerCallback(Callback callback) {
registerCallback(callback, null);
}
@@ -1825,9 +1890,15 @@
/**
* Registers a callback for changes to packages in this user and managed profiles.
*
+ * <p>To receive callbacks for hidden profile {@link UserManager.USER_TYPE_PROFILE_PRIVATE},
+ * caller should have {@link android.app.role.RoleManager.ROLE_HOME} and either of the
+ * permissions required.
+ *
* @param callback The callback to register.
* @param handler that should be used to post callbacks on, may be null.
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public void registerCallback(Callback callback, Handler handler) {
synchronized (this) {
if (callback != null && findCallbackLocked(callback) < 0) {
@@ -2275,8 +2346,14 @@
* package name in the app's manifest, have the android.permission.QUERY_ALL_PACKAGES, or be
* the session owner to retrieve these details.
*
+ * <p>If the user in question is a hidden profile
+ * {@link UserManager.USER_TYPE_PROFILE_PRIVATE}, caller should have
+ * {@link android.app.role.RoleManager.ROLE_HOME} and either of the permissions required.
+ *
* @see PackageInstaller#getAllSessions()
*/
+ @RequiresPermission(conditional = true,
+ anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES})
public @NonNull List<SessionInfo> getAllPackageInstallerSessions() {
try {
return mService.getAllSessions(mContext.getPackageName()).getList();
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 749e0f8..2d32aed 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -193,3 +193,10 @@
bug: "290333800"
is_fixed_read_only: true
}
+
+flag {
+ name: "delete_private_space_from_reset"
+ namespace: "profile_experiences"
+ description: "Add entrypoint in Settings Reset options for deleting private space when lock is forgotten"
+ bug: "329601751"
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index dfb77c0..faa2c70 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -34,22 +34,17 @@
import android.util.LruCache;
import android.util.Pair;
import android.util.Printer;
-import com.android.internal.util.RingBuffer;
import dalvik.system.BlockGuard;
import dalvik.system.CloseGuard;
-
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
-import java.util.Locale;
import java.util.Map;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
@@ -190,7 +185,7 @@
SQLiteDatabaseConfiguration configuration,
int connectionId, boolean primaryConnection) {
mPool = pool;
- mRecentOperations = new OperationLog();
+ mRecentOperations = new OperationLog(mPool);
mConfiguration = new SQLiteDatabaseConfiguration(configuration);
mConnectionId = connectionId;
mIsPrimaryConnection = primaryConnection;
@@ -312,16 +307,6 @@
}
}
- /** Record the start of a transaction for logging and debugging. */
- void recordBeginTransaction(String mode) {
- mRecentOperations.beginTransaction(mode);
- }
-
- /** Record the end of a transaction for logging and debugging. */
- void recordEndTransaction(boolean successful) {
- mRecentOperations.endTransaction(successful);
- }
-
private void setPageSize() {
if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
final long newValue = SQLiteGlobal.getDefaultPageSize();
@@ -1352,7 +1337,6 @@
}
printer.println(" isPrimaryConnection: " + mIsPrimaryConnection);
printer.println(" onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
- printer.println(" totalLongOperations: " + mRecentOperations.getTotalLongOperations());
mRecentOperations.dump(printer);
@@ -1611,39 +1595,51 @@
}
}
- private final class OperationLog {
+ private static final class OperationLog {
private static final int MAX_RECENT_OPERATIONS = 20;
private static final int COOKIE_GENERATION_SHIFT = 8;
private static final int COOKIE_INDEX_MASK = 0xff;
- // Operations over 2s are long. Save the last ten.
- private static final long LONG_OPERATION_THRESHOLD_MS = 2_000;
- private static final int MAX_LONG_OPERATIONS = 10;
-
private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
- private int mIndex = -1;
- private int mGeneration = 0;
- private final Operation mTransaction = new Operation();
+ private int mIndex;
+ private int mGeneration;
+ private final SQLiteConnectionPool mPool;
private long mResultLong = Long.MIN_VALUE;
private String mResultString;
- private final RingBuffer<Operation> mLongOperations =
- new RingBuffer<>(()->{return new Operation();},
- (n) ->{return new Operation[n];},
- MAX_LONG_OPERATIONS);
- private int mTotalLongOperations = 0;
+ OperationLog(SQLiteConnectionPool pool) {
+ mPool = pool;
+ }
public int beginOperation(String kind, String sql, Object[] bindArgs) {
mResultLong = Long.MIN_VALUE;
mResultString = null;
synchronized (mOperations) {
- Operation operation = newOperationLocked();
+ final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
+ Operation operation = mOperations[index];
+ if (operation == null) {
+ operation = new Operation();
+ mOperations[index] = operation;
+ } else {
+ operation.mFinished = false;
+ operation.mException = null;
+ if (operation.mBindArgs != null) {
+ operation.mBindArgs.clear();
+ }
+ }
+ operation.mStartWallTime = System.currentTimeMillis();
+ operation.mStartTime = SystemClock.uptimeMillis();
operation.mKind = kind;
operation.mSql = sql;
+ operation.mPath = mPool.getPath();
+ operation.mResultLong = Long.MIN_VALUE;
+ operation.mResultString = null;
if (bindArgs != null) {
if (operation.mBindArgs == null) {
operation.mBindArgs = new ArrayList<Object>();
+ } else {
+ operation.mBindArgs.clear();
}
for (int i = 0; i < bindArgs.length; i++) {
final Object arg = bindArgs[i];
@@ -1655,44 +1651,16 @@
}
}
}
+ operation.mCookie = newOperationCookieLocked(index);
if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
operation.mCookie);
}
+ mIndex = index;
return operation.mCookie;
}
}
- public void beginTransaction(String kind) {
- synchronized (mOperations) {
- Operation operation = newOperationLocked();
- operation.mKind = kind;
- mTransaction.copyFrom(operation);
-
- if (Trace.isTagEnabled(Trace.TRACE_TAG_DATABASE)) {
- Trace.asyncTraceBegin(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
- operation.mCookie);
- }
- }
- }
-
- /**
- * Fetch a new operation from the ring buffer. The operation is properly initialized.
- * This advances mIndex to point to the next element.
- */
- private Operation newOperationLocked() {
- final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
- Operation operation = mOperations[index];
- if (operation == null) {
- mOperations[index] = new Operation();
- operation = mOperations[index];
- }
- operation.start();
- operation.mCookie = newOperationCookieLocked(index);
- mIndex = index;
- return operation;
- }
-
public void failOperation(int cookie, Exception ex) {
synchronized (mOperations) {
final Operation operation = getOperationLocked(cookie);
@@ -1716,20 +1684,6 @@
}
}
- public boolean endTransaction(boolean success) {
- synchronized (mOperations) {
- mTransaction.mResultLong = success ? 1 : 0;
- final long execTime = finishOperationLocked(mTransaction);
- final Operation operation = getOperationLocked(mTransaction.mCookie);
- if (operation != null) {
- operation.copyFrom(mTransaction);
- }
- mTransaction.setEmpty();
- return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES
- && SQLiteDebug.shouldLogSlowQuery(execTime);
- }
- }
-
public void logOperation(int cookie, String detail) {
synchronized (mOperations) {
logOperationLocked(cookie, detail);
@@ -1751,7 +1705,9 @@
Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
operation.mCookie);
}
- final long execTime = finishOperationLocked(operation);
+ operation.mEndTime = SystemClock.uptimeMillis();
+ operation.mFinished = true;
+ final long execTime = operation.mEndTime - operation.mStartTime;
mPool.onStatementExecuted(execTime);
return NoPreloadHolder.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
execTime);
@@ -1776,22 +1732,10 @@
return generation << COOKIE_GENERATION_SHIFT | index;
}
- /** Close out the operation and return the elapsed time. */
- private long finishOperationLocked(Operation operation) {
- operation.mEndTime = SystemClock.uptimeMillis();
- operation.mFinished = true;
- final long elapsed = operation.mEndTime - operation.mStartTime;
- if (elapsed > LONG_OPERATION_THRESHOLD_MS) {
- mLongOperations.getNextSlot().copyFrom(operation);
- mTotalLongOperations++;
- }
- return elapsed;
- }
-
private Operation getOperationLocked(int cookie) {
final int index = cookie & COOKIE_INDEX_MASK;
final Operation operation = mOperations[index];
- return (operation != null && operation.mCookie == cookie) ? operation : null;
+ return operation.mCookie == cookie ? operation : null;
}
public String describeCurrentOperation() {
@@ -1806,87 +1750,48 @@
}
}
- /**
- * Dump an Operation if it is not in the recent operations list. Return 1 if the
- * operation was dumped and 0 if not.
- */
- private int dumpIfNotRecentLocked(Printer pw, Operation op, int counter) {
- if (op == null || op.isEmpty() || getOperationLocked(op.mCookie) != null) {
- return 0;
- }
- pw.println(op.describe(counter));
- return 1;
- }
-
- private void dumpRecentLocked(Printer printer) {
+ public void dump(Printer printer) {
synchronized (mOperations) {
printer.println(" Most recently executed operations:");
int index = mIndex;
- if (index == 0) {
- printer.println(" <none>");
- return;
- }
-
- // Operations are dumped in order of most recent first.
- int counter = 0;
- int n = 0;
Operation operation = mOperations[index];
- do {
- printer.println(operation.describe(counter));
+ if (operation != null) {
+ // Note: SimpleDateFormat is not thread-safe, cannot be compile-time created,
+ // and is relatively expensive to create during preloading. This method is only
+ // used when dumping a connection, which is a rare (mainly error) case.
+ SimpleDateFormat opDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ int n = 0;
+ do {
+ StringBuilder msg = new StringBuilder();
+ msg.append(" ").append(n).append(": [");
+ String formattedStartTime = opDF.format(new Date(operation.mStartWallTime));
+ msg.append(formattedStartTime);
+ msg.append("] ");
+ operation.describe(msg, false); // Never dump bingargs in a bugreport
+ printer.println(msg.toString());
- if (index > 0) {
- index -= 1;
- } else {
- index = MAX_RECENT_OPERATIONS - 1;
- }
- n++;
- counter++;
- operation = mOperations[index];
- } while (operation != null && n < MAX_RECENT_OPERATIONS);
- counter += dumpIfNotRecentLocked(printer, mTransaction, counter);
- }
- }
-
- private void dumpLongLocked(Printer printer) {
- printer.println(" Operations exceeding " + LONG_OPERATION_THRESHOLD_MS + "ms:");
- if (mLongOperations.isEmpty()) {
- printer.println(" <none>");
- return;
- }
- Operation[] longOps = mLongOperations.toArray();
- for (int i = 0; i < longOps.length; i++) {
- if (longOps[i] != null) {
- printer.println(longOps[i].describe(i));
+ if (index > 0) {
+ index -= 1;
+ } else {
+ index = MAX_RECENT_OPERATIONS - 1;
+ }
+ n += 1;
+ operation = mOperations[index];
+ } while (operation != null && n < MAX_RECENT_OPERATIONS);
+ } else {
+ printer.println(" <none>");
}
}
}
-
- public long getTotalLongOperations() {
- return mTotalLongOperations;
- }
-
- public void dump(Printer printer) {
- synchronized (mOperations) {
- dumpRecentLocked(printer);
- dumpLongLocked(printer);
- }
- }
}
- private final class Operation {
+ private static final class Operation {
// Trim all SQL statements to 256 characters inside the trace marker.
// This limit gives plenty of context while leaving space for other
// entries in the trace buffer (and ensures atrace doesn't truncate the
// marker for us, potentially losing metadata in the process).
private static final int MAX_TRACE_METHOD_NAME_LEN = 256;
- // The reserved start time that indicates the Operation is empty.
- private static final long EMPTY_OPERATION = -1;
-
- // The formatter for the timestamp.
- private static final DateTimeFormatter sDateTime =
- DateTimeFormatter.ofPattern("MM-dd HH:mm:ss.SSS", Locale.US);
-
public long mStartWallTime; // in System.currentTimeMillis()
public long mStartTime; // in SystemClock.uptimeMillis();
public long mEndTime; // in SystemClock.uptimeMillis();
@@ -1896,58 +1801,16 @@
public boolean mFinished;
public Exception mException;
public int mCookie;
+ public String mPath;
public long mResultLong; // MIN_VALUE means "value not set".
public String mResultString;
- /** Reset the object to begin a new operation. */
- void start() {
- mStartWallTime = System.currentTimeMillis();
- mStartTime = SystemClock.uptimeMillis();
- mEndTime = Long.MIN_VALUE;
- mKind = null;
- mSql = null;
- if (mBindArgs != null) mBindArgs.clear();
- mFinished = false;
- mException = null;
- mCookie = -1;
- mResultLong = Long.MIN_VALUE;
- mResultString = null;
- }
-
- /**
- * Initialize from the source object. This is meant to clone the object for use in a
- * transaction operation. To that end, the local bind args are set to null.
- */
- void copyFrom(Operation r) {
- mStartWallTime = r.mStartWallTime;
- mStartTime = r.mStartTime;
- mEndTime = r.mEndTime;
- mKind = r.mKind;
- mSql = r.mSql;
- mBindArgs = null;
- mFinished = r.mFinished;
- mException = r.mException;
- mCookie = r.mCookie;
- mResultLong = r.mResultLong;
- mResultString = r.mResultString;
- }
-
- /** Mark the operation empty. */
- void setEmpty() {
- mStartWallTime = EMPTY_OPERATION;
- }
-
- /** Return true if the operation is empty. */
- boolean isEmpty() {
- return mStartWallTime == EMPTY_OPERATION;
- }
-
public void describe(StringBuilder msg, boolean allowDetailedLog) {
msg.append(mKind);
if (mFinished) {
msg.append(" took ").append(mEndTime - mStartTime).append("ms");
} else {
- msg.append(" started ").append(SystemClock.uptimeMillis() - mStartTime)
+ msg.append(" started ").append(System.currentTimeMillis() - mStartWallTime)
.append("ms ago");
}
msg.append(" - ").append(getStatus());
@@ -1976,7 +1839,7 @@
}
msg.append("]");
}
- msg.append(", path=").append(mPool.getPath());
+ msg.append(", path=").append(mPath);
if (mException != null) {
msg.append(", exception=\"").append(mException.getMessage()).append("\"");
}
@@ -1988,21 +1851,6 @@
}
}
- /**
- * Convert a wall-clock time in milliseconds to logcat format.
- */
- private String timeString(long millis) {
- return sDateTime.withZone(ZoneId.systemDefault()).format(Instant.ofEpochMilli(millis));
- }
-
- public String describe(int n) {
- final StringBuilder msg = new StringBuilder();
- final String start = timeString(mStartWallTime);
- msg.append(" ").append(n).append(": [").append(start).append("] ");
- describe(msg, false); // Never dump bingargs in a bugreport
- return msg.toString();
- }
-
private String getStatus() {
if (!mFinished) {
return "running";
@@ -2016,6 +1864,7 @@
return methodName.substring(0, MAX_TRACE_METHOD_NAME_LEN);
return methodName;
}
+
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 15d7d66..ad335b6 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1175,7 +1175,7 @@
+ ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled
+ ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.resolveJournalMode())
+ ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.resolveSyncMode()));
- printer.println(" IsReadOnlyDatabase: " + mConfiguration.isReadOnlyDatabase());
+ printer.println(" IsReadOnlyDatabase=" + mConfiguration.isReadOnlyDatabase());
if (isCompatibilityWalEnabled) {
printer.println(" Compatibility WAL enabled: wal_syncmode="
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 3b14d9d..7d9f02d 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -312,15 +312,6 @@
cancellationSignal);
}
- private String modeString(int transactionMode) {
- switch (transactionMode) {
- case TRANSACTION_MODE_IMMEDIATE: return "TRANSACTION-IMMEDIATE";
- case TRANSACTION_MODE_EXCLUSIVE: return "TRANSACTION-EXCLUSIVE";
- case TRANSACTION_MODE_DEFERRED: return "TRANSACTION-DEFERRED";
- default: return "TRANSACTION";
- }
- }
-
private void beginTransactionUnchecked(int transactionMode,
SQLiteTransactionListener transactionListener, int connectionFlags,
CancellationSignal cancellationSignal) {
@@ -330,7 +321,6 @@
if (mTransactionStack == null) {
acquireConnection(null, connectionFlags, cancellationSignal); // might throw
- mConnection.recordBeginTransaction(modeString(transactionMode));
}
try {
// Set up the transaction such that we can back out safely
@@ -475,7 +465,6 @@
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
} finally {
- mConnection.recordEndTransaction(successful);
releaseConnection(); // might throw
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index be9f0a0..a8d54ed 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -16,7 +16,7 @@
package android.hardware.biometrics;
-import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -135,7 +135,7 @@
@Retention(RetentionPolicy.SOURCE)
public @interface DismissedReason {}
- private static class ButtonInfo {
+ static class ButtonInfo {
Executor executor;
DialogInterface.OnClickListener listener;
ButtonInfo(Executor ex, DialogInterface.OnClickListener l) {
@@ -150,6 +150,7 @@
public static class Builder {
private PromptInfo mPromptInfo;
private ButtonInfo mNegativeButtonInfo;
+ private ButtonInfo mContentViewMoreOptionsButtonInfo;
private Context mContext;
private IAuthService mService;
@@ -175,7 +176,7 @@
* @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoRes(@DrawableRes int logoRes) {
mPromptInfo.setLogoRes(logoRes);
@@ -194,7 +195,7 @@
* @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) {
mPromptInfo.setLogoBitmap(logoBitmap);
@@ -213,7 +214,7 @@
* @return This builder.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@NonNull
public BiometricPrompt.Builder setLogoDescription(@NonNull String logoDescription) {
mPromptInfo.setLogoDescription(logoDescription);
@@ -301,6 +302,12 @@
@NonNull
public BiometricPrompt.Builder setContentView(@NonNull PromptContentView view) {
mPromptInfo.setContentView(view);
+
+ if (mPromptInfo.isContentViewMoreOptionsButtonUsed()) {
+ mContentViewMoreOptionsButtonInfo =
+ ((PromptContentViewWithMoreOptionsButton) view).getButtonInfo();
+ }
+
return this;
}
@@ -619,7 +626,8 @@
}
mService = (mService == null) ? IAuthService.Stub.asInterface(
ServiceManager.getService(Context.AUTH_SERVICE)) : mService;
- return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService);
+ return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo,
+ mContentViewMoreOptionsButtonInfo, mService);
}
}
@@ -646,6 +654,9 @@
private final IAuthService mService;
private final PromptInfo mPromptInfo;
private final ButtonInfo mNegativeButtonInfo;
+ // TODO(b/328843028): add callback onContentViewMoreOptionsButtonClicked() in
+ // IBiometricServiceReceiver.
+ private final ButtonInfo mContentViewMoreOptionsButtonInfo;
private CryptoObject mCryptoObject;
private Executor mExecutor;
@@ -751,10 +762,11 @@
private boolean mIsPromptShowing;
private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo,
- IAuthService service) {
+ ButtonInfo contentViewMoreOptionsButtonInfo, IAuthService service) {
mContext = context;
mPromptInfo = promptInfo;
mNegativeButtonInfo = negativeButtonInfo;
+ mContentViewMoreOptionsButtonInfo = contentViewMoreOptionsButtonInfo;
mService = service;
mIsPromptShowing = false;
}
@@ -766,7 +778,7 @@
* @return The drawable resource of the logo, or -1 if the prompt has no logo resource set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@DrawableRes
public int getLogoRes() {
return mPromptInfo.getLogoRes();
@@ -779,7 +791,7 @@
* @return The logo bitmap of the prompt, or null if the prompt has no logo bitmap set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@Nullable
public Bitmap getLogoBitmap() {
return mPromptInfo.getLogoBitmap();
@@ -794,7 +806,7 @@
* set.
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- @RequiresPermission(SET_BIOMETRIC_DIALOG_LOGO)
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
@Nullable
public String getLogoDescription() {
return mPromptInfo.getLogoDescription();
@@ -932,14 +944,29 @@
* @see Builder#setAllowedAuthenticators(int)
*/
public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
+ /**
+ * Create from a {@link Signature} object.
+ *
+ * @param signature a {@link Signature} object.
+ */
public CryptoObject(@NonNull Signature signature) {
super(signature);
}
+ /**
+ * Create from a {@link Cipher} object.
+ *
+ * @param cipher a {@link Cipher} object.
+ */
public CryptoObject(@NonNull Cipher cipher) {
super(cipher);
}
+ /**
+ * Create from a {@link Mac} object.
+ *
+ * @param mac a {@link Mac} object.
+ */
public CryptoObject(@NonNull Mac mac) {
super(mac);
}
@@ -955,16 +982,37 @@
super(credential);
}
+ /**
+ * Create from a {@link PresentationSession} object.
+ *
+ * @param session a {@link PresentationSession} object.
+ */
public CryptoObject(@NonNull PresentationSession session) {
super(session);
}
+ /**
+ * Create from a {@link KeyAgreement} object.
+ *
+ * @param keyAgreement a {@link KeyAgreement} object.
+ */
@FlaggedApi(FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT)
public CryptoObject(@NonNull KeyAgreement keyAgreement) {
super(keyAgreement);
}
/**
+ * Create from an operation handle.
+ * @see CryptoObject#getOperationHandle()
+ *
+ * @param operationHandle the operation handle associated with this object.
+ */
+ @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
+ public CryptoObject(long operationHandle) {
+ super(operationHandle);
+ }
+
+ /**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
*/
@@ -1016,7 +1064,20 @@
}
/**
- * Get the operation handle associated with this object or 0 if none.
+ * Returns the {@code operationHandle} associated with this object or 0 if none.
+ * The {@code operationHandle} is the underlying identifier associated with
+ * the {@code CryptoObject}.
+ *
+ * <p> The {@code operationHandle} can be used to reconstruct a {@code CryptoObject}
+ * instance. This is useful for any cross-process communication as the {@code CryptoObject}
+ * class is not {@link android.os.Parcelable}. Hence, if the {@code CryptoObject} is
+ * constructed in one process, and needs to be propagated to another process,
+ * before calling the
+ * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor,
+ * AuthenticationCallback)} API in the second process, the recommendation is to retrieve the
+ * {@code operationHandle} using this API, and then reconstruct the
+ * {@code CryptoObject}using the constructor that takes in an {@code operationHandle}, and
+ * pass that in to the {@code authenticate} API mentioned above.
*/
@FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
public long getOperationHandle() {
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 8d3ea3f..81b4c21 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -97,6 +97,10 @@
mCrypto = keyAgreement;
}
+ public CryptoObject(long operationHandle) {
+ mCrypto = operationHandle;
+ }
+
/**
* Get {@link Signature} object.
* @return {@link Signature} object or null if this doesn't contain one.
@@ -157,6 +161,8 @@
public long getOpId() {
if (mCrypto == null) {
return 0;
+ } else if (mCrypto instanceof Long) {
+ return (long) mCrypto;
} else if (mCrypto instanceof IdentityCredential) {
return ((IdentityCredential) mCrypto).getCredstoreOperationHandle();
} else if (mCrypto instanceof PresentationSession) {
diff --git a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
index 43b965b..b5982d4 100644
--- a/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
+++ b/core/java/android/hardware/biometrics/PromptContentViewParcelable.java
@@ -26,5 +26,5 @@
*/
@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
sealed interface PromptContentViewParcelable extends PromptContentView, Parcelable
- permits PromptVerticalListContentView {
+ permits PromptVerticalListContentView, PromptContentViewWithMoreOptionsButton {
}
diff --git a/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
new file mode 100644
index 0000000..9ebfa8f
--- /dev/null
+++ b/core/java/android/hardware/biometrics/PromptContentViewWithMoreOptionsButton.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.biometrics;
+
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
+import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.DialogInterface;
+import android.hardware.biometrics.BiometricPrompt.ButtonInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Contains the information of the template of content view with a more options button for Biometric
+ * Prompt.
+ * This button should be used to provide more options for sign in or other purposes, such as when a
+ * user needs to select between multiple app-specific accounts or profiles that are available for
+ * sign in. This is not common and apps should avoid using it if there is only one choice available
+ * or if the user has already selected the appropriate account to use before invoking
+ * BiometricPrompt because it will create additional steps that the user must navigate through.
+ * Clicking the more options button will dismiss the prompt, provide the app an opportunity to ask
+ * the user for the correct account, &finally allow the app to decide how to proceed once selected.
+ * <p>
+ * Here's how you'd set a <code>PromptContentViewWithMoreOptionsButton</code> on a Biometric Prompt:
+ * <pre class="prettyprint">
+ * BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(...)
+ * .setTitle(...)
+ * .setSubTitle(...)
+ * .setContentView(new PromptContentViewWithMoreOptionsButton.Builder()
+ * .setDescription("test description")
+ * .setMoreOptionsButtonListener(executor, listener)
+ * .build())
+ * .build();
+ * </pre>
+ */
+@FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+public final class PromptContentViewWithMoreOptionsButton implements PromptContentViewParcelable {
+
+ private final String mDescription;
+ private DialogInterface.OnClickListener mListener;
+ private ButtonInfo mButtonInfo;
+
+ private PromptContentViewWithMoreOptionsButton(
+ @NonNull String description, @NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ mDescription = description;
+ mListener = listener;
+ mButtonInfo = new ButtonInfo(executor, listener);
+ }
+
+ private PromptContentViewWithMoreOptionsButton(Parcel in) {
+ mDescription = in.readString();
+ }
+
+ /**
+ * Gets the description for the content view, as set by
+ * {@link PromptContentViewWithMoreOptionsButton.Builder#setDescription(String)}.
+ *
+ * @return The description for the content view, or null if the content view has no description.
+ */
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+ @Nullable
+ public String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Gets the click listener for the more options button on the content view, as set by
+ * {@link PromptContentViewWithMoreOptionsButton.Builder#setMoreOptionsButtonListener(Executor,
+ * DialogInterface.OnClickListener)}.
+ *
+ * @return The click listener for the more options button on the content view.
+ */
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+ @NonNull
+ public DialogInterface.OnClickListener getMoreOptionsButtonListener() {
+ return mListener;
+ }
+
+ ButtonInfo getButtonInfo() {
+ return mButtonInfo;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mDescription);
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ @NonNull
+ public static final Creator<PromptContentViewWithMoreOptionsButton> CREATOR = new Creator<>() {
+ @Override
+ public PromptContentViewWithMoreOptionsButton createFromParcel(Parcel in) {
+ return new PromptContentViewWithMoreOptionsButton(in);
+ }
+
+ @Override
+ public PromptContentViewWithMoreOptionsButton[] newArray(int size) {
+ return new PromptContentViewWithMoreOptionsButton[size];
+ }
+ };
+
+ public static final class Builder {
+ private String mDescription;
+ private Executor mExecutor;
+ private DialogInterface.OnClickListener mListener;
+
+ /**
+ * Optional: Sets a description that will be shown on the content view. Note that there are
+ * limits on the number of characters allowed for description.
+ *
+ * @param description The description to display.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+ public Builder setDescription(@NonNull String description) {
+ mDescription = description;
+ return this;
+ }
+
+ /**
+ * Required: Sets the executor and click listener for the more options button on the
+ * prompt content.
+ * This button should be used to provide more options for sign in or other purposes, such
+ * as when a user needs to select between multiple app-specific accounts or profiles that
+ * are available for sign in. This is not common and apps should avoid using it if there
+ * is only one choice available or if the user has already selected the appropriate
+ * account to use before invoking BiometricPrompt because it will create additional steps
+ * that the user must navigate through. Clicking the more options button will dismiss the
+ * prompt, provide the app an opportunity to ask the user for the correct account, &finally
+ * allow the app to decide how to proceed once selected.
+ *
+ * @param executor Executor that will be used to run the on click callback.
+ * @param listener Listener containing a callback to be run when the button is pressed.
+ * @return This builder.
+ */
+ @NonNull
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+ public Builder setMoreOptionsButtonListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull DialogInterface.OnClickListener listener) {
+ if (executor == null) {
+ throw new IllegalArgumentException("Executor must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener must not be null");
+ }
+ mExecutor = executor;
+ mListener = listener;
+ return this;
+ }
+
+
+ /**
+ * Creates a {@link PromptContentViewWithMoreOptionsButton}.
+ *
+ * @return An instance of {@link PromptContentViewWithMoreOptionsButton}.
+ */
+ @NonNull
+ @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED)
+ public PromptContentViewWithMoreOptionsButton build() {
+ if (mListener == null) {
+ throw new IllegalArgumentException(
+ "The listener of more options button on prompt content must be set if "
+ + "PromptContentViewWithMoreOptionsButton is used.");
+ }
+ return new PromptContentViewWithMoreOptionsButton(mDescription, mExecutor, mListener);
+ }
+ }
+}
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 8bb9585..8143213 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -172,7 +172,7 @@
}
/**
- * Returns whether SET_BIOMETRIC_DIALOG_LOGO is contained.
+ * Returns whether SET_BIOMETRIC_DIALOG_ADVANCED is contained.
*/
public boolean containsSetLogoApiConfigurations() {
if (mLogoRes != -1) {
@@ -191,6 +191,15 @@
public boolean shouldUseParentProfileForDeviceCredential() {
return mUseParentProfileForDeviceCredential;
}
+
+ /**
+ * Returns if the PromptContentViewWithMoreOptionsButton is set.
+ */
+ public boolean isContentViewMoreOptionsButtonUsed() {
+ return Flags.customBiometricPrompt() && mContentView != null
+ && mContentView instanceof PromptContentViewWithMoreOptionsButton;
+ }
+
// LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
// Setters
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 991bade..ec9b013 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -585,6 +585,11 @@
* <p>Configuring a session with an empty or null list will close the current session, if
* any. This can be used to release the current session's target surfaces for another use.</p>
*
+ * <p>This function throws an {@code IllegalArgumentException} if called with a
+ * SessionConfiguration lacking state callbacks or valid output surfaces. The only exceptions
+ * are deferred SurfaceView or SurfaceTexture outputs. See {@link
+ * OutputConfiguration#OutputConfiguration(Size, Class)} for details.</p>
+ *
* <h3>Regular capture</h3>
*
* <p>While any of the sizes from {@link StreamConfigurationMap#getOutputSizes} can be used when
@@ -1675,19 +1680,32 @@
*
* <p><b>IMPORTANT:</b></p>
* <ul>
- * <li>If a feature support can be queried via
+ * <li>If feature support can be queried via
* {@link CameraCharacteristics#SCALER_MANDATORY_STREAM_COMBINATIONS} or
* {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}, applications should
- * directly use it rather than calling this function as: (1) using
- * {@code CameraCharacteristics} is more efficient, and (2) calling this function with on
- * non-supported devices will throw a {@link UnsupportedOperationException}.
+ * directly use that route rather than calling this function as: (1) using
+ * {@code CameraCharacteristics} is more efficient, and (2) calling this function with
+ * certain non-supported features will throw a {@link IllegalArgumentException}.</li>
*
- * <li>To minimize latency of {@link SessionConfiguration} creation, applications can
- * use deferred surfaces for SurfaceView and SurfaceTexture to avoid waiting for UI
- * creation before setting up the camera. For {@link android.media.MediaRecorder} and
- * {@link android.media.MediaCodec} uses, applications can use {@code ImageReader} with
- * {@link android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE}. The lightweight nature of
- * {@code ImageReader} helps minimize the latency cost.
+ * <li>To minimize {@link SessionConfiguration} creation latency due to its dependency on
+ * output surfaces, the application can call this method before acquiring valid
+ * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture},
+ * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec}, or {@link
+ * android.media.ImageReader} surfaces. For {@link android.view.SurfaceView},
+ * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, and
+ * {@link android.media.MediaCodec}, the application can call
+ * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. For {@link
+ * android.media.ImageReader}, the application can call {@link
+ * OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, Size, long)}, or {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size, long)}. The {@link
+ * SessionConfiguration} can then be created using the OutputConfiguration objects and
+ * be used to query whether it's supported by the camera device. To create the
+ * CameraCaptureSession, the application still needs to make sure all output surfaces
+ * are added via {@link OutputConfiguration#addSurfaces} with the exception of deferred
+ * surfaces for {@link android.view.SurfaceView} and
+ * {@link android.graphics.SurfaceTexture}.</li>
* </ul>
*
* @return {@code true} if the given session configuration is supported by the camera
@@ -1706,8 +1724,8 @@
@NonNull SessionConfiguration config) throws CameraAccessException;
/**
- * <p>Get camera characteristics for a particular session configuration for this camera
- * device</p>
+ * Get camera characteristics for a particular session configuration for this camera
+ * device.
*
* <p>The camera characteristics returned by this method are different from those returned
* from {@link CameraManager#getCameraCharacteristics}. The characteristics returned here
@@ -1718,6 +1736,24 @@
* <p>Other than that, the characteristics returned here can be used in the same way as
* those returned from {@link CameraManager#getCameraCharacteristics}.</p>
*
+ * <p>To optimize latency, the application can call this method before acquiring valid
+ * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture},
+ * {@link android.media.MediaRecorder}, {@link android.media.MediaCodec}, or {@link
+ * android.media.ImageReader} surfaces. For {@link android.view.SurfaceView},
+ * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, and
+ * {@link android.media.MediaCodec}, the application can call
+ * {@link OutputConfiguration#OutputConfiguration(Size, Class)}. For {@link
+ * android.media.ImageReader}, the application can call {@link
+ * OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, Size, long)}, or {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size, long)}. The {@link
+ * SessionConfiguration} can then be created using the OutputConfiguration objects and
+ * be used for this function. To create the CameraCaptureSession, the application still
+ * needs to make sure all output surfaces are added via {@link
+ * OutputConfiguration#addSurfaces} with the exception of deferred surfaces for {@link
+ * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}.</p>
+ *
* @param sessionConfig The session configuration for which characteristics are fetched.
* @return CameraCharacteristics specific to a given session configuration.
*
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 6962811..eb644e8 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -229,9 +229,10 @@
StreamConfigurationMap streamMap) {
ArrayList<Size> ret = getSupportedSizes(sizesList, format);
- if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888) {
+ if (format == ImageFormat.JPEG || format == ImageFormat.YUV_420_888 ||
+ format == ImageFormat.PRIVATE) {
// Per API contract it is assumed that the extension is able to support all
- // camera advertised sizes for JPEG and YUV_420_888 in case it doesn't return
+ // camera advertised sizes for JPEG, YUV_420_888 and PRIVATE in case it doesn't return
// a valid non-empty size list.
Size[] supportedSizes = streamMap.getOutputSizes(format);
if ((ret.isEmpty()) && (supportedSizes != null)) {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index d2e4a61..b43a900 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -47,6 +47,7 @@
import android.hardware.camera2.utils.CameraIdAndSessionConfiguration;
import android.hardware.camera2.utils.ConcurrentCameraIdCombination;
import android.hardware.camera2.utils.ExceptionUtils;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.os.Binder;
@@ -204,14 +205,12 @@
mDeviceStateListeners.add(new WeakReference<>(listener));
}
+ @SuppressWarnings("FlaggedApi")
@Override
- public final void onBaseStateChanged(int state) {
- handleStateChange(state);
- }
-
- @Override
- public final void onStateChanged(int state) {
- handleStateChange(state);
+ public void onDeviceStateChanged(DeviceState state) {
+ // Suppressing the FlaggedAPI warning as this specific API isn't new, just moved to
+ // system API which requires it to be flagged.
+ handleStateChange(state.getIdentifier());
}
}
diff --git a/core/java/android/hardware/camera2/MultiResolutionImageReader.java b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
index 8a18a0d..116928b 100644
--- a/core/java/android/hardware/camera2/MultiResolutionImageReader.java
+++ b/core/java/android/hardware/camera2/MultiResolutionImageReader.java
@@ -16,6 +16,8 @@
package android.hardware.camera2;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
@@ -26,20 +28,15 @@
import android.graphics.ImageFormat.Format;
import android.hardware.HardwareBuffer;
import android.hardware.HardwareBuffer.Usage;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.media.Image;
import android.media.ImageReader;
-import android.hardware.camera2.params.MultiResolutionStreamInfo;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
+import android.util.Size;
import android.view.Surface;
import com.android.internal.camera.flags.Flags;
-import java.nio.NioUtils;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -351,6 +348,52 @@
}
/**
+ * Get the internal ImageReader surface based on configured size and physical camera Id.
+ *
+ * <p>The {@code configuredSize} and {@code physicalCameraId} parameters must match one of the
+ * MultiResolutionStreamInfo used to create this {@link MultiResolutionImageReader}.</p>
+ *
+ * <p>The Surface returned from this function isn't meant to be used directly as part of a
+ * {@link CaptureRequest}. It should instead be used for creating an OutputConfiguration
+ * before session creation. See {@link OutputConfiguration#setSurfacesForMultiResolutionOutput}
+ * for details. For {@link CaptureRequest}, use {@link #getSurface()} instead.</p>
+ *
+ * <p>Please note that holding on to the Surface objects returned by this method is not enough
+ * to keep their parent MultiResolutionImageReaders from being reclaimed. In that sense, a
+ * Surface acts like a {@link java.lang.ref.WeakReference weak reference} to the
+ * MultiResolutionImageReader that provides it.</p>
+ *
+ * @param configuredSize The configured size corresponding to one of the internal ImageReader.
+ * @param physicalCameraId The physical camera Id the internal ImageReader targets for. If
+ * the ImageReader is not targeting a physical camera of a logical
+ * multi-camera, this parameter is set to "".
+ *
+ * @return The {@link Surface} of the internal ImageReader corresponding to the provided
+ * configured size and physical camera Id.
+ *
+ * @throws IllegalArgumentException If {@code configuredSize} is {@code null}, or the ({@code
+ * configuredSize} and {@code physicalCameraId}) combo is not
+ * part of this {@code MultiResolutionImageReader}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public @NonNull Surface getSurface(@NonNull Size configuredSize,
+ @NonNull String physicalCameraId) {
+ checkNotNull(configuredSize, "configuredSize must not be null");
+ checkNotNull(physicalCameraId, "physicalCameraId must not be null");
+
+ for (int i = 0; i < mStreamInfo.length; i++) {
+ if (mStreamInfo[i].getWidth() == configuredSize.getWidth()
+ && mStreamInfo[i].getHeight() == configuredSize.getHeight()
+ && physicalCameraId.equals(mStreamInfo[i].getPhysicalCameraId())) {
+ return mReaders[i].getSurface();
+ }
+ }
+ throw new IllegalArgumentException("configuredSize and physicalCameraId don't match with "
+ + "this MultiResolutionImageReader");
+ }
+
+ /**
* Get the surface that is used as a target for {@link CaptureRequest}
*
* <p>The application must use the surface returned by this function as a target for
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
index 0f199b1..9e01438 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java
@@ -90,7 +90,6 @@
@Override
public boolean isSessionConfigurationSupported(@NonNull SessionConfiguration config)
throws CameraAccessException {
- // TODO(b/298033056): restructure the OutputConfiguration API for better usability
synchronized (mInterfaceLock) {
if (mCameraManager.isCameraServiceDisabled()) {
throw new IllegalArgumentException("No cameras available on device");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 8aacd5e..dda52dd 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.Preconditions.*;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,9 @@
import android.annotation.TestApi;
import android.graphics.ColorSpace;
import android.graphics.ImageFormat;
+import android.graphics.ImageFormat.Format;
+import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -43,6 +47,8 @@
import android.util.Size;
import android.view.Surface;
+import com.android.internal.camera.flags.Flags;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -50,6 +56,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A class for describing camera output, which contains a {@link Surface} and its specific
@@ -361,6 +368,21 @@
private final int SURFACE_TYPE_SURFACE_TEXTURE = 1;
/**
+ * The surface is obtained from {@link android.media.MediaRecorder}.
+ */
+ private static final int SURFACE_TYPE_MEDIA_RECORDER = 2;
+
+ /**
+ * The surface is obtained from {@link android.media.MediaCodec}.
+ */
+ private static final int SURFACE_TYPE_MEDIA_CODEC = 3;
+
+ /**
+ * The surface is obtained from {@link android.media.ImageReader}.
+ */
+ private static final int SURFACE_TYPE_IMAGE_READER = 4;
+
+ /**
* Maximum number of surfaces supported by one {@link OutputConfiguration}.
*
* <p>The combined number of surfaces added by the constructor and
@@ -576,6 +598,7 @@
mMirrorMode = MIRROR_MODE_AUTO;
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
+ mUsage = 0;
}
/**
@@ -592,13 +615,7 @@
@NonNull MultiResolutionImageReader multiResolutionImageReader) {
checkNotNull(multiResolutionImageReader, "Multi-resolution image reader must not be null");
- int groupId = MULTI_RESOLUTION_GROUP_ID_COUNTER;
- MULTI_RESOLUTION_GROUP_ID_COUNTER++;
- // Skip in case the group id counter overflows to -1, the invalid value.
- if (MULTI_RESOLUTION_GROUP_ID_COUNTER == -1) {
- MULTI_RESOLUTION_GROUP_ID_COUNTER++;
- }
-
+ int groupId = getAndIncreaseMultiResolutionGroupId();
ImageReader[] imageReaders = multiResolutionImageReader.getReaders();
ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
for (int i = 0; i < imageReaders.length; i++) {
@@ -620,6 +637,115 @@
}
/**
+ * Create a list of {@link OutputConfiguration} instances for a
+ * {@link android.hardware.camera2.params.MultiResolutionImageReader}.
+ *
+ * <p>This method can be used to create query OutputConfigurations for a
+ * MultiResolutionImageReader that can be included in a SessionConfiguration passed into
+ * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up
+ * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput}
+ * can be used to link to the actual MultiResolutionImageReader.</p>
+ *
+ * <p>This constructor takes same arguments used to create a {@link
+ * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo}
+ * objects and the format.</p>
+ *
+ * @param streams The group of multi-resolution stream info objects, which are used to create a
+ * multi-resolution image reader containing a number of ImageReaders.
+ * @param format The format of the MultiResolutionImageReader. This must be one of the {@link
+ * android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants
+ * supported by the camera device. Note that not all formats are supported, like
+ * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be
+ * queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}.
+ *
+ * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader.
+ *
+ * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain
+ * at least 2 items, or if {@code format} isn't a valid camera
+ * format.
+ *
+ * @see MultiResolutionImageReader
+ * @see MultiResolutionStreamInfo
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public static @NonNull List<OutputConfiguration> createInstancesForMultiResolutionOutput(
+ @NonNull Collection<MultiResolutionStreamInfo> streams,
+ @Format int format) {
+ if (streams == null || streams.size() <= 1) {
+ throw new IllegalArgumentException(
+ "The streams list must contain at least 2 entries");
+ }
+ if (format == ImageFormat.NV21) {
+ throw new IllegalArgumentException(
+ "NV21 format is not supported");
+ }
+
+ int groupId = getAndIncreaseMultiResolutionGroupId();
+ ArrayList<OutputConfiguration> configs = new ArrayList<OutputConfiguration>();
+ for (MultiResolutionStreamInfo stream : streams) {
+ Size surfaceSize = new Size(stream.getWidth(), stream.getHeight());
+ OutputConfiguration config = new OutputConfiguration(
+ groupId, format, surfaceSize);
+ config.setPhysicalCameraId(stream.getPhysicalCameraId());
+ config.setMultiResolutionOutput();
+ configs.add(config);
+
+ // No need to call addSensorPixelModeUsed for ultra high resolution sensor camera,
+ // because regular and max resolution output configurations are used for DEFAULT mode
+ // and MAX_RESOLUTION mode respectively by default.
+ }
+
+ return configs;
+ }
+
+ /**
+ * Set the OutputConfiguration surfaces corresponding to the {@link MultiResolutionImageReader}.
+ *
+ * <p>This function should be used together with {@link
+ * #createInstancesForMultiResolutionOutput}. The application calls {@link
+ * #createInstancesForMultiResolutionOutput} first to create a list of
+ * OutputConfiguration objects without the actual MultiResolutionImageReader.
+ * Once the MultiResolutionImageReader is created later during full camera setup, the
+ * application then calls this function to assign the surfaces to the OutputConfiguration
+ * instances.</p>
+ *
+ * @param outputConfigurations The OutputConfiguration objects created by {@link
+ * #createInstancesFromMultiResolutionOutput}
+ * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same
+ * MultiResolutionStreamInfo parameters as
+ * {@code outputConfigurations}.
+ * @throws IllegalArgumentException If {@code outputConfigurations} or {@code
+ * multiResolutionImageReader} is {@code null}, the {@code
+ * outputConfigurations} and {@code multiResolutionImageReader}
+ * sizes don't match, or if the
+ * {@code multiResolutionImageReader}'s surfaces don't match
+ * with the {@code outputConfigurations}.
+ * @throws IllegalStateException If {@code outputConfigurations} already contains valid output
+ * surfaces.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public static void setSurfacesForMultiResolutionOutput(
+ @NonNull Collection<OutputConfiguration> outputConfigurations,
+ @NonNull MultiResolutionImageReader multiResolutionImageReader) {
+ checkNotNull(outputConfigurations, "outputConfigurations must not be null");
+ checkNotNull(multiResolutionImageReader, "multiResolutionImageReader must not be null");
+ if (outputConfigurations.size() != multiResolutionImageReader.getReaders().length) {
+ throw new IllegalArgumentException(
+ "outputConfigurations and multiResolutionImageReader sizes must match");
+ }
+
+ for (OutputConfiguration config : outputConfigurations) {
+ String physicalCameraId = config.getPhysicalCameraId();
+ if (physicalCameraId == null) {
+ physicalCameraId = "";
+ }
+ Surface surface = multiResolutionImageReader.getSurface(config.getConfiguredSize(),
+ physicalCameraId);
+ config.addSurface(surface);
+ }
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance, with desired Surface size and Surface
* source class.
* <p>
@@ -628,30 +754,64 @@
* with a deferred Surface. The application can use this output configuration to create a
* session.
* </p>
- * <p>
- * However, the actual output Surface must be set via {@link #addSurface} and the deferred
- * Surface configuration must be finalized via {@link
- * CameraCaptureSession#finalizeOutputConfigurations} before submitting a request with this
- * Surface target. The deferred Surface can only be obtained either from {@link
- * android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface}, or from
+ *
+ * <p>Starting from {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V},
+ * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView}
+ * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from
* {@link android.graphics.SurfaceTexture} via
- * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).
- * </p>
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link
+ * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link
+ * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link
+ * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link
+ * android.media.MediaCodec#createPersistentInputSource}.</p>
+ *
+ * <ul>
+ * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture}
+ * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the
+ * output Surface must be set via {@link #addSurface}, and the Surface configuration must be
+ * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting
+ * a request with the Surface target.</li>
+ * <li>For all other target types, the output Surface must be set by {@link #addSurface},
+ * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the
+ * OutputConfiguration used to create the session will contain the actual Surface.</li>
+ * </ul>
+ *
+ * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link
+ * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both
+ * kind of outputs can be deferred until after {@link
+ * CameraDevice#createCaptureSessionByOutputConfiguration}.</p>
+ *
+ * <p>An OutputConfiguration object created by this constructor can be used for {@link
+ * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
*
* @param surfaceSize Size for the deferred surface.
* @param klass a non-{@code null} {@link Class} object reference that indicates the source of
- * this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class} and
- * {@link android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
+ * this surface. Only {@link android.view.SurfaceHolder SurfaceHolder.class},
+ * {@link android.graphics.SurfaceTexture SurfaceTexture.class}, {@link
+ * android.media.MediaRecorder MediaRecorder.class}, and
+ * {@link android.media.MediaCodec MediaCodec.class} are supported.
+ * Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only
+ * {@link android.view.SurfaceHolder SurfaceHolder.class} and {@link
+ * android.graphics.SurfaceTexture SurfaceTexture.class} are supported.
* @throws IllegalArgumentException if the Surface source class is not supported, or Surface
* size is zero.
*/
public <T> OutputConfiguration(@NonNull Size surfaceSize, @NonNull Class<T> klass) {
- checkNotNull(klass, "surfaceSize must not be null");
+ checkNotNull(surfaceSize, "surfaceSize must not be null");
checkNotNull(klass, "klass must not be null");
if (klass == android.view.SurfaceHolder.class) {
mSurfaceType = SURFACE_TYPE_SURFACE_VIEW;
+ mIsDeferredConfig = true;
} else if (klass == android.graphics.SurfaceTexture.class) {
mSurfaceType = SURFACE_TYPE_SURFACE_TEXTURE;
+ mIsDeferredConfig = true;
+ } else if (klass == android.media.MediaRecorder.class) {
+ mSurfaceType = SURFACE_TYPE_MEDIA_RECORDER;
+ mIsDeferredConfig = false;
+ } else if (klass == android.media.MediaCodec.class) {
+ mSurfaceType = SURFACE_TYPE_MEDIA_CODEC;
+ mIsDeferredConfig = false;
} else {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
throw new IllegalArgumentException("Unknown surface source class type");
@@ -668,7 +828,6 @@
mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
mConfiguredGenerationId = 0;
- mIsDeferredConfig = true;
mIsShared = false;
mPhysicalCameraId = null;
mIsMultiResolution = false;
@@ -678,6 +837,131 @@
mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
mReadoutTimestampEnabled = false;
mIsReadoutSensorTimestampBase = false;
+ mUsage = 0;
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+ * format and size.
+ *
+ * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+ * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+ * before creating the capture session.</p>
+ *
+ * <p>An OutputConfiguration object created by this constructor can be used for {@link
+ * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ *
+ * @param format The format of the ImageReader output. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported by the camera device.
+ * @param surfaceSize Size for the ImageReader surface.
+ * @throws IllegalArgumentException if the Surface size is null or zero.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public OutputConfiguration(@Format int format, @NonNull Size surfaceSize) {
+ this(format, surfaceSize,
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+ * surfaceGroupId, format, and size.
+ *
+ * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+ * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+ * before creating the capture session.</p>
+ *
+ * <p>An OutputConfiguration object created by this constructor can be used for {@link
+ * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ *
+ * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
+ * outputs.
+ * @param format The format of the ImageReader output. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported by the camera device.
+ * @param surfaceSize Size for the ImageReader surface.
+ * @throws IllegalArgumentException if the Surface size is null or zero.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public OutputConfiguration(int surfaceGroupId, @Format int format, @NonNull Size surfaceSize) {
+ this(surfaceGroupId, format, surfaceSize,
+ format == ImageFormat.PRIVATE ? 0 : HardwareBuffer.USAGE_CPU_READ_OFTEN);
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+ * format, size, and usage flags.
+ *
+ * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+ * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+ * before creating the capture session.</p>
+ *
+ * <p>An OutputConfiguration object created by this constructor can be used for {@link
+ * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ *
+ * @param format The format of the ImageReader output. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported by the camera device.
+ * @param surfaceSize Size for the ImageReader surface.
+ * @param usage The usage flags of the ImageReader output surface.
+ * @throws IllegalArgumentException if the Surface size is null or zero.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public OutputConfiguration(@Format int format, @NonNull Size surfaceSize, @Usage long usage) {
+ this(SURFACE_GROUP_ID_NONE, format, surfaceSize, usage);
+ }
+
+ /**
+ * Create a new {@link OutputConfiguration} instance for an {@link ImageReader} for a given
+ * surface group id, format, size, and usage flags.
+ *
+ * <p>This constructor creates an OutputConfiguration for an ImageReader without providing
+ * the actual output Surface. The actual output Surface must be set via {@link #addSurface}
+ * before creating the capture session.</p>
+ *
+ * <p>An OutputConfiguration object created by this constructor can be used for {@link
+ * CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p>
+ *
+ * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple
+ * outputs.
+ * @param format The format of the ImageReader output. This must be one of the
+ * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat}
+ * constants. Note that not all formats are supported by the camera device.
+ * @param surfaceSize Size for the ImageReader surface.
+ * @param usage The usage flags of the ImageReader output surface.
+ * @throws IllegalArgumentException if the Surface size is null or zero.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public OutputConfiguration(int surfaceGroupId, @Format int format,
+ @NonNull Size surfaceSize, @Usage long usage) {
+ checkNotNull(surfaceSize, "surfaceSize must not be null");
+ if (surfaceSize.getWidth() == 0 || surfaceSize.getHeight() == 0) {
+ throw new IllegalArgumentException("Surface size needs to be non-zero");
+ }
+
+ mSurfaceType = SURFACE_TYPE_IMAGE_READER;
+ mSurfaceGroupId = surfaceGroupId;
+ mSurfaces = new ArrayList<Surface>();
+ mRotation = ROTATION_0;
+ mConfiguredSize = surfaceSize;
+ mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(format);
+ mConfiguredDataspace = StreamConfigurationMap.imageFormatToDataspace(format);
+ mConfiguredGenerationId = 0;
+ mIsDeferredConfig = false;
+ mIsShared = false;
+ mPhysicalCameraId = null;
+ mIsMultiResolution = false;
+ mSensorPixelModesUsed = new ArrayList<Integer>();
+ mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mColorSpace = ColorSpaceProfiles.UNSPECIFIED;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
+ mReadoutTimestampEnabled = false;
+ mIsReadoutSensorTimestampBase = false;
+ mUsage = usage;
}
/**
@@ -852,7 +1136,8 @@
/**
* Check if this configuration has deferred configuration.
*
- * <p>This will return true if the output configuration was constructed with surface deferred by
+ * <p>This will return true if the output configuration was constructed with {@link
+ * android.view.SurfaceView} or {@link android.graphics.SurfaceTexture} deferred by
* {@link OutputConfiguration#OutputConfiguration(Size, Class)}. It will return true even after
* the deferred surface is added later by {@link OutputConfiguration#addSurface}.</p>
*
@@ -872,13 +1157,28 @@
* {@link CameraCaptureSession#finalizeOutputConfigurations}. It is possible to call this method
* after the output configurations have been finalized only in cases of enabled surface sharing
* see {@link #enableSurfaceSharing}. The modified output configuration must be updated with
- * {@link CameraCaptureSession#updateOutputConfiguration}.</p>
+ * {@link CameraCaptureSession#updateOutputConfiguration}. If this function is called before
+ * session creation, {@link CameraCaptureSession#finalizeOutputConfigurations} doesn't need to
+ * be called.</p>
*
- * <p> If the OutputConfiguration was constructed with a deferred surface by {@link
- * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained
- * from {@link android.view.SurfaceView} by calling {@link android.view.SurfaceHolder#getSurface},
- * or from {@link android.graphics.SurfaceTexture} via
- * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}).</p>
+ * <p> If the OutputConfiguration was constructed by {@link
+ * OutputConfiguration#OutputConfiguration(Size, Class)}, the added surface must be obtained:
+ * <ul>
+ * <li>from {@link android.view.SurfaceView} by calling
+ * {@link android.view.SurfaceHolder#getSurface}</li>
+ * <li>from {@link android.graphics.SurfaceTexture} by calling
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}</li>
+ * <li>from {@link android.media.MediaRecorder} by calling
+ * {@link android.media.MediaRecorder#getSurface} or {@link
+ * android.media.MediaCodec#createPersistentInputSurface}</li>
+ * <li>from {@link android.media.MediaCodce} by calling
+ * {@link android.media.MediaCodec#createInputSurface} or {@link
+ * android.media.MediaCodec#createPersistentInputSource}</li>
+ * </ul>
+ *
+ * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)}
+ * or its variants, the added surface must be obtained from {@link android.media.ImageReader}
+ * by calling {@link android.media.ImageReader#getSurface}.</p>
*
* <p> If the OutputConfiguration was constructed by other constructors, the added
* surface must be compatible with the existing surface. See {@link #enableSurfaceSharing} for
@@ -934,9 +1234,13 @@
*
* <p> Surfaces added via calls to {@link #addSurface} can also be removed from the
* OutputConfiguration. The only notable exception is the surface associated with
- * the OutputConfiguration see {@link #getSurface} which was passed as part of the constructor
- * or was added first in the deferred case
- * {@link OutputConfiguration#OutputConfiguration(Size, Class)}.</p>
+ * the OutputConfiguration (see {@link #getSurface}) which was passed as part of the
+ * constructor or was added first in the case of
+ * {@link OutputConfiguration#OutputConfiguration(Size, Class)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, Size, long)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size)}, {@link
+ * OutputConfiguration#OutputConfiguration(int, int, Size, long)}.</p>
*
* @param surface The surface to be removed.
*
@@ -945,6 +1249,7 @@
* with {@link #addSurface}.
*/
public void removeSurface(@NonNull Surface surface) {
+ checkNotNull(surface, "Surface must not be null");
if (getSurface() == surface) {
throw new IllegalArgumentException(
"Cannot remove surface associated with this output configuration");
@@ -1175,6 +1480,7 @@
this.mTimestampBase = other.mTimestampBase;
this.mMirrorMode = other.mMirrorMode;
this.mReadoutTimestampEnabled = other.mReadoutTimestampEnabled;
+ this.mUsage = other.mUsage;
}
/**
@@ -1203,6 +1509,9 @@
int timestampBase = source.readInt();
int mirrorMode = source.readInt();
boolean readoutTimestampEnabled = source.readInt() == 1;
+ int format = source.readInt();
+ int dataSpace = source.readInt();
+ long usage = source.readLong();
mSurfaceGroupId = surfaceSetId;
mRotation = rotation;
@@ -1211,6 +1520,7 @@
mIsDeferredConfig = isDeferred;
mIsShared = isShared;
mSurfaces = surfaces;
+ mUsage = 0;
if (mSurfaces.size() > 0) {
mSurfaceType = SURFACE_TYPE_UNKNOWN;
mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurfaces.get(0));
@@ -1218,9 +1528,16 @@
mConfiguredGenerationId = mSurfaces.get(0).getGenerationId();
} else {
mSurfaceType = surfaceType;
- mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(ImageFormat.PRIVATE);
- mConfiguredDataspace =
- StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+ if (mSurfaceType != SURFACE_TYPE_IMAGE_READER) {
+ mConfiguredFormat = StreamConfigurationMap.imageFormatToInternal(
+ ImageFormat.PRIVATE);
+ mConfiguredDataspace =
+ StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
+ } else {
+ mConfiguredFormat = format;
+ mConfiguredDataspace = dataSpace;
+ mUsage = usage;
+ }
mConfiguredGenerationId = 0;
}
mPhysicalCameraId = physicalCameraId;
@@ -1293,6 +1610,31 @@
return mSurfaceGroupId;
}
+ /**
+ * Get the configured size associated with this {@link OutputConfiguration}.
+ *
+ * @return The configured size associated with this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public Size getConfiguredSize() {
+ return mConfiguredSize;
+ }
+
+ /**
+ * Get the physical camera ID associated with this {@link OutputConfiguration}.
+ *
+ * <p>If this OutputConfiguration isn't targeting a physical camera of a logical
+ * multi-camera, this function returns {@code null}.</p>
+ *
+ * @return The physical camera Id associated with this {@link OutputConfiguration}.
+ *
+ * @hide
+ */
+ public @Nullable String getPhysicalCameraId() {
+ return mPhysicalCameraId;
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<OutputConfiguration> CREATOR =
new Parcelable.Creator<OutputConfiguration>() {
@Override
@@ -1353,6 +1695,9 @@
dest.writeInt(mTimestampBase);
dest.writeInt(mMirrorMode);
dest.writeInt(mReadoutTimestampEnabled ? 1 : 0);
+ dest.writeInt(mConfiguredFormat);
+ dest.writeInt(mConfiguredDataspace);
+ dest.writeLong(mUsage);
}
/**
@@ -1372,22 +1717,24 @@
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
- if (mRotation != other.mRotation ||
- !mConfiguredSize.equals(other.mConfiguredSize) ||
- mConfiguredFormat != other.mConfiguredFormat ||
- mSurfaceGroupId != other.mSurfaceGroupId ||
- mSurfaceType != other.mSurfaceType ||
- mIsDeferredConfig != other.mIsDeferredConfig ||
- mIsShared != other.mIsShared ||
- mConfiguredDataspace != other.mConfiguredDataspace ||
- mConfiguredGenerationId != other.mConfiguredGenerationId ||
- !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
- mIsMultiResolution != other.mIsMultiResolution ||
- mStreamUseCase != other.mStreamUseCase ||
- mTimestampBase != other.mTimestampBase ||
- mMirrorMode != other.mMirrorMode ||
- mReadoutTimestampEnabled != other.mReadoutTimestampEnabled)
+ if (mRotation != other.mRotation
+ || !mConfiguredSize.equals(other.mConfiguredSize)
+ || mConfiguredFormat != other.mConfiguredFormat
+ || mSurfaceGroupId != other.mSurfaceGroupId
+ || mSurfaceType != other.mSurfaceType
+ || mIsDeferredConfig != other.mIsDeferredConfig
+ || mIsShared != other.mIsShared
+ || mConfiguredDataspace != other.mConfiguredDataspace
+ || mConfiguredGenerationId != other.mConfiguredGenerationId
+ || !Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId)
+ || mIsMultiResolution != other.mIsMultiResolution
+ || mStreamUseCase != other.mStreamUseCase
+ || mTimestampBase != other.mTimestampBase
+ || mMirrorMode != other.mMirrorMode
+ || mReadoutTimestampEnabled != other.mReadoutTimestampEnabled
+ || mUsage != other.mUsage) {
return false;
+ }
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
}
@@ -1416,6 +1763,16 @@
}
/**
+ * Get and increase the next MultiResolution group id.
+ *
+ * If the ID reaches -1, skip it.
+ */
+ private static int getAndIncreaseMultiResolutionGroupId() {
+ return sNextMultiResolutionGroupId.getAndUpdate(i ->
+ i + 1 == SURFACE_GROUP_ID_NONE ? i + 2 : i + 1);
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -1430,7 +1787,8 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase,
- mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
+ mTimestampBase, mMirrorMode, mReadoutTimestampEnabled ? 1 : 0,
+ Long.hashCode(mUsage));
}
return HashCodeHelpers.hashCode(
@@ -1440,14 +1798,14 @@
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
mDynamicRangeProfile, mColorSpace, mStreamUseCase, mTimestampBase,
- mMirrorMode, mReadoutTimestampEnabled ? 1 : 0);
+ mMirrorMode, mReadoutTimestampEnabled ? 1 : 0, Long.hashCode(mUsage));
}
private static final String TAG = "OutputConfiguration";
// A surfaceGroupId counter used for MultiResolutionImageReader. Its value is
// incremented every time {@link createInstancesForMultiResolutionOutput} is called.
- private static int MULTI_RESOLUTION_GROUP_ID_COUNTER = 0;
+ private static AtomicInteger sNextMultiResolutionGroupId = new AtomicInteger(0);
private ArrayList<Surface> mSurfaces;
private final int mRotation;
@@ -1486,4 +1844,6 @@
private boolean mReadoutTimestampEnabled;
// Whether the timestamp base is set to READOUT_SENSOR
private boolean mIsReadoutSensorTimestampBase;
+ // The usage flags. Only set for instances created for ImageReader without specifying surface.
+ private long mUsage;
}
diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java
index 991f545..b0f354f 100644
--- a/core/java/android/hardware/camera2/params/SessionConfiguration.java
+++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java
@@ -20,6 +20,7 @@
import static com.android.internal.util.Preconditions.*;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +29,7 @@
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CameraDeviceSetup;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.InputConfiguration;
@@ -125,6 +127,34 @@
}
/**
+ * Create a new {@link SessionConfiguration} with sessionType and output configurations.
+ *
+ * <p>The SessionConfiguration objects created by this constructor can be used by
+ * {@link CameraDeviceSetup.isSessionConfigurationSupported} and {@link
+ * CameraDeviceSetup.getSessionCharacteristics} to query a camera device's feature
+ * combination support and session specific characteristics. For the SessionConfiguration
+ * object to be used to create a capture session, {@link #setCallback} must be called to
+ * specify the state callback function, and any incomplete OutputConfigurations must be
+ * completed via {@link OutputConfiguration#addSurface} or
+ * {@link OutputConfiguration#setSurfacesForMultiResolutionOutput} as appropriate.</p>
+ *
+ * @param sessionType The session type.
+ * @param outputs A list of output configurations for the capture session.
+ *
+ * @see #SESSION_REGULAR
+ * @see #SESSION_HIGH_SPEED
+ * @see CameraDevice#createCaptureSession(SessionConfiguration)
+ * @see CameraDeviceSetup#isSessionConfigurationSupported
+ * @see CameraDeviceSetup#getSessionCharacteristics
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public SessionConfiguration(@SessionMode int sessionType,
+ @NonNull List<OutputConfiguration> outputs) {
+ mSessionType = sessionType;
+ mOutputConfigurations = Collections.unmodifiableList(new ArrayList<>(outputs));
+ }
+
+ /**
* Create a SessionConfiguration from Parcel.
* No support for parcelable 'mStateCallback' and 'mExecutor' yet.
*/
@@ -376,4 +406,23 @@
return null;
}
}
+
+ /**
+ * Set the state callback and executor.
+ *
+ * <p>This function must be called for the SessionConfiguration object created via {@link
+ * #SessionConfiguration(int, List) SessionConfiguration(int, List<OutputConfiguration>)}
+ * before it's used to create a capture session.</p>
+ *
+ * @param executor The executor which should be used to invoke the callback. In general it is
+ * recommended that camera operations are not done on the main (UI) thread.
+ * @param cb A state callback interface implementation.
+ */
+ @FlaggedApi(Flags.FLAG_CAMERA_DEVICE_SETUP)
+ public void setCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CameraCaptureSession.StateCallback cb) {
+ mStateCallback = cb;
+ mExecutor = executor;
+ }
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index b85d686..b067095 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1509,6 +1509,8 @@
return HAL_DATASPACE_HEIF;
case ImageFormat.JPEG_R:
return HAL_DATASPACE_JPEG_R;
+ case ImageFormat.YUV_420_888:
+ return HAL_DATASPACE_JFIF;
default:
return HAL_DATASPACE_UNKNOWN;
}
@@ -2025,6 +2027,10 @@
* @hide
*/
public static final int HAL_DATASPACE_JPEG_R = 0x1005;
+ /**
+ * @hide
+ */
+ public static final int HAL_DATASPACE_JFIF = 0x8C20000;
private static final long DURATION_20FPS_NS = 50000000L;
/**
* @see #getDurations(int, int)
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index 76888f3..b214da2 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -52,78 +52,6 @@
@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
public final class DeviceState {
/**
- * Flag that indicates override requests should be cancelled when this device state becomes the
- * base device state.
- * @hide
- * @deprecated use {@link #PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS}
- */
- @Deprecated
- public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
-
- /**
- * Flag that indicates this device state is inaccessible for applications to be placed in. This
- * could be a device-state where the {@link Display#DEFAULT_DISPLAY} is not enabled.
- * @hide
- * @deprecated use {@link #PROPERTY_APP_INACCESSIBLE}
- */
- @Deprecated
- public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
-
- /**
- * Some device states can be both entered through a physical configuration as well as emulation
- * through {@link DeviceStateManager#requestState}, while some states can only be entered
- * through emulation and have no physical configuration to match.
- *
- * This flag indicates that the corresponding state can only be entered through emulation.
- * @hide
- * @deprecated use {@link #PROPERTY_EMULATED_ONLY}
- */
- @Deprecated
- public static final int FLAG_EMULATED_ONLY = 1 << 2;
-
- /**
- * This flag indicates that the corresponding state should be automatically canceled when the
- * requesting app is no longer on top. The app is considered not on top when (1) the top
- * activity in the system is from a different app, (2) the device is in sleep mode, or
- * (3) the keyguard shows up.
- * @hide
- * @deprecated use {@link #PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP}
- */
- @Deprecated
- public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;
-
- /**
- * This flag indicates that the corresponding state should be disabled when the device is
- * overheating and reaching the critical status.
- * @hide
- * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL}
- */
- @Deprecated
- public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
-
- /**
- * This flag indicates that the corresponding state should be disabled when power save mode
- * is enabled.
- * @hide
- * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE}
- */
- @Deprecated
- public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
-
- /** @hide */
- @IntDef(prefix = {"FLAG_"}, flag = true, value = {
- FLAG_CANCEL_OVERRIDE_REQUESTS,
- FLAG_APP_INACCESSIBLE,
- FLAG_EMULATED_ONLY,
- FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
- FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
- FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
- })
- @Deprecated
- @Retention(RetentionPolicy.SOURCE)
- public @interface DeviceStateFlags {}
-
- /**
* Property that indicates that a fold-in style foldable device is currently in a fully closed
* configuration.
*/
@@ -302,42 +230,11 @@
@NonNull
private final DeviceState.Configuration mDeviceStateConfiguration;
- @DeviceStateFlags
- private final int mFlags;
-
/** @hide */
+ @TestApi
public DeviceState(@NonNull DeviceState.Configuration deviceStateConfiguration) {
Objects.requireNonNull(deviceStateConfiguration, "Device StateConfiguration is null");
mDeviceStateConfiguration = deviceStateConfiguration;
- mFlags = 0;
- }
-
- /** @hide */
- public DeviceState(
- @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
- MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
- @NonNull String name,
- @NonNull Set<@DeviceStateProperties Integer> properties) {
- mDeviceStateConfiguration = new DeviceState.Configuration(identifier, name, properties,
- Collections.emptySet());
- mFlags = 0;
- }
-
- /**
- * @deprecated Deprecated in favor of {@link #DeviceState(int, String, Set)}
- * @hide
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @Deprecated
- public DeviceState(
- @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
- MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
- @NonNull String name,
- @DeviceStateFlags int flags) {
-
- mDeviceStateConfiguration = new DeviceState.Configuration(identifier, name,
- Collections.emptySet(), Collections.emptySet());
- mFlags = flags;
}
/** Returns the unique identifier for the device state. */
@@ -352,17 +249,6 @@
return mDeviceStateConfiguration.getName();
}
- /**
- * @hide
- * @deprecated in favor of {@link #hasProperty(int)} method
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @Deprecated
- @DeviceStateFlags
- public int getFlags() {
- return mFlags;
- }
-
@Override
public String toString() {
return "DeviceState{" + "identifier=" + mDeviceStateConfiguration.getIdentifier()
@@ -388,16 +274,6 @@
return Objects.hash(mDeviceStateConfiguration);
}
- /** Checks if a specific flag is set
- * @hide
- * @deprecated in favor of {@link #hasProperty(int)}
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @Deprecated
- public boolean hasFlag(int flagToCheckFor) {
- return (mFlags & flagToCheckFor) == flagToCheckFor;
- }
-
/**
* Checks if a specific property is set on this state
*/
@@ -438,6 +314,7 @@
* @see DeviceStateManager
* @hide
*/
+ @TestApi
public static final class Configuration implements Parcelable {
/** Unique identifier for the device state. */
@IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
@@ -563,29 +440,35 @@
};
/** @hide */
- public static class Builder {
+ @TestApi
+ public static final class Builder {
private final int mIdentifier;
+ @NonNull
private final String mName;
+ @NonNull
private Set<@SystemDeviceStateProperties Integer> mSystemProperties =
Collections.emptySet();
+ @NonNull
private Set<@PhysicalDeviceStateProperties Integer> mPhysicalProperties =
Collections.emptySet();
- public Builder(int identifier, String name) {
+ public Builder(int identifier, @NonNull String name) {
mIdentifier = identifier;
mName = name;
}
/** Sets the system properties for this {@link DeviceState.Configuration.Builder} */
+ @NonNull
public Builder setSystemProperties(
- Set<@SystemDeviceStateProperties Integer> systemProperties) {
+ @NonNull Set<@SystemDeviceStateProperties Integer> systemProperties) {
mSystemProperties = systemProperties;
return this;
}
/** Sets the system properties for this {@link DeviceState.Configuration.Builder} */
+ @NonNull
public Builder setPhysicalProperties(
- Set<@PhysicalDeviceStateProperties Integer> physicalProperties) {
+ @NonNull Set<@PhysicalDeviceStateProperties Integer> physicalProperties) {
mPhysicalProperties = physicalProperties;
return this;
}
@@ -594,6 +477,7 @@
* Returns a new {@link DeviceState.Configuration} whose values match the values set on
* the builder.
*/
+ @NonNull
public DeviceState.Configuration build() {
return new DeviceState.Configuration(mIdentifier, mName, mSystemProperties,
mPhysicalProperties);
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index a4c3833..febc24c1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -49,6 +49,7 @@
*
* @hide
*/
+ @TestApi
public static final int INVALID_DEVICE_STATE_IDENTIFIER = -1;
/**
@@ -96,20 +97,6 @@
/**
* Returns the list of device states that are supported and can be requested with
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
- * @deprecated use {@link #getSupportedDeviceStates()}
- * @hide
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @TestApi
- @Deprecated
- @NonNull
- public int[] getSupportedStates() {
- return mGlobal.getSupportedStates();
- }
-
- /**
- * Returns the list of device states that are supported and can be requested with
- * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
*/
@NonNull
public List<DeviceState> getSupportedDeviceStates() {
@@ -239,23 +226,6 @@
* Guaranteed to be called once on registration of the callback with the initial value and
* then on every subsequent change in the supported states.
*
- * @param supportedStates the new supported states.
- *
- * @see DeviceStateManager#getSupportedStates()
- * @deprecated use {@link #onSupportedStatesChanged(List)}
- * @hide
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @TestApi
- @Deprecated
- default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
-
- /**
- * Called in response to a change in the states supported by the device.
- * <p>
- * Guaranteed to be called once on registration of the callback with the initial value and
- * then on every subsequent change in the supported states.
- *
* The supported device states may change due to certain states becoming unavailable
* due to device configuration or device conditions such as if the device is too hot or
* external monitors have been connected.
@@ -267,42 +237,6 @@
default void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {}
/**
- * Called in response to a change in the base device state.
- * <p>
- * The base state is the state of the device without considering any requests made through
- * calls to {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}
- * from any client process. The base state is guaranteed to match the state provided with a
- * call to {@link #onStateChanged(int)} when there are no active requests from any process.
- * <p>
- * Guaranteed to be called once on registration of the callback with the initial value and
- * then on every subsequent change in the non-override state.
- *
- * @param state the new base device state.
- * @deprecated use {@link #onDeviceStateChanged(DeviceState)} and query for physical
- * properties that are relevant to your needs.
- * @hide
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @TestApi
- @Deprecated
- default void onBaseStateChanged(int state) {}
-
- /**
- * Called in response to device state changes.
- * <p>
- * Guaranteed to be called once on registration of the callback with the initial value and
- * then on every subsequent change in device state.
- *
- * @param state the new device state.
- * @deprecated use {@link #onDeviceStateChanged(DeviceState)}
- * @hide
- */
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- @TestApi
- @Deprecated
- void onStateChanged(int state);
-
- /**
* Called in response to device state changes.
* <p>
* Guaranteed to be called once on registration of the callback with the initial value and
@@ -310,8 +244,7 @@
*
* @param state the new device state.
*/
- // TODO(b/325124054): Make non-default and remove deprecated callback methods.
- default void onDeviceStateChanged(@NonNull DeviceState state) {}
+ void onDeviceStateChanged(@NonNull DeviceState state);
}
/**
@@ -340,9 +273,6 @@
}
@Override
- public final void onStateChanged(int state) {}
-
- @Override
public final void onDeviceStateChanged(@NonNull DeviceState deviceState) {
final boolean folded;
if (mFeatureFlags.deviceStatePropertyApi()) {
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index d6cc00d..0c84019 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -90,33 +90,6 @@
}
/**
- * Returns the set of supported device states.
- *
- * @see DeviceStateManager#getSupportedStates()
- */
- // TODO(b/325124054): Remove unused methods when clients are migrated.
- public int[] getSupportedStates() {
- synchronized (mLock) {
- final DeviceStateInfo currentInfo;
- if (mLastReceivedInfo != null) {
- // If we have mLastReceivedInfo a callback is registered for this instance and it
- // is receiving the most recent info from the server. Use that info here.
- currentInfo = mLastReceivedInfo;
- } else {
- // If mLastReceivedInfo is null there is no registered callback so we manually
- // fetch the current info.
- try {
- currentInfo = mDeviceStateManager.getDeviceStateInfo();
- } catch (RemoteException ex) {
- throw ex.rethrowFromSystemServer();
- }
- }
-
- return getSupportedStateIdentifiersLocked(currentInfo.supportedStates);
- }
- }
-
- /**
* Returns {@link List} of supported {@link DeviceState}s.
*
* @see DeviceStateManager#getSupportedDeviceStates()
@@ -264,14 +237,8 @@
mCallbacks.add(wrapper);
if (mLastReceivedInfo != null) {
- // Copy the array to prevent the callback from modifying the internal state.
- final int[] supportedStates = getSupportedStateIdentifiersLocked(
- mLastReceivedInfo.supportedStates);
- wrapper.notifySupportedStatesChanged(supportedStates);
wrapper.notifySupportedDeviceStatesChanged(
List.copyOf(mLastReceivedInfo.supportedStates));
- wrapper.notifyBaseStateChanged(mLastReceivedInfo.baseState.getIdentifier());
- wrapper.notifyStateChanged(mLastReceivedInfo.currentState.getIdentifier());
wrapper.notifyDeviceStateChanged(mLastReceivedInfo.currentState);
}
}
@@ -330,15 +297,6 @@
return -1;
}
- @GuardedBy("mLock")
- private int[] getSupportedStateIdentifiersLocked(List<DeviceState> states) {
- int[] identifiers = new int[states.size()];
- for (int i = 0; i < states.size(); i++) {
- identifiers[i] = states.get(i).getIdentifier();
- }
- return identifiers;
- }
-
@Nullable
private IBinder findRequestTokenLocked(@NonNull DeviceStateRequest request) {
for (int i = 0; i < mRequests.size(); i++) {
@@ -353,12 +311,10 @@
private void handleDeviceStateInfoChanged(@NonNull DeviceStateInfo info) {
ArrayList<DeviceStateCallbackWrapper> callbacks;
DeviceStateInfo oldInfo;
- int[] supportedStateIdentifiers;
synchronized (mLock) {
oldInfo = mLastReceivedInfo;
mLastReceivedInfo = info;
callbacks = new ArrayList<>(mCallbacks);
- supportedStateIdentifiers = getSupportedStateIdentifiersLocked(info.supportedStates);
}
final int diff = oldInfo == null ? ~0 : info.diff(oldInfo);
@@ -366,18 +322,11 @@
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notifySupportedDeviceStatesChanged(
List.copyOf(info.supportedStates));
- callbacks.get(i).notifySupportedStatesChanged(supportedStateIdentifiers);
- }
- }
- if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
- for (int i = 0; i < callbacks.size(); i++) {
- callbacks.get(i).notifyBaseStateChanged(info.baseState.getIdentifier());
}
}
if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notifyDeviceStateChanged(info.currentState);
- callbacks.get(i).notifyStateChanged(info.currentState.getIdentifier());
}
}
}
@@ -439,26 +388,11 @@
mExecutor = executor;
}
- void notifySupportedStatesChanged(int[] newSupportedStates) {
- mExecutor.execute(() ->
- mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
- }
-
void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
mExecutor.execute(() ->
mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
}
- void notifyBaseStateChanged(int newBaseState) {
- execute("notifyBaseStateChanged",
- () -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
- }
-
- void notifyStateChanged(int newDeviceState) {
- execute("notifyStateChanged",
- () -> mDeviceStateCallback.onStateChanged(newDeviceState));
- }
-
void notifyDeviceStateChanged(DeviceState newDeviceState) {
execute("notifyDeviceStateChanged",
() -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
diff --git a/core/java/android/hardware/devicestate/DeviceStateRequest.java b/core/java/android/hardware/devicestate/DeviceStateRequest.java
index 893d765..7665e2f 100644
--- a/core/java/android/hardware/devicestate/DeviceStateRequest.java
+++ b/core/java/android/hardware/devicestate/DeviceStateRequest.java
@@ -115,8 +115,8 @@
* requested state.
* <p>
* Guaranteed to be called after a call to
- * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} with a state
- * matching the requested state.
+ * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} with a
+ * state matching the requested state.
*/
default void onRequestActivated(@NonNull DeviceStateRequest request) {}
@@ -124,7 +124,7 @@
* Called to indicate the request has been temporarily suspended.
* <p>
* Guaranteed to be called before a call to
- * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+ * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
*/
default void onRequestSuspended(@NonNull DeviceStateRequest request) {}
@@ -134,7 +134,7 @@
* DeviceStateRequest.Callback)}.
* <p>
* Guaranteed to be called before a call to
- * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+ * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
* <p>
* Note: A call to {@link #onRequestSuspended(DeviceStateRequest)} is not guaranteed to
* occur before this method.
diff --git a/core/java/android/hardware/devicestate/DeviceStateUtil.java b/core/java/android/hardware/devicestate/DeviceStateUtil.java
new file mode 100644
index 0000000..627e740
--- /dev/null
+++ b/core/java/android/hardware/devicestate/DeviceStateUtil.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.devicestate;
+
+import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
+
+import android.annotation.NonNull;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utilities for {@link DeviceStateManager}.
+ * @hide
+ */
+public class DeviceStateUtil {
+ private DeviceStateUtil() { }
+
+ /**
+ * Returns the state identifier of the {@link DeviceState} that matches the
+ * {@code currentState}s physical properties. This will return the identifier of the
+ * {@link DeviceState} that matches the devices physical configuration.
+ *
+ * Returns {@link INVALID_DEVICE_STATE_IDENTIFIER} if there is no {@link DeviceState} in the
+ * provided list of {@code supportedStates} that matches.
+ * @hide
+ */
+ public static int calculateBaseStateIdentifier(@NonNull DeviceState currentState,
+ @NonNull List<DeviceState> supportedStates) {
+ DeviceState.Configuration stateConfiguration = currentState.getConfiguration();
+ for (int i = 0; i < supportedStates.size(); i++) {
+ DeviceState stateToCompare = supportedStates.get(i);
+ if (stateToCompare.getConfiguration().getPhysicalProperties().isEmpty()) {
+ continue;
+ }
+ if (isDeviceStateMatchingPhysicalProperties(stateConfiguration.getPhysicalProperties(),
+ supportedStates.get(i))) {
+ return supportedStates.get(i).getIdentifier();
+ }
+ }
+ return INVALID_DEVICE_STATE_IDENTIFIER;
+ }
+
+ /**
+ * Returns if the physical properties provided, matches the same physical properties on the
+ * provided {@link DeviceState}.
+ */
+ private static boolean isDeviceStateMatchingPhysicalProperties(
+ Set<@DeviceState.PhysicalDeviceStateProperties Integer> physicalProperties,
+ DeviceState state) {
+ Iterator<@DeviceState.PhysicalDeviceStateProperties Integer> iterator =
+ physicalProperties.iterator();
+ while (iterator.hasNext()) {
+ if (!state.hasProperty(iterator.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index eb26a76..4894fb1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1653,6 +1653,22 @@
}
/**
+ * Allows internal application to restrict display modes to specified modeIds
+ *
+ * @param displayId display that restrictions will be applied to
+ * @param modeIds allowed mode ids
+ *
+ * @hide
+ */
+ @RequiresPermission("android.permission.RESTRICT_DISPLAY_MODES")
+ public void requestDisplayModes(int displayId, @Nullable int[] modeIds) {
+ if (modeIds != null && modeIds.length == 0) {
+ throw new IllegalArgumentException("requestDisplayModes: modesIds can't be empty");
+ }
+ mGlobal.requestDisplayModes(displayId, modeIds);
+ }
+
+ /**
* Listens for changes in available display devices.
*/
public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 75f0ceb..3d7b714 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -38,6 +38,7 @@
import android.hardware.graphics.common.DisplayDecorationSupport;
import android.media.projection.IMediaProjection;
import android.media.projection.MediaProjection;
+import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
@@ -138,6 +139,8 @@
private int mWifiDisplayScanNestCount;
+ private final Binder mToken = new Binder();
+
@VisibleForTesting
public DisplayManagerGlobal(IDisplayManager dm) {
mDm = dm;
@@ -1182,6 +1185,20 @@
}
}
+ /**
+ * Sets allowed display mode ids
+ *
+ * @hide
+ */
+ @RequiresPermission("android.permission.RESTRICT_DISPLAY_MODES")
+ public void requestDisplayModes(int displayId, @Nullable int[] modeIds) {
+ try {
+ mDm.requestDisplayModes(mToken, displayId, modeIds);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
@Override
public void onDisplayEvent(int displayId, @DisplayEvent int event) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 83de4e4..70efc6f 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -235,4 +235,8 @@
// Disable a connected display that is enabled.
@EnforcePermission("MANAGE_DISPLAYS")
void disableConnectedDisplay(int displayId);
+
+ // Restricts display modes to specified modeIds.
+ @EnforcePermission("RESTRICT_DISPLAY_MODES")
+ void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds);
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 318c291..84619a0 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1024,7 +1024,11 @@
/**
* Specifies if a user is disallowed from creating a private profile.
* <p>The default value for an unmanaged user is <code>false</code>.
- * For users with a device owner set, the default is <code>true</code>.
+ * For users with a device owner set, the default value is <code>true</code> and the
+ * device owner currently cannot change it to <code>false</code>.
+ * On organization-owned managed profile devices, the default value is <code>false</code> but
+ * the profile owner can change it to <code>true</code> via the parent profile to block creating
+ * of private profiles on the personal user.
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_PROFILES}
@@ -1034,9 +1038,10 @@
* <p>Type: Boolean
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#getParentProfileInstance(ComponentName)
* @see #getUserRestrictions()
- * @hide
*/
+ @FlaggedApi(android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE)
public static final String DISALLOW_ADD_PRIVATE_PROFILE = "no_add_private_profile";
/**
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index 27e8628..6815440 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -222,7 +222,10 @@
/**
* Invoked by the {@link OnDeviceIntelligenceService} inorder to send updates to the inference
- * service if there is a state change to be performed.
+ * service if there is a state change to be performed. State change could be config updates,
+ * performing initialization or cleanup tasks in the remote inference service.
+ * The Bundle passed in here is expected to be read-only and will be rejected if it has any
+ * writable fields as detailed under {@link InferenceParams}.
*
* @param processingState the updated state to be applied.
* @param callbackExecutor executor to the run status callback on.
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 9b9cc19..7a1c75a 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -100,7 +101,7 @@
*/
@SystemApi
@SuppressLint("RequiresPermission")
- public int write(byte[] data) {
+ public int write(@Nullable byte[] data) {
try {
return sService.write(data);
} catch (RemoteException e) {
@@ -115,7 +116,7 @@
*/
@SystemApi
@SuppressLint("RequiresPermission")
- public byte[] read() {
+ public @Nullable byte[] read() {
try {
return sService.read();
} catch (RemoteException e) {
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 821e13d..b300022 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -49,9 +49,9 @@
/**
* Tracks whether we have an outstanding request from the IME to show, but weren't able to
- * execute it because we didn't have control yet.
+ * execute it because we didn't have control yet, or we didn't have a leash on the control yet.
*/
- private boolean mIsRequestedVisibleAwaitingControl;
+ private boolean mIsRequestedVisibleAwaitingLeash;
public ImeInsetsSourceConsumer(
int id, InsetsState state, Supplier<Transaction> transactionSupplier,
@@ -68,7 +68,7 @@
}
final boolean insetsChanged = super.onAnimationStateChanged(running);
if (!isShowRequested()) {
- mIsRequestedVisibleAwaitingControl = false;
+ mIsRequestedVisibleAwaitingLeash = false;
if (!running && !mHasPendingRequest) {
final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
ImeTracker.ORIGIN_CLIENT,
@@ -91,8 +91,8 @@
public void onWindowFocusGained(boolean hasViewFocus) {
super.onWindowFocusGained(hasViewFocus);
getImm().registerImeConsumer(this);
- if ((mController.getRequestedVisibleTypes() & getType()) != 0 && getControl() == null) {
- mIsRequestedVisibleAwaitingControl = true;
+ if ((mController.getRequestedVisibleTypes() & getType()) != 0 && !hasLeash()) {
+ mIsRequestedVisibleAwaitingLeash = true;
}
}
@@ -100,7 +100,7 @@
public void onWindowFocusLost() {
super.onWindowFocusLost();
getImm().unregisterImeConsumer(this);
- mIsRequestedVisibleAwaitingControl = false;
+ mIsRequestedVisibleAwaitingLeash = false;
}
@Override
@@ -130,15 +130,15 @@
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_REQUEST_SHOW);
- if (getControl() == null) {
- // If control is null, schedule to show IME when control is available.
- mIsRequestedVisibleAwaitingControl = true;
+ if (!hasLeash()) {
+ // If control or leash is null, schedule to show IME when both available.
+ mIsRequestedVisibleAwaitingLeash = true;
}
// If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
// this code here means that we now got control, so we can start the animation immediately.
// If client window is trying to control IME and IME is already visible, it is immediate.
if (fromIme
- || (mState.isSourceOrDefaultVisible(getId(), getType()) && getControl() != null)) {
+ || (mState.isSourceOrDefaultVisible(getId(), getType()) && hasLeash())) {
return ShowResult.SHOW_IMMEDIATELY;
}
@@ -148,9 +148,9 @@
void requestHide(boolean fromIme, @Nullable ImeTracker.Token statsToken) {
if (!fromIme) {
- // Create a new token to track the hide request when we have control,
+ // Create a new token to track the hide request when we have control and leash,
// as we use the passed in token for the insets animation already.
- final var notifyStatsToken = getControl() != null
+ final var notifyStatsToken = hasLeash()
? ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
ImeTracker.ORIGIN_CLIENT,
SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
@@ -176,7 +176,7 @@
ImeTracker.PHASE_CLIENT_INSETS_CONSUMER_NOTIFY_HIDDEN);
getImm().notifyImeHidden(mController.getHost().getWindowToken(), statsToken);
- mIsRequestedVisibleAwaitingControl = false;
+ mIsRequestedVisibleAwaitingLeash = false;
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.hideRequestFromApi", 0);
}
@@ -196,19 +196,28 @@
if (!super.setControl(control, showTypes, hideTypes)) {
return false;
}
- if (control == null && !mIsRequestedVisibleAwaitingControl) {
+ final boolean hasLeash = control != null && control.getLeash() != null;
+ if (!hasLeash && !mIsRequestedVisibleAwaitingLeash) {
mController.setRequestedVisibleTypes(0 /* visibleTypes */, getType());
removeSurface();
}
- if (control != null) {
- mIsRequestedVisibleAwaitingControl = false;
+ if (hasLeash) {
+ mIsRequestedVisibleAwaitingLeash = false;
}
return true;
}
@Override
protected boolean isRequestedVisibleAwaitingControl() {
- return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingControl;
+ return super.isRequestedVisibleAwaitingControl() || mIsRequestedVisibleAwaitingLeash;
+ }
+
+ /**
+ * Checks whether the consumer has an insets source control with a leash.
+ */
+ private boolean hasLeash() {
+ final var control = getControl();
+ return control != null && control.getLeash() != null;
}
@Override
@@ -224,7 +233,7 @@
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
super.dumpDebug(proto, INSETS_SOURCE_CONSUMER);
- proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingControl);
+ proto.write(IS_REQUESTED_VISIBLE_AWAITING_CONTROL, mIsRequestedVisibleAwaitingLeash);
proto.write(HAS_PENDING_REQUEST, mHasPendingRequest);
proto.end(token);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index a098e4d..4f5b51d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2631,7 +2631,10 @@
* Threshold values that are sent with
* {@link Transaction#setTrustedPresentationCallback(SurfaceControl,
* TrustedPresentationThresholds, Executor, Consumer)}
+ *
+ * @deprecated Use {@link android.window.TrustedPresentationThresholds} instead.
*/
+ @Deprecated
public static final class TrustedPresentationThresholds {
private final float mMinAlpha;
private final float mMinFractionRendered;
@@ -4451,8 +4454,7 @@
* <tr><td>o</td><td>x</td><td>x</td><td>x</td></tr>
* </table>
* </blockquote>
- *
- *<p>
+ * <p>
* We first start by computing fr=xscale*yscale=0.9*0.9=0.81, indicating
* that "81%" of the pixels were rendered. This corresponds to what was 100
* pixels being displayed in 81 pixels. This is somewhat of an abuse of
@@ -4469,6 +4471,7 @@
* be somewhat arbitrary, and so there are some somewhat arbitrary decisions in
* this API as well.
* <p>
+ *
* @param sc The {@link SurfaceControl} to set the callback on
* @param thresholds The {@link TrustedPresentationThresholds} that will specify when the to
* invoke the callback.
@@ -4477,7 +4480,11 @@
* exited the threshold.
* @return This transaction
* @see TrustedPresentationThresholds
+ * @deprecated Use
+ * {@link WindowManager#registerTrustedPresentationListener(IBinder,
+ * android.window.TrustedPresentationThresholds, Executor, Consumer)} instead.
*/
+ @Deprecated
@NonNull
public Transaction setTrustedPresentationCallback(@NonNull SurfaceControl sc,
@NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor,
@@ -4506,7 +4513,10 @@
*
* @param sc The SurfaceControl that the callback should be cleared from
* @return This transaction
+ * @deprecated Use {@link WindowManager#unregisterTrustedPresentationListener(Consumer)}
+ * instead.
*/
+ @Deprecated
@NonNull
public Transaction clearTrustedPresentationCallback(@NonNull SurfaceControl sc) {
checkPreconditions(sc);
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 6f757df..04ec4d0 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -583,13 +583,16 @@
* associated host {@link InputTransferToken}.
*
* @return Whether the touch stream was transferred.
+ * @deprecated Use {@link WindowManager#transferTouchGesture(InputTransferToken,
+ * InputTransferToken)} instead.
*/
+ @Deprecated
public boolean transferTouchGestureToHost() {
if (mViewRoot == null) {
return false;
}
- final WindowManager wm =
- (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
+ final WindowManager wm = (WindowManager) mViewRoot.mContext.getSystemService(
+ Context.WINDOW_SERVICE);
InputTransferToken embeddedToken = getInputTransferToken();
InputTransferToken hostToken = mWm.mHostInputTransferToken;
if (embeddedToken == null || hostToken == null) {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d494e28..c95d6ff 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1946,7 +1946,9 @@
* be passed from the host process to the client process.
*
* @return The token
+ * @deprecated Use {@link AttachedSurfaceControl#getInputTransferToken()} instead.
*/
+ @Deprecated
public @Nullable IBinder getHostToken() {
final ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0ac8936..0a75f4e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -18,6 +18,7 @@
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
+import static android.os.Trace.TRACE_TAG_VIEW;
import static android.service.autofill.Flags.FLAG_AUTOFILL_CREDMAN_DEV_INTEGRATION;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
@@ -16356,20 +16357,20 @@
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED) {
try {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
+ Trace.traceBegin(TRACE_TAG_VIEW, "View.onTouchListener#onTouch");
handled = li.mOnTouchListener.onTouch(this, event);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ Trace.traceEnd(TRACE_TAG_VIEW);
}
}
if (handled) {
return true;
}
try {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "View#onTouchEvent");
+ Trace.traceBegin(TRACE_TAG_VIEW, "View#onTouchEvent");
return onTouchEvent(event);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ Trace.traceEnd(TRACE_TAG_VIEW);
}
}
@@ -23846,14 +23847,14 @@
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW,
+ if (Trace.isTagEnabled(TRACE_TAG_VIEW)) {
+ Trace.traceBegin(TRACE_TAG_VIEW,
"buildDrawingCache/SW Layer for " + getClass().getSimpleName());
}
try {
buildDrawingCacheImpl(autoScale);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ Trace.traceEnd(TRACE_TAG_VIEW);
}
}
}
@@ -28980,6 +28981,7 @@
surface.copyFrom(surfaceControl);
IBinder token = null;
try {
+ Trace.traceBegin(TRACE_TAG_VIEW, "startDragAndDrop#drawDragShadow");
final Canvas canvas = isHardwareAccelerated()
? surface.lockHardwareCanvas()
: surface.lockCanvas(null);
@@ -28988,33 +28990,40 @@
shadowBuilder.onDrawShadow(canvas);
} finally {
surface.unlockCanvasAndPost(canvas);
+ Trace.traceEnd(TRACE_TAG_VIEW);
}
- token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
- root.getLastTouchSource(), root.getLastTouchDeviceId(),
- root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
- shadowTouchPoint.x, shadowTouchPoint.y, data);
- if (ViewDebug.DEBUG_DRAG) {
- Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
+ Trace.traceBegin(TRACE_TAG_VIEW, "startDragAndDrop#performDrag");
+ try {
+ token = mAttachInfo.mSession.performDrag(mAttachInfo.mWindow, flags, surfaceControl,
+ root.getLastTouchSource(), root.getLastTouchDeviceId(),
+ root.getLastTouchPointerId(), lastTouchPoint.x, lastTouchPoint.y,
+ shadowTouchPoint.x, shadowTouchPoint.y, data);
+ if (ViewDebug.DEBUG_DRAG) {
+ Log.d(VIEW_LOG_TAG, "performDrag returned " + token);
+ }
+ if (token != null) {
+ if (mAttachInfo.mDragSurface != null) {
+ mAttachInfo.mDragSurface.release();
+ }
+ if (mAttachInfo.mDragData != null) {
+ // Clean up previous drag data intents
+ View.cleanUpPendingIntents(mAttachInfo.mDragData);
+ }
+ mAttachInfo.mDragSurface = surface;
+ mAttachInfo.mDragToken = token;
+ mAttachInfo.mDragData = data;
+ // Cache the local state object for delivery with DragEvents
+ root.setLocalDragState(myLocalState);
+ if (a11yEnabled) {
+ // Set for AccessibilityEvents
+ mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
+ }
+ }
+ return token != null;
+ } finally {
+ Trace.traceEnd(TRACE_TAG_VIEW);
}
- if (token != null) {
- if (mAttachInfo.mDragSurface != null) {
- mAttachInfo.mDragSurface.release();
- }
- if (mAttachInfo.mDragData != null) {
- View.cleanUpPendingIntents(mAttachInfo.mDragData);
- }
- mAttachInfo.mDragSurface = surface;
- mAttachInfo.mDragToken = token;
- mAttachInfo.mDragData = data;
- // Cache the local state object for delivery with DragEvents
- root.setLocalDragState(myLocalState);
- if (a11yEnabled) {
- // Set for AccessibilityEvents
- mAttachInfo.mViewRootImpl.setDragStartedViewForAccessibility(this);
- }
- }
- return token != null;
} catch (Exception e) {
Log.e(VIEW_LOG_TAG, "Unable to initiate drag", e);
return false;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8c3cf5f..cae6672 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -21,8 +21,10 @@
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
+import static android.os.Trace.TRACE_TAG_VIEW;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.DragEvent.ACTION_DRAG_LOCATION;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
@@ -6473,7 +6475,18 @@
DragEvent event = (DragEvent) msg.obj;
// only present when this app called startDrag()
event.mLocalState = mLocalDragState;
- handleDragEvent(event);
+ final boolean traceDragEvent = event.mAction != ACTION_DRAG_LOCATION;
+ try {
+ if (traceDragEvent) {
+ Trace.traceBegin(TRACE_TAG_VIEW,
+ "c#" + DragEvent.actionToString(event.mAction));
+ }
+ handleDragEvent(event);
+ } finally {
+ if (traceDragEvent) {
+ Trace.traceEnd(TRACE_TAG_VIEW);
+ }
+ }
} break;
case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: {
handleDispatchSystemUiVisibilityChanged();
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c127a43..107c5f2 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -5871,7 +5871,10 @@
&& height == WindowManager.LayoutParams.MATCH_PARENT;
}
- private static String layoutInDisplayCutoutModeToString(
+ /**
+ * @hide
+ */
+ public static String layoutInDisplayCutoutModeToString(
@LayoutInDisplayCutoutMode int mode) {
switch (mode) {
case LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 40a437f..9dec102 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -129,7 +129,6 @@
private final int mDefaultWindowAnimationStyleResId;
private final boolean mDebug;
- private final boolean mGridLayoutRecentsEnabled;
private final boolean mLowRamRecentsEnabled;
public TransitionAnimation(Context context, boolean debug, String tag) {
@@ -166,7 +165,6 @@
mConfigShortAnimTime = context.getResources().getInteger(
com.android.internal.R.integer.config_shortAnimTime);
- mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();
final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
@@ -768,10 +766,8 @@
// We scale the width and clip to the top/left square
float scale =
thumbWidth / (appWidth - contentInsets.left - contentInsets.right);
- if (!mGridLayoutRecentsEnabled) {
- int unscaledThumbHeight = (int) (thumbHeight / scale);
- mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
- }
+ int unscaledThumbHeight = (int) (thumbHeight / scale);
+ mTmpFromClipRect.bottom = mTmpFromClipRect.top + unscaledThumbHeight;
Animation scaleAnim = new ScaleAnimation(
scaleUp ? scale : 1, scaleUp ? 1 : scale,
@@ -887,12 +883,6 @@
toY = appRect.height() / 2 * (1 - 1 / scaleW) + appRect.top;
pivotX = mTmpRect.width() / 2;
pivotY = appRect.height() / 2 / scaleW;
- if (mGridLayoutRecentsEnabled) {
- // In the grid layout, the header is displayed above the thumbnail instead of
- // overlapping it.
- fromY -= thumbHeightI;
- toY -= thumbHeightI * scaleW;
- }
} else {
pivotX = 0;
pivotY = 0;
@@ -936,10 +926,7 @@
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
+ set.addAnimation(alpha);
set.addAnimation(translate);
set.addAnimation(clipAnim);
a = set;
@@ -958,10 +945,7 @@
// This AnimationSet uses the Interpolators assigned above.
AnimationSet set = new AnimationSet(false);
set.addAnimation(scale);
- if (!mGridLayoutRecentsEnabled) {
- // In the grid layout, the header should be shown for the whole animation.
- set.addAnimation(alpha);
- }
+ set.addAnimation(alpha);
set.addAnimation(translate);
a = set;
@@ -1081,8 +1065,7 @@
* @return whether the transition should show the thumbnail being scaled down.
*/
private boolean shouldScaleDownThumbnailTransition(int orientation) {
- return mGridLayoutRecentsEnabled
- || orientation == Configuration.ORIENTATION_PORTRAIT;
+ return orientation == Configuration.ORIENTATION_PORTRAIT;
}
private static int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a2efbd2..a22232a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -376,6 +376,12 @@
void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop);
/**
+ * Set the split screen focus to the left / top app or the right / bottom app based on
+ * {@param leftOrTop}.
+ */
+ void setSplitscreenFocus(boolean leftOrTop);
+
+ /**
* Shows the media output switcher dialog.
*
* @param packageName of the session for which the output switcher is shown.
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 79b1376..a44e92c 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -156,6 +156,7 @@
generate_product_characteristics_rro: true,
flags_packages: [
+ "android.app.contextualsearch.flags-aconfig",
"android.content.pm.flags-aconfig",
"android.provider.flags-aconfig",
"camera_platform_flags",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0869af5..1b9da20 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3801,12 +3801,12 @@
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK"
android:protectionLevel="internal|role" />
- <!-- Allows an application to manage policy related to theft detection.
+ <!-- Allows an application to query the device stolen state.
@FlaggedApi("android.app.admin.flags.device_theft_api_enabled")
@hide
@SystemApi
-->
- <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION"
+ <permission android:name="android.permission.QUERY_DEVICE_STOLEN_STATE"
android:protectionLevel="internal|role" />
<!-- Allows an application to manage policy related to system apps.
@@ -6776,11 +6776,12 @@
<permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG"
android:protectionLevel="signature" />
- <!-- Allows an application to set the BiometricDialog (SystemUI) logo .
+ <!-- Allows an application to set the advanced features on BiometricDialog (SystemUI), including
+ logo, logo description.
<p>Not for use by third-party applications.
@FlaggedApi("android.hardware.biometrics.custom_biometric_prompt")
-->
- <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO"
+ <permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED"
android:protectionLevel="signature|privileged" />
<!-- Allows an application to control keyguard. Only allowed for system processes.
@@ -7208,6 +7209,13 @@
<permission android:name="android.permission.ACCESS_SMARTSPACE"
android:protectionLevel="signature|privileged|development" />
+ <!-- @SystemApi Allows an application to start a contextual search.
+ @FlaggedApi("android.app.contextualsearch.flags.enable_service")
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.ACCESS_CONTEXTUAL_SEARCH"
+ android:protectionLevel="signature|privileged"
+ android:featureFlag="android.app.contextualsearch.flags.enable_service"/>
+
<!-- @SystemApi Allows an application to manage the wallpaper effects
generation service.
@hide <p>Not for use by third-party applications.</p> -->
@@ -8150,6 +8158,14 @@
<permission android:name="android.permission.EMERGENCY_INSTALL_PACKAGES"
android:protectionLevel="signature|privileged"/>
+ <!-- Allows internal applications to restrict display modes
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ @hide
+ -->
+ <permission android:name="android.permission.RESTRICT_DISPLAY_MODES"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b879c97..417c6df 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -238,204 +238,204 @@
<color name="conversation_important_highlight">#F9AB00</color>
- <!-- Lightest shade of the accent color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_0">#ffffff</color>
- <!-- Shade of the accent system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_10">#F9FCFF</color>
- <!-- Shade of the accent system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_50">#E0F3FF</color>
- <!-- Shade of the accent system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_100">#C1E8FF</color>
- <!-- Shade of the accent system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_200">#76D1FF</color>
- <!-- Shade of the accent system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_300">#4BB6E8</color>
- <!-- Shade of the accent system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_400">#219BCC</color>
- <!-- Shade of the accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_500">#007FAC</color>
- <!-- Shade of the accent system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_600">#00668B</color>
- <!-- Shade of the accent system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_700">#004C69</color>
- <!-- Shade of the accent system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_800">#003549</color>
- <!-- Shade of the accent system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent1_900">#001E2C</color>
- <!-- Darkest shade of the accent color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Primary color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_0">#FFFFFF</color>
+ <!--Shade of the Primary system color at 99% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_10">#FEFBFF</color>
+ <!--Shade of the Primary system color at 95% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_50">#EEF0FF</color>
+ <!--Shade of the Primary system color at 90% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_100">#D9E2FF</color>
+ <!--Shade of the Primary system color at 80% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_200">#B0C6FF</color>
+ <!--Shade of the Primary system color at 70% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_300">#94AAE4</color>
+ <!--Shade of the Primary system color at 60% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_400">#7A90C8</color>
+ <!--Shade of the Primary system color at 50% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_500">#6076AC</color>
+ <!--Shade of the Primary system color at 40% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_600">#475D92</color>
+ <!--Shade of the Primary system color at 30% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_700">#2F4578</color>
+ <!--Shade of the Primary system color at 20% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_800">#152E60</color>
+ <!--Shade of the Primary system color at 10% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent1_900">#001945</color>
+ <!--Darkest shade of the Primary color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent1_1000">#000000</color>
- <!-- Lightest shade of the secondary accent color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_0">#ffffff</color>
- <!-- Shade of the secondary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_10">#F9FCFF</color>
- <!-- Shade of the secondary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_50">#E0F3FF</color>
- <!-- Shade of the secondary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_100">#D1E5F4</color>
- <!-- Shade of the secondary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_200">#B5CAD7</color>
- <!-- Shade of the secondary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_300">#9AAEBB</color>
- <!-- Shade of the secondary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_400">#8094A0</color>
- <!-- Shade of the secondary accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_500">#657985</color>
- <!-- Shade of the secondary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_600">#4E616C</color>
- <!-- Shade of the secondary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_700">#374955</color>
- <!-- Shade of the secondary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_800">#20333D</color>
- <!-- Shade of the secondary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent2_900">#091E28</color>
- <!-- Darkest shade of the secondary accent color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Secondary color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_0">#FFFFFF</color>
+ <!--Shade of the Secondary system color at 99% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_10">#FEFBFF</color>
+ <!--Shade of the Secondary system color at 95% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_50">#EEF0FF</color>
+ <!--Shade of the Secondary system color at 90% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_100">#DCE2F9</color>
+ <!--Shade of the Secondary system color at 80% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_200">#C0C6DC</color>
+ <!--Shade of the Secondary system color at 70% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_300">#A4ABC1</color>
+ <!--Shade of the Secondary system color at 60% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_400">#8A90A5</color>
+ <!--Shade of the Secondary system color at 50% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_500">#70778B</color>
+ <!--Shade of the Secondary system color at 40% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_600">#575E71</color>
+ <!--Shade of the Secondary system color at 30% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_700">#404659</color>
+ <!--Shade of the Secondary system color at 20% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_800">#2A3042</color>
+ <!--Shade of the Secondary system color at 10% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent2_900">#151B2C</color>
+ <!--Darkest shade of the Secondary color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent2_1000">#000000</color>
- <!-- Lightest shade of the tertiary accent color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_0">#ffffff</color>
- <!-- Shade of the tertiary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Tertiary color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_0">#FFFFFF</color>
+ <!--Shade of the Tertiary system color at 99% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_10">#FFFBFF</color>
- <!-- Shade of the tertiary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_50">#F5EEFF</color>
- <!-- Shade of the tertiary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_100">#E6DEFF</color>
- <!-- Shade of the tertiary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_200">#CAC1EA</color>
- <!-- Shade of the tertiary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_300">#AEA6CE</color>
- <!-- Shade of the tertiary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_400">#938CB1</color>
- <!-- Shade of the tertiary accent system color at 49% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_500">#787296</color>
- <!-- Shade of the tertiary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_600">#605A7C</color>
- <!-- Shade of the tertiary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_700">#484264</color>
- <!-- Shade of the tertiary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_800">#322C4C</color>
- <!-- Shade of the tertiary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_accent3_900">#1D1736</color>
- <!-- Darkest shade of the tertiary accent color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Shade of the Tertiary system color at 95% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_50">#FFEBFA</color>
+ <!--Shade of the Tertiary system color at 90% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_100">#FDD7FA</color>
+ <!--Shade of the Tertiary system color at 80% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_200">#E0BBDD</color>
+ <!--Shade of the Tertiary system color at 70% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_300">#C3A0C1</color>
+ <!--Shade of the Tertiary system color at 60% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_400">#A886A6</color>
+ <!--Shade of the Tertiary system color at 50% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_500">#8C6D8C</color>
+ <!--Shade of the Tertiary system color at 40% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_600">#725572</color>
+ <!--Shade of the Tertiary system color at 30% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_700">#593D59</color>
+ <!--Shade of the Tertiary system color at 20% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_800">#412742</color>
+ <!--Shade of the Tertiary system color at 10% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_accent3_900">#2A122C</color>
+ <!--Darkest shade of the Tertiary color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_accent3_1000">#000000</color>
- <!-- Lightest shade of the neutral color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_0">#ffffff</color>
- <!-- Shade of the neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_10">#FCFCFF</color>
- <!-- Shade of the neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_50">#F0F0F3</color>
- <!-- Shade of the neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_100">#E1E3E5</color>
- <!-- Shade of the neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_200">#C5C7C9</color>
- <!-- Shade of the neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_300">#AAABAE</color>
- <!-- Shade of the neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_400">#8F9193</color>
- <!-- Shade of the neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_500">#747679</color>
- <!-- Shade of the neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_600">#5C5F61</color>
- <!-- Shade of the neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_700">#454749</color>
- <!-- Shade of the neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_800">#2E3133</color>
- <!-- Shade of the neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral1_900">#191C1E</color>
- <!-- Darkest shade of the neutral color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Neutral color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_0">#FFFFFF</color>
+ <!--Shade of the Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_10">#FEFBFF</color>
+ <!--Shade of the Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_50">#F1F0F7</color>
+ <!--Shade of the Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_100">#E2E2E9</color>
+ <!--Shade of the Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_200">#C6C6CD</color>
+ <!--Shade of the Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_300">#ABABB1</color>
+ <!--Shade of the Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_400">#909097</color>
+ <!--Shade of the Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_500">#76777D</color>
+ <!--Shade of the Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_600">#5D5E64</color>
+ <!--Shade of the Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_700">#45464C</color>
+ <!--Shade of the Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_800">#2F3036</color>
+ <!--Shade of the Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral1_900">#1A1B20</color>
+ <!--Darkest shade of the Neutral color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral1_1000">#000000</color>
- <!-- Lightest shade of the secondary neutral color used by the system. White.
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_0">#ffffff</color>
- <!-- Shade of the secondary neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_10">#F9FCFF</color>
- <!-- Shade of the secondary neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_50">#EBF1F8</color>
- <!-- Shade of the secondary neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_100">#DCE3E9</color>
- <!-- Shade of the secondary neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_200">#C0C7CD</color>
- <!-- Shade of the secondary neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_300">#A5ACB2</color>
- <!-- Shade of the secondary neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_400">#8A9297</color>
- <!-- Shade of the secondary neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_500">#70777C</color>
- <!-- Shade of the secondary neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_600">#585F65</color>
- <!-- Shade of the secondary neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_700">#40484D</color>
- <!-- Shade of the secondary neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_800">#2A3136</color>
- <!-- Shade of the secondary neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
- This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_neutral2_900">#161C20</color>
- <!-- Darkest shade of the secondary neutral color used by the system. Black.
- This value can be overlaid at runtime by OverlayManager RROs. -->
+ <!--Lightest shade of the Secondary Neutral color used by the system. White.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_0">#FFFFFF</color>
+ <!--Shade of the Secondary Neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_10">#FEFBFF</color>
+ <!--Shade of the Secondary Neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_50">#F0F0FA</color>
+ <!--Shade of the Secondary Neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_100">#E1E2EC</color>
+ <!--Shade of the Secondary Neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_200">#C5C6D0</color>
+ <!--Shade of the Secondary Neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_300">#A9ABB4</color>
+ <!--Shade of the Secondary Neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_400">#8F9099</color>
+ <!--Shade of the Secondary Neutral system color at 50% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_500">#757780</color>
+ <!--Shade of the Secondary Neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_600">#5C5E67</color>
+ <!--Shade of the Secondary Neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_700">#44464F</color>
+ <!--Shade of the Secondary Neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_800">#2E3038</color>
+ <!--Shade of the Secondary Neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
+ This value can be overlaid at runtime by OverlayManager RROs.-->
+ <color name="system_neutral2_900">#191B23</color>
+ <!--Darkest shade of the Secondary Neutral color used by the system. Black.
+ This value can be overlaid at runtime by OverlayManager RROs.-->
<color name="system_neutral2_1000">#000000</color>
<!-- Lightest shade of the error color used by the system. White.
@@ -480,106 +480,106 @@
<!-- Colors used in Android system, from design system.
These values can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_primary_container_light">#D8E2FF</color>
- <color name="system_on_primary_container_light">#001A41</color>
- <color name="system_primary_light">#445E91</color>
+ <color name="system_primary_container_light">#5E73A9</color>
+ <color name="system_on_primary_container_light">#FFFFFF</color>
+ <color name="system_primary_light">#2A4174</color>
<color name="system_on_primary_light">#FFFFFF</color>
- <color name="system_secondary_container_light">#DBE2F9</color>
- <color name="system_on_secondary_container_light">#141B2C</color>
- <color name="system_secondary_light">#575E71</color>
+ <color name="system_secondary_container_light">#6E7488</color>
+ <color name="system_on_secondary_container_light">#FFFFFF</color>
+ <color name="system_secondary_light">#3C4255</color>
<color name="system_on_secondary_light">#FFFFFF</color>
- <color name="system_tertiary_container_light">#FBD7FC</color>
- <color name="system_on_tertiary_container_light">#29132D</color>
- <color name="system_tertiary_light">#715573</color>
+ <color name="system_tertiary_container_light">#8A6A89</color>
+ <color name="system_on_tertiary_container_light">#FFFFFF</color>
+ <color name="system_tertiary_light">#553A55</color>
<color name="system_on_tertiary_light">#FFFFFF</color>
- <color name="system_background_light">#FAF9FD</color>
- <color name="system_on_background_light">#1B1B1F</color>
- <color name="system_surface_light">#FAF9FD</color>
- <color name="system_on_surface_light">#1B1B1F</color>
- <color name="system_surface_container_low_light">#F5F3F7</color>
+ <color name="system_background_light">#FAF8FF</color>
+ <color name="system_on_background_light">#1A1B20</color>
+ <color name="system_surface_light">#FAF8FF</color>
+ <color name="system_on_surface_light">#1A1B20</color>
+ <color name="system_surface_container_low_light">#F4F3FA</color>
<color name="system_surface_container_lowest_light">#FFFFFF</color>
- <color name="system_surface_container_light">#EFEDF1</color>
- <color name="system_surface_container_high_light">#E9E7EC</color>
- <color name="system_surface_container_highest_light">#E3E2E6</color>
- <color name="system_surface_bright_light">#FAF9FD</color>
- <color name="system_surface_dim_light">#DBD9DD</color>
+ <color name="system_surface_container_light">#EEEDF4</color>
+ <color name="system_surface_container_high_light">#E8E7EF</color>
+ <color name="system_surface_container_highest_light">#E2E2E9</color>
+ <color name="system_surface_bright_light">#FAF8FF</color>
+ <color name="system_surface_dim_light">#DAD9E0</color>
<color name="system_surface_variant_light">#E1E2EC</color>
- <color name="system_on_surface_variant_light">#44474F</color>
- <color name="system_outline_light">#72747D</color>
- <color name="system_outline_variant_light">#C4C7C5</color>
- <color name="system_error_light">#C00003</color>
+ <color name="system_on_surface_variant_light">#40434B</color>
+ <color name="system_outline_light">#5D5F67</color>
+ <color name="system_outline_variant_light">#797A83</color>
+ <color name="system_error_light">#8C0009</color>
<color name="system_on_error_light">#FFFFFF</color>
- <color name="system_error_container_light">#FFDAD5</color>
- <color name="system_on_error_container_light">#410000</color>
- <color name="system_primary_fixed">#D8E2FF</color>
- <color name="system_primary_fixed_dim">#ADC6FF</color>
- <color name="system_on_primary_fixed">#001A41</color>
- <color name="system_on_primary_fixed_variant">#2B4678</color>
- <color name="system_secondary_fixed">#DBE2F9</color>
- <color name="system_secondary_fixed_dim">#BFC6DC</color>
- <color name="system_on_secondary_fixed">#141B2C</color>
- <color name="system_on_secondary_fixed_variant">#3F4759</color>
- <color name="system_tertiary_fixed">#FBD7FC</color>
- <color name="system_tertiary_fixed_dim">#DEBCDF</color>
- <color name="system_on_tertiary_fixed">#29132D</color>
- <color name="system_on_tertiary_fixed_variant">#583E5B</color>
- <color name="system_control_activated_light">#D8E2FF</color>
- <color name="system_control_normal_light">#44474F</color>
- <color name="system_control_highlight_light">#1F000000</color>
- <color name="system_text_primary_inverse_light">#E3E2E6</color>
- <color name="system_text_secondary_and_tertiary_inverse_light">#C4C6D0</color>
- <color name="system_text_primary_inverse_disable_only_light">#E3E2E6</color>
- <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E3E2E6</color>
- <color name="system_text_hint_inverse_light">#E3E2E6</color>
- <color name="system_palette_key_color_primary_light">#5D77AC</color>
- <color name="system_palette_key_color_secondary_light">#6C7488</color>
- <color name="system_palette_key_color_tertiary_light">#907292</color>
- <color name="system_palette_key_color_neutral_light">#838387</color>
- <color name="system_palette_key_color_neutral_variant_light">#777982</color>
- <color name="system_primary_container_dark">#2B4678</color>
- <color name="system_on_primary_container_dark">#D8E2FF</color>
- <color name="system_primary_dark">#ADC6FF</color>
- <color name="system_on_primary_dark">#102F60</color>
- <color name="system_secondary_container_dark">#3F4759</color>
- <color name="system_on_secondary_container_dark">#DBE2F9</color>
- <color name="system_secondary_dark">#BFC6DC</color>
- <color name="system_on_secondary_dark">#293041</color>
- <color name="system_tertiary_container_dark">#583E5B</color>
- <color name="system_on_tertiary_container_dark">#FBD7FC</color>
- <color name="system_tertiary_dark">#DEBCDF</color>
- <color name="system_on_tertiary_dark">#402843</color>
- <color name="system_background_dark">#121316</color>
- <color name="system_on_background_dark">#E3E2E6</color>
- <color name="system_surface_dark">#121316</color>
- <color name="system_on_surface_dark">#E3E2E6</color>
- <color name="system_surface_container_low_dark">#1B1B1F</color>
- <color name="system_surface_container_lowest_dark">#0D0E11</color>
- <color name="system_surface_container_dark">#1F1F23</color>
- <color name="system_surface_container_high_dark">#292A2D</color>
- <color name="system_surface_container_highest_dark">#343538</color>
- <color name="system_surface_bright_dark">#38393C</color>
- <color name="system_surface_dim_dark">#121316</color>
- <color name="system_surface_variant_dark">#44474F</color>
- <color name="system_on_surface_variant_dark">#C4C6D0</color>
- <color name="system_outline_dark">#8E9099</color>
- <color name="system_outline_variant_dark">#444746</color>
- <color name="system_error_dark">#FFB4A8</color>
- <color name="system_on_error_dark">#690001</color>
- <color name="system_error_container_dark">#930001</color>
- <color name="system_on_error_container_dark">#FFDAD5</color>
- <color name="system_control_activated_dark">#2B4678</color>
- <color name="system_control_normal_dark">#C4C6D0</color>
- <color name="system_control_highlight_dark">#33FFFFFF</color>
- <color name="system_text_primary_inverse_dark">#1B1B1F</color>
- <color name="system_text_secondary_and_tertiary_inverse_dark">#44474F</color>
- <color name="system_text_primary_inverse_disable_only_dark">#1B1B1F</color>
- <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1B1B1F</color>
- <color name="system_text_hint_inverse_dark">#1B1B1F</color>
- <color name="system_palette_key_color_primary_dark">#5D77AC</color>
- <color name="system_palette_key_color_secondary_dark">#6C7488</color>
- <color name="system_palette_key_color_tertiary_dark">#907292</color>
- <color name="system_palette_key_color_neutral_dark">#838387</color>
- <color name="system_palette_key_color_neutral_variant_dark">#777982</color>
+ <color name="system_error_container_light">#DA342E</color>
+ <color name="system_on_error_container_light">#FFFFFF</color>
+ <color name="system_control_activated_light">#D9E2FF</color>
+ <color name="system_control_normal_light">#44464F</color>
+ <color name="system_control_highlight_light">#000000</color>
+ <color name="system_text_primary_inverse_light">#E2E2E9</color>
+ <color name="system_text_secondary_and_tertiary_inverse_light">#C5C6D0</color>
+ <color name="system_text_primary_inverse_disable_only_light">#E2E2E9</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_light">#E2E2E9</color>
+ <color name="system_text_hint_inverse_light">#E2E2E9</color>
+ <color name="system_palette_key_color_primary_light">#6076AC</color>
+ <color name="system_palette_key_color_secondary_light">#70778B</color>
+ <color name="system_palette_key_color_tertiary_light">#8C6D8C</color>
+ <color name="system_palette_key_color_neutral_light">#76777D</color>
+ <color name="system_palette_key_color_neutral_variant_light">#757780</color>
+ <color name="system_primary_container_dark">#7A90C8</color>
+ <color name="system_on_primary_container_dark">#000000</color>
+ <color name="system_primary_dark">#B7CAFF</color>
+ <color name="system_on_primary_dark">#00143B</color>
+ <color name="system_secondary_container_dark">#8A90A5</color>
+ <color name="system_on_secondary_container_dark">#000000</color>
+ <color name="system_secondary_dark">#C4CAE1</color>
+ <color name="system_on_secondary_dark">#0F1626</color>
+ <color name="system_tertiary_container_dark">#A886A6</color>
+ <color name="system_on_tertiary_container_dark">#000000</color>
+ <color name="system_tertiary_dark">#E4BFE2</color>
+ <color name="system_on_tertiary_dark">#240D26</color>
+ <color name="system_background_dark">#121318</color>
+ <color name="system_on_background_dark">#E2E2E9</color>
+ <color name="system_surface_dark">#121318</color>
+ <color name="system_on_surface_dark">#FCFAFF</color>
+ <color name="system_surface_container_low_dark">#1A1B20</color>
+ <color name="system_surface_container_lowest_dark">#0C0E13</color>
+ <color name="system_surface_container_dark">#1E1F25</color>
+ <color name="system_surface_container_high_dark">#282A2F</color>
+ <color name="system_surface_container_highest_dark">#33343A</color>
+ <color name="system_surface_bright_dark">#38393F</color>
+ <color name="system_surface_dim_dark">#121318</color>
+ <color name="system_surface_variant_dark">#44464F</color>
+ <color name="system_on_surface_variant_dark">#C9CAD4</color>
+ <color name="system_outline_dark">#A1A2AC</color>
+ <color name="system_outline_variant_dark">#81838C</color>
+ <color name="system_error_dark">#FFBAB1</color>
+ <color name="system_on_error_dark">#370001</color>
+ <color name="system_error_container_dark">#FF5449</color>
+ <color name="system_on_error_container_dark">#000000</color>
+ <color name="system_control_activated_dark">#2F4578</color>
+ <color name="system_control_normal_dark">#C5C6D0</color>
+ <color name="system_control_highlight_dark">#FFFFFF</color>
+ <color name="system_text_primary_inverse_dark">#1A1B20</color>
+ <color name="system_text_secondary_and_tertiary_inverse_dark">#44464F</color>
+ <color name="system_text_primary_inverse_disable_only_dark">#1A1B20</color>
+ <color name="system_text_secondary_and_tertiary_inverse_disabled_dark">#1A1B20</color>
+ <color name="system_text_hint_inverse_dark">#1A1B20</color>
+ <color name="system_palette_key_color_primary_dark">#6076AC</color>
+ <color name="system_palette_key_color_secondary_dark">#70778B</color>
+ <color name="system_palette_key_color_tertiary_dark">#8C6D8C</color>
+ <color name="system_palette_key_color_neutral_dark">#76777D</color>
+ <color name="system_palette_key_color_neutral_variant_dark">#757780</color>
+ <color name="system_primary_fixed">#5E73A9</color>
+ <color name="system_primary_fixed_dim">#455B8F</color>
+ <color name="system_on_primary_fixed">#FFFFFF</color>
+ <color name="system_on_primary_fixed_variant">#FFFFFF</color>
+ <color name="system_secondary_fixed">#6E7488</color>
+ <color name="system_secondary_fixed_dim">#555C6F</color>
+ <color name="system_on_secondary_fixed">#FFFFFF</color>
+ <color name="system_on_secondary_fixed_variant">#FFFFFF</color>
+ <color name="system_tertiary_fixed">#8A6A89</color>
+ <color name="system_tertiary_fixed_dim">#705270</color>
+ <color name="system_on_tertiary_fixed">#FFFFFF</color>
+ <color name="system_on_tertiary_fixed_variant">#FFFFFF</color>
<!-- Accessibility shortcut icon background color -->
<color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f59c099..e3f1cb6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3586,14 +3586,6 @@
<!-- The minimum number of visible recent tasks to be presented to the user through the
SystemUI. Can be -1 if there is no minimum limit. -->
- <integer name="config_minNumVisibleRecentTasks_grid">-1</integer>
-
- <!-- The maximum number of visible recent tasks to be presented to the user through the
- SystemUI. Can be -1 if there is no maximum limit. -->
- <integer name="config_maxNumVisibleRecentTasks_grid">9</integer>
-
- <!-- The minimum number of visible recent tasks to be presented to the user through the
- SystemUI. Can be -1 if there is no minimum limit. -->
<integer name="config_minNumVisibleRecentTasks_lowRam">-1</integer>
<!-- The maximum number of visible recent tasks to be presented to the user through the
@@ -6985,4 +6977,8 @@
<!-- The key containing the branching boolean for legacy Search. -->
<string name="config_defaultContextualSearchLegacyEnabled" translatable="false" />
+
+ <!-- Whether WM DisplayContent supports high performance transitions
+ (lower-end devices may want to disable) -->
+ <bool name="config_deviceSupportsHighPerfTransitions">true</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c603fa7..9628d30 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -392,8 +392,6 @@
<java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
- <java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
- <java-symbol type="integer" name="config_maxNumVisibleRecentTasks_grid" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks" />
<java-symbol type="integer" name="config_activeTaskDurationHours" />
@@ -5376,4 +5374,7 @@
<java-symbol type="string" name="config_defaultContextualSearchLegacyEnabled" />
<java-symbol type="string" name="unarchival_session_app_label" />
+
+ <!-- Whether WM DisplayContent supports high performance transitions -->
+ <java-symbol type="bool" name="config_deviceSupportsHighPerfTransitions" />
</resources>
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index 9a41fe0..5cc1ee4 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -1249,31 +1249,25 @@
}
@Test
- @Ignore // TODO: b/329402256 - Restore or delete
- public void testBigPictureStyle_setExtras_pictureIconNull_pictureIconKeyNull() {
+ public void testBigPictureStyle_setExtras_pictureIconNull_noPictureIconKey() {
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture((Bitmap) null);
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isTrue();
- final Parcelable pictureIcon = extras.getParcelable(EXTRA_PICTURE_ICON);
- assertThat(pictureIcon).isNull();
+ assertThat(extras.containsKey(EXTRA_PICTURE_ICON)).isFalse();
}
@Test
- @Ignore // TODO: b/329402256 - Restore or delete
- public void testBigPictureStyle_setExtras_pictureIconNull_pictureKeyNull() {
+ public void testBigPictureStyle_setExtras_pictureIconNull_noPictureKey() {
Notification.BigPictureStyle bpStyle = new Notification.BigPictureStyle();
bpStyle.bigPicture((Bitmap) null);
Bundle extras = new Bundle();
bpStyle.addExtras(extras);
- assertThat(extras.containsKey(EXTRA_PICTURE)).isTrue();
- final Parcelable picture = extras.getParcelable(EXTRA_PICTURE);
- assertThat(picture).isNull();
+ assertThat(extras.containsKey(EXTRA_PICTURE)).isFalse();
}
@Test
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
index 2905a5a..e118c98d 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteDatabaseTest.java
@@ -31,7 +31,6 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.test.AndroidTestCase;
import android.util.Log;
-import android.util.Printer;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -47,15 +46,12 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Vector;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Phaser;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -407,138 +403,4 @@
}
assertFalse(allowed);
}
-
- /** Dumpsys information about a single database. */
-
- /**
- * Collect and parse dumpsys output. This is not a full parser. It is only enough to support
- * the unit tests.
- */
- private static class Dumpsys {
- // Regular expressions for parsing the output. Reportedly, regular expressions are
- // expensive, so these are created only if a dumpsys object is created.
- private static final Object sLock = new Object();
- static Pattern mPool;
- static Pattern mConnection;
- static Pattern mEntry;
- static Pattern mSingleWord;
- static Pattern mNone;
-
- // The raw strings read from dumpsys. Once loaded, this list never changes.
- final ArrayList<String> mRaw = new ArrayList<>();
-
- // Parsed dumpsys. This contains only the bits that are being tested.
- static class Connection {
- ArrayList<String> mRecent = new ArrayList<>();
- ArrayList<String> mLong = new ArrayList<>();
- }
- static class Database {
- String mPath;
- ArrayList<Connection> mConnection = new ArrayList<>();
- }
- ArrayList<Database> mDatabase;
- ArrayList<String> mConcurrent;
-
- Dumpsys() {
- SQLiteDebug.dump(
- new Printer() { public void println(String x) { mRaw.add(x); } },
- new String[0]);
- parse();
- }
-
- /** Parse the raw text. Return true if no errors were detected. */
- boolean parse() {
- initialize();
-
- // Reset the parsed information. This method may be called repeatedly.
- mDatabase = new ArrayList<>();
- mConcurrent = new ArrayList<>();
-
- Database current = null;
- Connection connection = null;
- Matcher matcher;
- for (int i = 0; i < mRaw.size(); i++) {
- final String line = mRaw.get(i);
- matcher = mPool.matcher(line);
- if (matcher.lookingAt()) {
- current = new Database();
- mDatabase.add(current);
- current.mPath = matcher.group(1);
- continue;
- }
- matcher = mConnection.matcher(line);
- if (matcher.lookingAt()) {
- connection = new Connection();
- current.mConnection.add(connection);
- continue;
- }
-
- if (line.contains("Most recently executed operations")) {
- i += readTable(connection.mRecent, i, mEntry);
- continue;
- }
-
- if (line.contains("Operations exceeding 2000ms")) {
- i += readTable(connection.mLong, i, mEntry);
- continue;
- }
- if (line.contains("Concurrently opened database files")) {
- i += readTable(mConcurrent, i, mSingleWord);
- continue;
- }
- }
- return true;
- }
-
- /**
- * Read a series of lines following a header. Return the number of lines read. The input
- * line number is the number of the header.
- */
- private int readTable(List<String> s, int header, Pattern p) {
- // Special case: if the first line is "<none>" then there are no more lines to the
- // table.
- if (lookingAt(header+1, mNone)) return 1;
-
- int i;
- for (i = header + 1; i < mRaw.size() && lookingAt(i, p); i++) {
- s.add(mRaw.get(i).trim());
- }
- return i - header;
- }
-
- /** Return true if the n'th raw line matches the pattern. */
- boolean lookingAt(int n, Pattern p) {
- return p.matcher(mRaw.get(n)).lookingAt();
- }
-
- /** Compile the regular expressions the first time. */
- private static void initialize() {
- synchronized (sLock) {
- if (mPool != null) return;
- mPool = Pattern.compile("Connection pool for (\\S+):");
- mConnection = Pattern.compile("\\s+Connection #(\\d+):");
- mEntry = Pattern.compile("\\s+(\\d+): ");
- mSingleWord = Pattern.compile(" (\\S+)$");
- mNone = Pattern.compile("\\s+<none>$");
- }
- }
- }
-
- @Test
- public void testDumpsys() throws Exception {
- Dumpsys dumpsys = new Dumpsys();
-
- assertEquals(1, dumpsys.mDatabase.size());
- // Note: cannot test mConcurrent because that attribute is not hermitic with respect to
- // the tests.
-
- Dumpsys.Database db = dumpsys.mDatabase.get(0);
-
- // Work with normalized paths.
- String wantPath = mDatabaseFile.toPath().toRealPath().toString();
- String realPath = new File(db.mPath).toPath().toRealPath().toString();
- assertEquals(wantPath, realPath);
-
- assertEquals(1, db.mConnection.size());
- }
}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 48ba526..47b28899 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -120,6 +121,10 @@
final var statsToken = ImeTracker.Token.empty();
mImeConsumer.onWindowFocusGained(true);
mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+ // Called once through the show flow.
+ verify(mController).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
+ eq(statsToken));
// set control and verify visibility is applied.
InsetsSourceControl control = new InsetsSourceControl(ID_IME,
@@ -127,11 +132,55 @@
mController.onControlsChanged(new InsetsSourceControl[] { control });
// IME show animation should be triggered when control becomes available.
verify(mController).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
+ verify(mController, never()).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
+ });
+ }
+
+ @Test
+ public void testImeRequestedVisibleAwaitingLeash() {
+ // Set null control, then request show.
+ mController.onControlsChanged(new InsetsSourceControl[] { null });
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Request IME visible before control is available.
+ final var statsToken = ImeTracker.Token.empty();
+ mImeConsumer.onWindowFocusGained(true);
+ mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+ // Called once through the show flow.
+ verify(mController).applyAnimation(
eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
eq(statsToken));
+ // Clear previous invocations to verify this is never called with control without leash.
+ clearInvocations(mController);
+
+ // set control without leash and verify visibility is not applied.
+ InsetsSourceControl control = new InsetsSourceControl(ID_IME,
+ WindowInsets.Type.ime(), null /* leash */, false, new Point(), Insets.NONE);
+ mController.onControlsChanged(new InsetsSourceControl[] { control });
+ // IME show animation should not be triggered when control becomes available,
+ // as we have no leash.
verify(mController, never()).applyAnimation(
- eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(true) /* fromIme */,
- eq(statsToken));
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
+ verify(mController, never()).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
+
+ // set control with leash and verify visibility is applied.
+ InsetsSourceControl controlWithLeash = new InsetsSourceControl(ID_IME,
+ WindowInsets.Type.ime(), mLeash, false, new Point(), Insets.NONE);
+ mController.onControlsChanged(new InsetsSourceControl[] { controlWithLeash });
+ // IME show animation should be triggered when control with leash becomes available.
+ verify(mController).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
+ verify(mController, never()).applyAnimation(
+ eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
+ and(not(eq(statsToken)), notNull()));
});
}
@@ -159,6 +208,10 @@
final var statsToken = ImeTracker.Token.empty();
if (imeVisible) {
mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsToken);
+ // Called once through the show flow.
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(true) /* fromIme */,
+ eq(false) /* skipAnim */, eq(statsToken));
}
// set control and verify visibility is applied.
@@ -184,13 +237,17 @@
if (!hasViewFocus) {
final var statsTokenNext = ImeTracker.Token.empty();
mController.show(WindowInsets.Type.ime(), true /* fromIme */, statsTokenNext);
+ // Called once through the show flow.
+ verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
+ eq(true) /* show */, eq(true) /* fromIme */,
+ eq(false) /* skipAnim */, eq(statsTokenNext));
mController.onControlsChanged(new InsetsSourceControl[]{ control });
// Verify IME show animation should be triggered when control becomes available and
// the animation will be skipped by getAndClearSkipAnimationOnce invoked.
verify(control).getAndClearSkipAnimationOnce();
verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
- eq(true) /* show */, eq(true) /* fromIme */,
- eq(false) /* skipAnim */, eq(statsTokenNext));
+ eq(true) /* show */, eq(false) /* fromIme */,
+ eq(true) /* skipAnim */, and(not(eq(statsToken)), notNull()));
}
});
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 749f0e1..9c1c7006 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -454,7 +454,7 @@
<!-- Permissions required for CTS test - android.server.biometrics -->
<permission name="android.permission.USE_BIOMETRIC" />
<permission name="android.permission.TEST_BIOMETRIC" />
- <permission name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
+ <permission name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
<permission name="android.permission.MANAGE_BIOMETRIC_DIALOG" />
<!-- Permissions required for CTS test - CtsContactsProviderTestCases -->
<permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index fa35b63..98935e9 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -19,13 +19,14 @@
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
-import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
import static androidx.window.common.CommonFoldingFeature.parseListFromString;
import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.hardware.devicestate.DeviceStateUtil;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseIntArray;
@@ -54,29 +55,27 @@
private static final boolean DEBUG = false;
/**
- * Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
+ * Emulated device state
+ * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)} to
* {@link CommonFoldingFeature.State} map.
*/
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
/**
- * Emulated device state received via
- * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
- * "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
- * physical device states. They represent the state of the device when various software
- * features and APIs are applied. The emulated states generally consist of all "base" states,
- * but may have additional states such as "concurrent" or "rear display". Concurrent mode for
- * example is activated via public API and can be active in both the "open" and "half folded"
- * device states.
+ * Device state received via
+ * {@link DeviceStateManager.DeviceStateCallback#onDeviceStateChanged(DeviceState)}.
+ * The identifier returned through {@link DeviceState#getIdentifier()} may not correspond 1:1
+ * with the physical state of the device. This could correspond to the system state of the
+ * device when various software features or overrides are applied. The emulated states generally
+ * consist of all "base" states, but may have additional states such as "concurrent" or
+ * "rear display". Concurrent mode for example is activated via public API and can be active in
+ * both the "open" and "half folded" device states.
*/
- private int mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+ private DeviceState mCurrentDeviceState = new DeviceState(
+ new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
+ "INVALID").build());
- /**
- * Base device state received via
- * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
- * "Base" in this context means the "physical" state of the device.
- */
- private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+ private List<DeviceState> mSupportedStates;
@NonNull
private final RawFoldingFeatureProducer mRawFoldSupplier;
@@ -85,22 +84,11 @@
private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
@Override
- public void onStateChanged(int state) {
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
mCurrentDeviceState = state;
mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
.this::notifyFoldingFeatureChange);
}
-
- @Override
- public void onBaseStateChanged(int state) {
- mCurrentBaseDeviceState = state;
-
- if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
- == COMMON_STATE_USE_BASE_STATE) {
- mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
- .this::notifyFoldingFeatureChange);
- }
- }
};
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
@@ -109,6 +97,7 @@
mRawFoldSupplier = rawFoldSupplier;
String[] deviceStatePosturePairs = context.getResources()
.getStringArray(R.array.config_device_state_postures);
+ mSupportedStates = deviceStateManager.getSupportedDeviceStates();
boolean isHalfOpenedSupported = false;
for (String deviceStatePosturePair : deviceStatePosturePairs) {
String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
@@ -168,7 +157,7 @@
*/
private boolean isCurrentStateValid() {
// If the device state is not found in the map, indexOfKey returns a negative number.
- return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState) >= 0;
+ return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState.getIdentifier()) >= 0;
}
@Override
@@ -177,7 +166,9 @@
if (hasListeners()) {
mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange);
} else {
- mCurrentDeviceState = INVALID_DEVICE_STATE_IDENTIFIER;
+ mCurrentDeviceState = new DeviceState(
+ new DeviceState.Configuration.Builder(INVALID_DEVICE_STATE_IDENTIFIER,
+ "INVALID").build());
mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange);
}
}
@@ -251,10 +242,13 @@
@CommonFoldingFeature.State
private int currentHingeState() {
@CommonFoldingFeature.State
- int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+ int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState.getIdentifier(),
+ COMMON_STATE_UNKNOWN);
if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
- posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
+ posture = mDeviceStateToPostureMap.get(
+ DeviceStateUtil.calculateBaseStateIdentifier(mCurrentDeviceState,
+ mSupportedStates), COMMON_STATE_UNKNOWN);
}
return posture;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index d31bf2a..a3d2d7f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -20,6 +20,7 @@
import android.app.Activity;
import android.content.Context;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
import android.hardware.display.DisplayManager;
@@ -40,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -101,7 +103,9 @@
mDisplayManager = context.getSystemService(DisplayManager.class);
mExecutor = context.getMainExecutor();
- mCurrentSupportedDeviceStates = mDeviceStateManager.getSupportedStates();
+ // TODO(b/329436166): Update the usage of device state manager API's
+ mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(
+ mDeviceStateManager.getSupportedDeviceStates());
mFoldedDeviceStates = context.getResources().getIntArray(
R.array.config_foldedDeviceStates);
@@ -446,9 +450,10 @@
}
@Override
- public void onSupportedStatesChanged(int[] supportedStates) {
+ public void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {
synchronized (mLock) {
- mCurrentSupportedDeviceStates = supportedStates;
+ // TODO(b/329436166): Update the usage of device state manager API's
+ mCurrentSupportedDeviceStates = getSupportedStateIdentifiers(supportedStates);
updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
updateRearDisplayPresentationStatusListeners(
getCurrentRearDisplayPresentationModeStatus());
@@ -456,9 +461,10 @@
}
@Override
- public void onStateChanged(int state) {
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
synchronized (mLock) {
- mCurrentDeviceState = state;
+ // TODO(b/329436166): Update the usage of device state manager API's
+ mCurrentDeviceState = state.getIdentifier();
updateRearDisplayStatusListeners(getCurrentRearDisplayModeStatus());
updateRearDisplayPresentationStatusListeners(
getCurrentRearDisplayPresentationModeStatus());
@@ -482,6 +488,15 @@
return WindowAreaComponent.STATUS_AVAILABLE;
}
+ // TODO(b/329436166): Remove and update the usage of device state manager API's
+ private int[] getSupportedStateIdentifiers(@NonNull List<DeviceState> states) {
+ int[] identifiers = new int[states.size()];
+ for (int i = 0; i < states.size(); i++) {
+ identifiers[i] = states.get(i).getIdentifier();
+ }
+ return identifiers;
+ }
+
/**
* Helper method to determine if a rear display session is currently active by checking
* if the current device state is that which corresponds to {@code mRearDisplayState}.
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
index 8989fc5..f3d70f7 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -18,27 +18,32 @@
import android.content.Context
import android.content.Intent
+import android.content.pm.ShortcutInfo
+import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.UserHandle
import android.view.IWindowManager
import android.view.WindowManager
import android.view.WindowManagerGlobal
-import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.common.ProtoLog
import com.android.launcher3.icons.BubbleIconFactory
import com.android.wm.shell.R
+import com.android.wm.shell.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix
import com.android.wm.shell.common.FloatingContentCoordinator
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.taskview.TaskView
import com.android.wm.shell.taskview.TaskViewTaskController
import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import org.junit.After
import java.util.concurrent.Semaphore
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
@@ -64,6 +69,7 @@
@Before
fun setUp() {
+ PhysicsAnimatorTestUtils.prepareForTest()
// Disable protolog tool when running the tests from studio
ProtoLog.REQUIRE_PROTOLOGTOOL = false
windowManager = WindowManagerGlobal.getWindowManagerService()!!
@@ -104,34 +110,158 @@
{ sysuiProxy },
shellExecutor
)
+
+ context
+ .getSharedPreferences(context.packageName, Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(StackEducationView.PREF_STACK_EDUCATION, true)
+ .apply()
}
- @UiThreadTest
+ @After
+ fun tearDown() {
+ PhysicsAnimatorTestUtils.tearDown()
+ }
+
@Test
fun addBubble() {
val bubble = createAndInflateBubble()
- bubbleStackView.addBubble(bubble)
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
}
- @UiThreadTest
@Test
fun tapBubbleToExpand() {
val bubble = createAndInflateBubble()
- bubbleStackView.addBubble(bubble)
- assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
- bubble.iconView!!.performClick()
- // we're checking the expanded state in BubbleData because that's the source of truth. This
- // will eventually propagate an update back to the stack view, but setting the entire
- // pipeline is outside the scope of a unit test.
- assertThat(bubbleData.isExpanded).isTrue()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble)
+ }
+
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+ var lastUpdate: BubbleData.Update? = null
+ val semaphore = Semaphore(0)
+ val listener =
+ BubbleData.Listener { update ->
+ lastUpdate = update
+ semaphore.release()
+ }
+ bubbleData.setListener(listener)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubble.iconView!!.performClick()
+ // we're checking the expanded state in BubbleData because that's the source of truth.
+ // This will eventually propagate an update back to the stack view, but setting the
+ // entire pipeline is outside the scope of a unit test.
+ assertThat(bubbleData.isExpanded).isTrue()
+ }
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(lastUpdate).isNotNull()
+ assertThat(lastUpdate!!.expandedChanged).isTrue()
+ assertThat(lastUpdate!!.expanded).isTrue()
+ }
+
+ @Test
+ fun tapDifferentBubble_shouldReorder() {
+ val bubble1 = createAndInflateChatBubble(key = "bubble1")
+ val bubble2 = createAndInflateChatBubble(key = "bubble2")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubbleStackView.addBubble(bubble1)
+ bubbleStackView.addBubble(bubble2)
+ }
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(2)
+ assertThat(bubbleData.bubbles).hasSize(2)
+ assertThat(bubbleData.selectedBubble).isEqualTo(bubble2)
+ assertThat(bubble2.iconView).isNotNull()
+
+ var lastUpdate: BubbleData.Update? = null
+ val semaphore = Semaphore(0)
+ val listener =
+ BubbleData.Listener { update ->
+ lastUpdate = update
+ semaphore.release()
+ }
+ bubbleData.setListener(listener)
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubble2.iconView!!.performClick()
+ assertThat(bubbleData.isExpanded).isTrue()
+
+ bubbleStackView.setSelectedBubble(bubble2)
+ bubbleStackView.isExpanded = true
+ }
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(lastUpdate!!.expanded).isTrue()
+ assertThat(lastUpdate!!.bubbles.map { it.key })
+ .containsExactly("bubble2", "bubble1")
+ .inOrder()
+
+ // wait for idle to allow the animation to start
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+ // wait for the expansion animation to complete before interacting with the bubbles
+ PhysicsAnimatorTestUtils.blockUntilAnimationsEnd(
+ AnimatableScaleMatrix.SCALE_X, AnimatableScaleMatrix.SCALE_Y)
+
+ // tap on bubble1 to select it
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ bubble1.iconView!!.performClick()
+ }
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
+
+ // tap on bubble1 again to collapse the stack
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ // we have to set the selected bubble in the stack view manually because we don't have a
+ // listener wired up.
+ bubbleStackView.setSelectedBubble(bubble1)
+ bubble1.iconView!!.performClick()
+ }
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(bubbleData.selectedBubble).isEqualTo(bubble1)
+ assertThat(bubbleData.isExpanded).isFalse()
+ assertThat(lastUpdate!!.orderChanged).isTrue()
+ assertThat(lastUpdate!!.bubbles.map { it.key })
+ .containsExactly("bubble1", "bubble2")
+ .inOrder()
+ }
+
+ private fun createAndInflateChatBubble(key: String): Bubble {
+ val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
+ val shortcutInfo = ShortcutInfo.Builder(context, "fakeId").setIcon(icon).build()
+ val bubble =
+ Bubble(
+ key,
+ shortcutInfo,
+ /* desiredHeight= */ 6,
+ Resources.ID_NULL,
+ "title",
+ /* taskId= */ 0,
+ "locus",
+ /* isDismissable= */ true,
+ directExecutor()
+ ) {}
+ inflateBubble(bubble)
+ return bubble
}
private fun createAndInflateBubble(): Bubble {
val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+ inflateBubble(bubble)
+ return bubble
+ }
+
+ private fun inflateBubble(bubble: Bubble) {
bubble.setInflateSynchronously(true)
bubbleData.notificationEntryUpdated(bubble, true, false)
@@ -152,7 +282,6 @@
assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
assertThat(bubble.isInflated).isTrue()
- return bubble
}
private class FakeBubbleStackViewManager : BubbleStackViewManager {
@@ -176,7 +305,7 @@
r.run()
}
- override fun removeCallbacks(r: Runnable) {}
+ override fun removeCallbacks(r: Runnable?) {}
override fun hasCallback(r: Runnable): Boolean = false
}
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 7dd3961..00fb298 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -272,6 +272,10 @@
<dimen name="bubble_bar_expanded_view_corner_radius">16dp</dimen>
<!-- Corner radius for expanded view while it is being dragged -->
<dimen name="bubble_bar_expanded_view_corner_radius_dragged">28dp</dimen>
+ <!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
+ <dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
+ <!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
+ <dimen name="bubble_bar_dismiss_zone_height">242dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
index ee8c414..b7f0890 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt
@@ -505,7 +505,6 @@
// Check for a spring configuration. If one is present, we're either springing, or
// flinging-then-springing.
if (springConfig != null) {
-
// If there is no corresponding fling config, we're only springing.
if (flingConfig == null) {
// Apply the configuration and start the animation.
@@ -679,7 +678,6 @@
value: Float,
velocity: Float
) {
-
// If this property animation isn't relevant to this listener, ignore it.
if (!properties.contains(property)) {
return
@@ -702,7 +700,6 @@
finalVelocity: Float,
isFling: Boolean
): Boolean {
-
// If this property animation isn't relevant to this listener, ignore it.
if (!properties.contains(property)) {
return false
@@ -971,17 +968,18 @@
companion object {
/**
- * Constructor to use to for new physics animator instances in [getInstance]. This is
- * typically the default constructor, but [PhysicsAnimatorTestUtils] can change it so that
- * all code using the physics animator is given testable instances instead.
+ * Callback to notify that a new animator was created. Used in [PhysicsAnimatorTestUtils]
+ * to be able to keep track of animators and wait for them to finish.
*/
- internal var instanceConstructor: (Any) -> PhysicsAnimator<*> = ::PhysicsAnimator
+ internal var onAnimatorCreated: (PhysicsAnimator<*>, Any) -> Unit = { _, _ -> }
@JvmStatic
@Suppress("UNCHECKED_CAST")
fun <T : Any> getInstance(target: T): PhysicsAnimator<T> {
if (!animators.containsKey(target)) {
- animators[target] = instanceConstructor(target)
+ val animator = PhysicsAnimator(target)
+ onAnimatorCreated(animator, target)
+ animators[target] = animator
}
return animators[target] as PhysicsAnimator<T>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
index 86eb8da..7defc26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimatorTestUtils.kt
@@ -62,12 +62,9 @@
*/
@JvmStatic
fun prepareForTest() {
- val defaultConstructor = PhysicsAnimator.instanceConstructor
- PhysicsAnimator.instanceConstructor = fun(target: Any): PhysicsAnimator<*> {
- val animator = defaultConstructor(target)
+ PhysicsAnimator.onAnimatorCreated = { animator, target ->
allAnimatedObjects.add(target)
animatorTestHelpers[animator] = AnimatorTestHelper(animator)
- return animator
}
timeoutMs = 2000
@@ -158,12 +155,12 @@
@Throws(InterruptedException::class)
@Suppress("UNCHECKED_CAST")
fun <T : Any> blockUntilAnimationsEnd(
- properties: FloatPropertyCompat<in T>
+ vararg properties: FloatPropertyCompat<in T>
) {
for (target in allAnimatedObjects) {
try {
blockUntilAnimationsEnd(
- PhysicsAnimator.getInstance(target) as PhysicsAnimator<T>, properties)
+ PhysicsAnimator.getInstance(target) as PhysicsAnimator<T>, *properties)
} catch (e: ClassCastException) {
// Keep checking the other objects for ones whose types match the provided
// properties.
@@ -267,10 +264,8 @@
// Loop through the updates from the testable animator.
for (update in framesForProperty) {
-
// Check whether this frame satisfies the current matcher.
if (curMatcher(update)) {
-
// If that was the last unsatisfied matcher, we're good here. 'Verify' all remaining
// frames and return without failing.
if (matchers.size == 0) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index 6a5f785..42de401 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -24,6 +24,7 @@
import android.animation.ArgbEvaluator;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -74,7 +75,7 @@
private final int mFlyoutElevation;
private final int mBubbleElevation;
- private final int mFloatingBackgroundColor;
+ private int mFloatingBackgroundColor;
private final float mCornerRadius;
private final ViewGroup mFlyoutTextContainer;
@@ -107,6 +108,9 @@
/** Color of the 'new' dot that the flyout will transform into. */
private int mDotColor;
+ /** Keeps last used night mode flags **/
+ private int mNightModeFlags;
+
/** The outline of the triangle, used for elevation shadows. */
private final Outline mTriangleOutline = new Outline();
@@ -176,11 +180,8 @@
mFlyoutElevation = res.getDimensionPixelSize(R.dimen.bubble_flyout_elevation);
final TypedArray ta = mContext.obtainStyledAttributes(
- new int[] {
- com.android.internal.R.attr.materialColorSurfaceContainer,
- android.R.attr.dialogCornerRadius});
- mFloatingBackgroundColor = ta.getColor(0, Color.WHITE);
- mCornerRadius = ta.getDimensionPixelSize(1, 0);
+ new int[] {android.R.attr.dialogCornerRadius});
+ mCornerRadius = ta.getDimensionPixelSize(0, 0);
ta.recycle();
// Add padding for the pointer on either side, onDraw will draw it in this space.
@@ -198,19 +199,17 @@
// Use locale direction so the text is aligned correctly.
setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
- mBgPaint.setColor(mFloatingBackgroundColor);
-
mLeftTriangleShape =
new ShapeDrawable(TriangleShape.createHorizontal(
mPointerSize, mPointerSize, true /* isPointingLeft */));
mLeftTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
- mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
mRightTriangleShape =
new ShapeDrawable(TriangleShape.createHorizontal(
mPointerSize, mPointerSize, false /* isPointingLeft */));
mRightTriangleShape.setBounds(0, 0, mPointerSize, mPointerSize);
- mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+
+ applyConfigurationColors(getResources().getConfiguration());
}
@Override
@@ -244,6 +243,13 @@
fade(false /* in */, stackPos, hideDot, afterFadeOut);
}
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ if (applyColorsAccordingToConfiguration(newConfig)) {
+ invalidate();
+ }
+ }
+
/*
* Fade-out above or fade-in from below.
*/
@@ -424,6 +430,42 @@
}
/**
+ * Resolving and applying colors according to the ui mode, remembering most recent mode.
+ *
+ * @return {@code true} if night mode setting has changed since the last invocation,
+ * {@code false} otherwise
+ */
+ boolean applyColorsAccordingToConfiguration(Configuration configuration) {
+ int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ boolean flagsChanged = nightModeFlags != mNightModeFlags;
+ if (flagsChanged) {
+ mNightModeFlags = nightModeFlags;
+ applyConfigurationColors(configuration);
+ }
+ return flagsChanged;
+ }
+
+ private void applyConfigurationColors(Configuration configuration) {
+ int nightModeFlags = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ boolean isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES;
+ try (TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{
+ com.android.internal.R.attr.materialColorSurfaceContainer,
+ com.android.internal.R.attr.materialColorOnSurface,
+ com.android.internal.R.attr.materialColorOnSurfaceVariant})) {
+ mFloatingBackgroundColor = ta.getColor(0,
+ isNightModeOn ? Color.BLACK : Color.WHITE);
+ mSenderText.setTextColor(ta.getColor(1,
+ isNightModeOn ? Color.WHITE : Color.BLACK));
+ mMessageText.setTextColor(ta.getColor(2,
+ isNightModeOn ? Color.WHITE : Color.BLACK));
+ mBgPaint.setColor(mFloatingBackgroundColor);
+ mLeftTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ mRightTriangleShape.getPaint().setColor(mFloatingBackgroundColor);
+ }
+ }
+
+ /**
* Renders the background, which is either the rounded 'chat bubble' flyout, or some state
* between that and the 'new' dot over the bubbles.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 056598b..b5b8a81 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -17,8 +17,10 @@
package com.android.wm.shell.bubbles.bar
import android.annotation.SuppressLint
+import android.graphics.RectF
import android.view.MotionEvent
import android.view.View
+import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.common.bubbles.DismissView
@@ -43,6 +45,8 @@
private val magnetizedExpandedView: MagnetizedObject<BubbleBarExpandedView> =
MagnetizedObject.magnetizeView(expandedView)
private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+ private val dismissZoneHeight: Int
+ private val dismissZoneWidth: Int
init {
magnetizedExpandedView.magnetListener = MagnetListener()
@@ -74,6 +78,11 @@
}
return@setOnTouchListener dragMotionEventHandler.onTouch(view, event) || magnetConsumed
}
+
+ dismissZoneHeight =
+ dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_height)
+ dismissZoneWidth =
+ dismissView.resources.getDimensionPixelSize(R.dimen.bubble_bar_dismiss_zone_width)
}
/** Listener to receive callback about dragging events */
@@ -97,12 +106,23 @@
private var isMoving = false
private var screenCenterX: Int = -1
private var isOnLeft = false
+ private val dismissZone = RectF()
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
if (expandedView.isAnimating) return false
- screenCenterX = bubblePositioner.screenRect.centerX()
isOnLeft = bubblePositioner.isBubbleBarOnLeft
+
+ val screenRect = bubblePositioner.screenRect
+ screenCenterX = screenRect.centerX()
+ val screenBottom = screenRect.bottom
+
+ dismissZone.set(
+ screenCenterX - dismissZoneWidth / 2f,
+ (screenBottom - dismissZoneHeight).toFloat(),
+ screenCenterX + dismissZoneHeight / 2f,
+ screenBottom.toFloat()
+ )
return true
}
@@ -122,6 +142,11 @@
expandedView.translationY = expandedViewInitialTranslationY + dy
dismissView.show()
+ // Check if we are in the zone around dismiss view where drag can only lead to dismiss
+ if (dismissZone.contains(ev.rawX, ev.rawY)) {
+ return
+ }
+
if (isOnLeft && ev.rawX > screenCenterX) {
isOnLeft = false
dragListener.onLocationChanged(BubbleBarLocation.RIGHT)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
index 8b4ac1a..d17e862 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DevicePostureController.java
@@ -107,7 +107,7 @@
DeviceStateManager.class);
if (deviceStateManager != null) {
deviceStateManager.registerCallback(mMainExecutor, state -> onDevicePostureChanged(
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN)));
+ mDeviceStateToPostureMap.get(state.getIdentifier(), DEVICE_POSTURE_UNKNOWN)));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
index 8826141..31214eb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/GlobalDragListener.kt
@@ -17,6 +17,8 @@
import android.app.ActivityManager
import android.os.RemoteException
+import android.os.Trace
+import android.os.Trace.TRACE_TAG_WINDOW_MANAGER
import android.util.Log
import android.view.DragEvent
import android.view.IWindowManager
@@ -27,6 +29,7 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.protolog.ShellProtoLogGroup
import java.util.function.Consumer
+import kotlin.random.Random
/**
* Manages the listener and callbacks for unhandled global drags.
@@ -101,10 +104,15 @@
@VisibleForTesting
fun onUnhandledDrop(dragEvent: DragEvent, wmCallback: IUnhandledDragCallback) {
+ val traceCookie = Random.nextInt()
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"onUnhandledDrop: %s", dragEvent)
if (callback == null) {
wmCallback.notifyUnhandledDropComplete(false)
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
return
}
@@ -112,6 +120,8 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP,
"Notifying onUnhandledDrop complete: %b", it)
wmCallback.notifyUnhandledDropComplete(it)
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "GlobalDragListener.onUnhandledDrop",
+ traceCookie);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index f7ffdd1..2b433e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -64,9 +64,7 @@
default void onSplitVisibilityChanged(boolean visible) {}
}
- /**
- * Callback interface for listening to requests to enter split select. Used for desktop -> split
- */
+ /** Callback interface for listening to requests to enter split select */
interface SplitSelectListener {
default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
@@ -74,15 +72,6 @@
}
}
- interface SplitInvocationListener {
- /**
- * Called whenever shell starts or stops the split screen animation
- * @param animationRunning if {@code true} the animation has begun, if {@code false} the
- * animation has finished
- */
- default void onSplitAnimationInvoked(boolean animationRunning) { }
- }
-
/** Registers listener that gets split screen callback. */
void registerSplitScreenListener(@NonNull SplitScreenListener listener,
@NonNull Executor executor);
@@ -90,21 +79,15 @@
/** Unregisters listener that gets split screen callback. */
void unregisterSplitScreenListener(@NonNull SplitScreenListener listener);
- /**
- * Registers a {@link SplitInvocationListener} to notify when the animation to enter split
- * screen has started and stopped
- *
- * @param executor callbacks to the listener will be executed on this executor
- */
- void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
- @NonNull Executor executor);
-
/** Called when device waking up finished. */
void onFinishedWakingUp();
/** Called when requested to go to fullscreen from the current active split app. */
void goToFullscreenFromSplit();
+ /** Called when splitscreen focused app is changed. */
+ void setSplitscreenFocus(boolean leftOrTop);
+
/** Get a string representation of a stage type */
static String stageTypeToString(@StageType int stage) {
switch (stage) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 218abe2..3e34c30 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -485,6 +485,12 @@
}
}
+ public void setSplitscreenFocus(boolean leftOrTop) {
+ if (mStageCoordinator.isSplitActive()) {
+ mStageCoordinator.grantFocusToPosition(leftOrTop);
+ }
+ }
+
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId, int exitReason) {
mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
@@ -1138,12 +1144,6 @@
}
@Override
- public void registerSplitAnimationListener(@NonNull SplitInvocationListener listener,
- @NonNull Executor executor) {
- mStageCoordinator.registerSplitAnimationListener(listener, executor);
- }
-
- @Override
public void onFinishedWakingUp() {
mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
}
@@ -1152,6 +1152,12 @@
public void goToFullscreenFromSplit() {
mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
}
+
+ @Override
+ public void setSplitscreenFocus(boolean leftOrTop) {
+ mMainExecutor.execute(
+ () -> SplitScreenController.this.setSplitscreenFocus(leftOrTop));
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index e69ff70..1a53a1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -55,7 +55,6 @@
import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
/** Manages transition animations for split-screen. */
class SplitScreenTransitions {
@@ -80,8 +79,6 @@
private Transitions.TransitionFinishCallback mFinishCallback = null;
private SurfaceControl.Transaction mFinishTransaction;
- private SplitScreen.SplitInvocationListener mSplitInvocationListener;
- private Executor mSplitInvocationListenerExecutor;
SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
@NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
@@ -356,10 +353,6 @@
+ " skip to start enter split transition since it already exist. ");
return null;
}
- if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) {
- mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener
- .onSplitAnimationInvoked(true /*animationRunning*/));
- }
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
return transition;
@@ -536,12 +529,6 @@
mTransitions.getAnimExecutor().execute(va::start);
}
- public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener,
- @NonNull Executor executor) {
- mSplitInvocationListener = listener;
- mSplitInvocationListenerExecutor = executor;
- }
-
/** Calls when the transition got consumed. */
interface TransitionConsumedCallback {
void onConsumed(boolean aborted);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 8ab6cf7..41890df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -156,7 +156,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -237,9 +236,6 @@
private DefaultMixedHandler mMixedHandler;
private final Toast mSplitUnsupportedToast;
private SplitRequest mSplitRequest;
- /** Used to notify others of when shell is animating into split screen */
- private SplitScreen.SplitInvocationListener mSplitInvocationListener;
- private Executor mSplitInvocationListenerExecutor;
/**
* Since StageCoordinator only coordinates MainStage and SideStage, it shouldn't support
@@ -250,14 +246,6 @@
return false;
}
- /** NOTE: Will overwrite any previously set {@link #mSplitInvocationListener} */
- public void registerSplitAnimationListener(
- @NonNull SplitScreen.SplitInvocationListener listener, @NonNull Executor executor) {
- mSplitInvocationListener = listener;
- mSplitInvocationListenerExecutor = executor;
- mSplitTransitions.registerSplitAnimListener(listener, executor);
- }
-
class SplitRequest {
@SplitPosition
int mActivatePosition;
@@ -541,7 +529,7 @@
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startShortcut",
"side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
if (finishedCallback != null) {
@@ -672,7 +660,7 @@
null /* childrenToTop */, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("startIntentLegacy",
"side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
if (apps != null) {
@@ -1225,7 +1213,7 @@
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinishedOrCancelled",
"main or side stage was not populated."));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
} else {
mSyncQueue.queue(evictWct);
mSyncQueue.runInSync(t -> {
@@ -1246,7 +1234,7 @@
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
Log.w(TAG, splitFailureMessage("onRemoteAnimationFinished",
"main or side stage was not populated"));
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return;
}
@@ -1604,6 +1592,11 @@
}
}
+ protected void grantFocusToPosition(boolean leftOrTop) {
+ grantFocusToStage(mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
+ ? getMainStagePosition() : getSideStagePosition());
+ }
+
private void clearRequestIfPresented() {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
@@ -2813,7 +2806,6 @@
if (hasEnteringPip) {
mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
startTransaction, finishTransaction, finishCallback);
- notifySplitAnimationFinished();
return true;
}
@@ -2848,7 +2840,6 @@
// the transition, or synchronize task-org callbacks.
}
// Use normal animations.
- notifySplitAnimationFinished();
return false;
} else if (mMixedHandler != null && TransitionUtil.hasDisplayChange(info)) {
// A display-change has been un-expectedly inserted into the transition. Redirect
@@ -2862,7 +2853,6 @@
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
}
- notifySplitAnimationFinished();
return true;
}
}
@@ -3036,7 +3026,7 @@
pendingEnter.mRemoteHandler.onTransitionConsumed(transition,
false /*aborted*/, finishT);
}
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return true;
}
}
@@ -3065,7 +3055,6 @@
final TransitionInfo.Change finalMainChild = mainChild;
final TransitionInfo.Change finalSideChild = sideChild;
enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
- notifySplitAnimationFinished();
if (finalMainChild != null) {
if (!mainNotContainOpenTask) {
mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
@@ -3482,19 +3471,6 @@
mSplitLayout.isLeftRightSplit());
}
- private void handleUnsupportedSplitStart() {
- mSplitUnsupportedToast.show();
- notifySplitAnimationFinished();
- }
-
- private void notifySplitAnimationFinished() {
- if (mSplitInvocationListener == null || mSplitInvocationListenerExecutor == null) {
- return;
- }
- mSplitInvocationListenerExecutor.execute(() ->
- mSplitInvocationListener.onSplitAnimationInvoked(false /*animationRunning*/));
- }
-
/**
* Logs the exit of splitscreen to a specific stage. This must be called before the exit is
* executed.
@@ -3557,7 +3533,7 @@
if (!ENABLE_SHELL_TRANSITIONS) {
StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
return;
}
@@ -3577,7 +3553,7 @@
"app package " + taskInfo.baseActivity.getPackageName()
+ " does not support splitscreen, or is a controlled activity type"));
if (splitScreenVisible) {
- handleUnsupportedSplitStart();
+ mSplitUnsupportedToast.show();
}
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 34b2eeb..befc702 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -39,13 +39,10 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
@@ -66,7 +63,6 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -109,8 +105,6 @@
@Mock private ShellExecutor mMainExecutor;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@Mock private DefaultMixedHandler mMixedHandler;
- @Mock private SplitScreen.SplitInvocationListener mInvocationListener;
- private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -153,7 +147,6 @@
.setParentTaskId(mSideStage.mRootTaskInfo.taskId).build();
doReturn(mock(SplitDecorManager.class)).when(mMainStage).getSplitDecorManager();
doReturn(mock(SplitDecorManager.class)).when(mSideStage).getSplitDecorManager();
- mStageCoordinator.registerSplitAnimationListener(mInvocationListener, mTestShellExecutor);
}
@Test
@@ -459,15 +452,6 @@
mMainStage.activate(new WindowContainerTransaction(), true /* includingTopTask */);
}
- @Test
- @UiThreadTest
- public void testSplitInvocationCallback() {
- enterSplit();
- mTestShellExecutor.flushAll();
- verify(mInvocationListener, times(1))
- .onSplitAnimationInvoked(eq(true));
- }
-
private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) {
for (int i = 0; i < wct.getHierarchyOps().size(); ++i) {
WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index f5dc6ea..1905fa8 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5101,7 +5101,7 @@
* size is larger than 16x16, then the qpOffset information of all 16x16 blocks that
* encompass the coding unit is combined and used. The QP of target block will be calculated
* as 'frameQP + offsetQP'. If the result exceeds minQP or maxQP configured then the value
- * may be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
+ * will be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
* positive offsets will result in encoding blocks at higher QP than frame QP. If the areas
* of negative QP and positive QP are chosen wisely, the overall viewing experience can be
* improved.
@@ -5128,7 +5128,7 @@
* quantization parameter (QP) offset of the blocks in the bounding box. The bounding box
* will get stretched outwards to align to LCU boundaries during encoding. The Qp Offset is
* integral and shall be in the range [-128, 127]. The QP of target block will be calculated
- * as frameQP + offsetQP. If the result exceeds minQP or maxQP configured then the value may
+ * as frameQP + offsetQP. If the result exceeds minQP or maxQP configured then the value will
* be clamped. Negative offset results in blocks encoded at lower QP than frame QP and
* positive offsets will result in blocks encoded at higher QP than frame QP. If the areas of
* negative QP and positive QP are chosen wisely, the overall viewing experience can be
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 1e7bc47..abad460 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -2651,11 +2651,10 @@
mBlockAspectRatioRange = POSITIVE_RATIONALS;
mAspectRatioRange = POSITIVE_RATIONALS;
- // YUV 4:2:0 requires 2:2 alignment
- mWidthAlignment = 2;
- mHeightAlignment = 2;
- mBlockWidth = 2;
- mBlockHeight = 2;
+ mWidthAlignment = 1;
+ mHeightAlignment = 1;
+ mBlockWidth = 1;
+ mBlockHeight = 1;
mSmallerDimensionUpperLimit = getSizeRange().getUpper();
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a4bc235..8a5dfef 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1485,7 +1485,7 @@
<string name="user_nickname">Nickname</string>
<!-- Confirmation message on dialog for editing user name and profile picture. Inform user on who will be able to see the changes [CHAR LIMIT=NONE]-->
- <string name="edit_user_info_message">Your name and picture will be visible to anyone that uses this device.</string>
+ <string name="edit_user_info_message">The name and picture you choose will be visible to anyone who uses this device.</string>
<!-- Label for adding a new user in the user switcher [CHAR LIMIT=35] -->
<string name="user_add_user">Add user</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 3266c12..b151a53 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -60,10 +60,12 @@
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
@@ -161,6 +163,11 @@
private static final String APEX_DIR = "/apex";
private static final String APEX_ACONFIG_PATH_SUFFIX = "/etc/aconfig_flags.pb";
+ private static final String STORAGE_MIGRATION_FLAG =
+ "core_experiments_team_internal/com.android.providers.settings.storage_test_mission_1";
+ private static final String STORAGE_MIGRATION_LOG =
+ "/metadata/aconfig/flags/storage_migration.log";
+
/**
* This tag is applied to all aconfig default value-loaded flags.
*/
@@ -1439,6 +1446,20 @@
}
}
+ if (name != null && name.equals(STORAGE_MIGRATION_FLAG) && value.equals("true")) {
+ File file = new File(STORAGE_MIGRATION_LOG);
+ if (!file.exists()) {
+ try (BufferedWriter writer =
+ new BufferedWriter(new FileWriter(STORAGE_MIGRATION_LOG))) {
+ final long timestamp = System.currentTimeMillis();
+ String entry = String.format("%d | Log init", timestamp);
+ writer.write(entry);
+ } catch (IOException e) {
+ Slog.e(LOG_TAG, "failed to write storage migration file", e);
+ }
+ }
+ }
+
mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
fromSystem, id, isPreservedInRestore));
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
index 2e14e9b..c572bdb 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
+++ b/packages/SettingsProvider/src/com/android/providers/settings/device_config_service.aconfig
@@ -33,3 +33,10 @@
bug: "327383546"
is_fixed_read_only: true
}
+
+flag {
+ name: "storage_test_mission_1"
+ namespace: "core_experiments_team_internal"
+ description: "If this flag is detected as true on boot, writes a logfile to track storage migration correctness."
+ bug: "328444881"
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 43ea3ec..02d19dc 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -564,7 +564,7 @@
<uses-permission android:name="android.permission.TEST_BIOMETRIC" />
<!-- Permission required for CTS test - android.server.biometrics -->
- <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_LOGO" />
+ <uses-permission android:name="android.permission.SET_BIOMETRIC_DIALOG_ADVANCED" />
<!-- Permissions required for CTS test - NotificationManagerTest -->
<uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3c18f17..d9c371a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -434,7 +434,7 @@
</intent-filter>
</receiver>
- <activity android:name=".screenshot.LongScreenshotActivity"
+ <activity android:name=".screenshot.scroll.LongScreenshotActivity"
android:theme="@style/LongScreenshotActivity"
android:process=":screenshot"
android:exported="false"
@@ -529,15 +529,6 @@
</intent-filter>
</activity-alias>
- <!-- Springboard for launching the share and edit activity. This needs to be in the main
- system ui process since we need to notify the status bar to dismiss the keyguard -->
- <receiver android:name=".screenshot.ActionProxyReceiver"
- android:exported="false" />
-
- <!-- Callback for deleting screenshot notification -->
- <receiver android:name=".screenshot.DeleteScreenshotReceiver"
- android:exported="false" />
-
<!-- Callback for invoking a smart action from the screenshot notification. -->
<receiver android:name=".screenshot.SmartActionsReceiver"
android:exported="false"/>
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index e5e3469..79ae389 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -345,6 +345,16 @@
}
flag {
+ name: "activity_transition_use_largest_window"
+ namespace: "systemui"
+ description: "Target largest opening window during activity transitions."
+ bug: "323294573"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "centralized_status_bar_height_fix"
namespace: "systemui"
description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
diff --git a/packages/SystemUI/animation/Android.bp b/packages/SystemUI/animation/Android.bp
index 99b7c36..2268d16 100644
--- a/packages/SystemUI/animation/Android.bp
+++ b/packages/SystemUI/animation/Android.bp
@@ -44,6 +44,7 @@
"androidx.core_core-animation-nodeps",
"androidx.core_core-ktx",
"androidx.annotation_annotation",
+ "com_android_systemui_flags_lib",
"SystemUIShaderLib",
"WindowManager-Shell-shared",
"animationlib",
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 1b99e19..ea1cb34 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -43,6 +43,7 @@
import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
+import com.android.systemui.Flags.activityTransitionUseLargestWindow
import kotlin.math.roundToInt
private const val TAG = "ActivityTransitionAnimator"
@@ -648,11 +649,27 @@
var candidate: RemoteAnimationTarget? = null
for (it in apps) {
if (it.mode == RemoteAnimationTarget.MODE_OPENING) {
- if (!it.hasAnimatingParent) {
- return it
- }
- if (candidate == null) {
- candidate = it
+ if (activityTransitionUseLargestWindow()) {
+ if (
+ candidate == null ||
+ !it.hasAnimatingParent && candidate.hasAnimatingParent
+ ) {
+ candidate = it
+ continue
+ }
+ if (
+ !it.hasAnimatingParent &&
+ it.screenSpaceBounds.hasGreaterAreaThan(candidate.screenSpaceBounds)
+ ) {
+ candidate = it
+ }
+ } else {
+ if (!it.hasAnimatingParent) {
+ return it
+ }
+ if (candidate == null) {
+ candidate = it
+ }
}
}
}
@@ -960,5 +977,9 @@
e.printStackTrace()
}
}
+
+ private fun Rect.hasGreaterAreaThan(other: Rect): Boolean {
+ return (this.width() * this.height()) > (other.width() * other.height())
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 53f400f..55f7f69a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -19,7 +19,6 @@
import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
@@ -32,7 +31,6 @@
DefaultBlueprintModule::class,
OptionalSectionModule::class,
ShortcutsBesideUdfpsBlueprintModule::class,
- SplitShadeBlueprintModule::class,
SplitShadeWeatherClockBlueprintModule::class,
WeatherClockBlueprintModule::class,
],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index c5ff859..d9ed497 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -38,6 +38,9 @@
from(ClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
transitioningToSmallClock()
}
+ from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
+ spec = tween(1000)
+ }
}
private fun TransitionBuilder.transitioningToLargeClock() {
@@ -70,6 +73,8 @@
object ClockScenes {
val smallClockScene = SceneKey("small-clock-scene")
val largeClockScene = SceneKey("large-clock-scene")
+ val splitShadeSmallClockScene = SceneKey("split-shade-small-clock-scene")
+ val splitShadeLargeClockScene = SceneKey("split-shade-large-clock-scene")
}
object ClockElementKeys {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 9509fd2..320c455 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -22,18 +22,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
@@ -50,13 +47,11 @@
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
private val lockSection: LockSection,
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
+ private val topAreaSection: TopAreaSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = "default"
@@ -64,7 +59,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- val resources = LocalContext.current.resources
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -77,17 +71,7 @@
modifier = Modifier.fillMaxWidth(),
) {
with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (viewModel.areNotificationsVisible(resources = resources)) {
- with(notificationSection) {
- Notifications(
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
-
+ with(topAreaSection) { DefaultClockLayoutWithNotifications() }
if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
AmbientIndication(modifier = Modifier.fillMaxWidth())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 9abfa42..64c2cb3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -22,18 +22,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
@@ -50,13 +47,11 @@
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
private val lockSection: LockSection,
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
+ private val topAreaSection: TopAreaSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = "shortcuts-besides-udfps"
@@ -64,7 +59,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- val resources = LocalContext.current.resources
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -77,16 +71,7 @@
modifier = Modifier.fillMaxWidth(),
) {
with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (viewModel.areNotificationsVisible(resources = resources)) {
- with(notificationSection) {
- Notifications(
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
+ with(topAreaSection) { DefaultClockLayoutWithNotifications() }
if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
deleted file mode 100644
index 652412d..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.composable.blueprint
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.systemui.Flags
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-
-/**
- * Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
- * tablet form factor).
- */
-class SplitShadeBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
- private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
-) : ComposableLockscreenSceneBlueprint {
-
- override val id: String = "split-shade"
-
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
-
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxSize(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- Row(
- modifier = Modifier.fillMaxSize(),
- ) {
- Column(
- modifier = Modifier.fillMaxHeight().weight(weight = 1f),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
- }
- with(notificationSection) {
- val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
- } else {
- dimensionResource(
- id = R.dimen.large_screen_shade_header_height
- )
- }
- Notifications(
- modifier =
- Modifier.fillMaxHeight()
- .weight(weight = 1f)
- .padding(top = splitShadeTopMargin)
- )
- }
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
- val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-@Module
-interface SplitShadeBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: SplitShadeBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index f86623f..ee4e2d6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -112,7 +112,7 @@
with(mediaCarouselSection) { MediaCarousel() }
- if (viewModel.areNotificationsVisible(resources = resources)) {
+ if (viewModel.areNotificationsVisible) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 1ab2bc76..82e19e7c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -16,39 +16,29 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
-import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.padding
import com.android.systemui.customization.R as customizationR
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smartspaceElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.defaultClockTransitions
-import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
import javax.inject.Inject
/** Provides small clock and large clock composables for the default clock face. */
@@ -56,91 +46,10 @@
@Inject
constructor(
private val viewModel: KeyguardClockViewModel,
- private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val lockscreenContentViewModel: LockscreenContentViewModel,
- private val smartSpaceSection: SmartSpaceSection,
) {
@Composable
- fun DefaultClockLayout(
- modifier: Modifier = Modifier,
- ) {
- val isLargeClockVisible by viewModel.isLargeClockVisible.collectAsState()
- val burnIn = rememberBurnIn(clockInteractor)
- val currentScene =
- if (isLargeClockVisible) {
- largeClockScene
- } else {
- smallClockScene
- }
-
- LaunchedEffect(isLargeClockVisible) {
- if (isLargeClockVisible) {
- burnIn.onSmallClockTopChanged(null)
- }
- }
-
- SceneTransitionLayout(
- modifier = modifier,
- currentScene = currentScene,
- onChangeScene = {},
- transitions = defaultClockTransitions,
- ) {
- scene(smallClockScene) {
- Column {
- SmallClock(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.element(smallClockElementKey).fillMaxWidth()
- )
- SmartSpaceContent()
- }
- }
-
- scene(largeClockScene) {
- Column {
- SmartSpaceContent()
- LargeClock(modifier = Modifier.element(largeClockElementKey).fillMaxWidth())
- }
- }
- }
- }
-
- @Composable
- private fun SceneScope.SmartSpaceContent(
- modifier: Modifier = Modifier,
- ) {
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
-
- MovableElement(key = smartspaceElementKey, modifier = modifier) {
- content {
- with(smartSpaceSection) {
- this@SmartSpaceContent.SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = {
- lockscreenContentViewModel.getSmartSpacePaddingTop(
- resources
- )
- },
- bottom = {
- resources.getDimensionPixelSize(
- R.dimen.keyguard_status_view_bottom_margin
- )
- }
- ),
- )
- }
- }
- }
- }
-
- @Composable
- private fun SceneScope.SmallClock(
+ fun SceneScope.SmallClock(
burnInParams: BurnInParameters,
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
@@ -152,58 +61,60 @@
viewModel.clock = currentClock
val context = LocalContext.current
-
- AndroidView(
- factory = { context ->
- FrameLayout(context).apply {
- val newClockView = checkNotNull(currentClock).smallClock.view
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- addView(newClockView)
- }
- },
- update = {
- val newClockView = checkNotNull(currentClock).smallClock.view
- it.removeAllViews()
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- it.addView(newClockView)
- },
- modifier =
- modifier
- .padding(
- horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
- )
- .padding(top = { viewModel.getSmallClockTopMargin(context) })
- .onTopPlacementChanged(onTopChanged)
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
+ MovableElement(key = smallClockElementKey, modifier = modifier) {
+ content {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addClockView(checkNotNull(currentClock).smallClock.view)
+ }
+ },
+ update = { it.addClockView(checkNotNull(currentClock).smallClock.view) },
+ modifier =
+ Modifier.padding(
+ horizontal =
+ dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ .padding(top = { viewModel.getSmallClockTopMargin(context) })
+ .onTopPlacementChanged(onTopChanged)
+ .burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ }
+ }
}
@Composable
- private fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
+ fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
val currentClock by viewModel.currentClock.collectAsState()
viewModel.clock = currentClock
if (currentClock?.largeClock?.view == null) {
return
}
- AndroidView(
- factory = { context ->
- FrameLayout(context).apply {
- val newClockView = checkNotNull(currentClock).largeClock.view
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- addView(newClockView)
- }
- },
- update = {
- val newClockView = checkNotNull(currentClock).largeClock.view
- it.removeAllViews()
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- it.addView(newClockView)
- },
- modifier = modifier.fillMaxSize()
- )
+ MovableElement(key = largeClockElementKey, modifier = modifier) {
+ content {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addClockView(checkNotNull(currentClock).largeClock.view)
+ }
+ },
+ update = { it.addClockView(checkNotNull(currentClock).largeClock.view) },
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
+ }
+
+ private fun FrameLayout.addClockView(clockView: View) {
+ if (contains(clockView)) {
+ return
+ }
+ removeAllViews()
+ (clockView.parent as? ViewGroup)?.removeView(clockView)
+ addView(clockView)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index d1cc53e..fc8b3b9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -30,16 +30,20 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
@@ -51,6 +55,7 @@
private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
+ private val lockscreenContentViewModel: LockscreenContentViewModel,
) {
@Composable
fun SceneScope.SmartSpace(
@@ -58,57 +63,71 @@
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
) {
- Column(
- modifier = modifier.onTopPlacementChanged(onTopChanged),
- ) {
- if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
- return
- }
+ val resources = LocalContext.current.resources
- val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
- val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+ MovableElement(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
+ Column(
+ modifier =
+ modifier
+ .onTopPlacementChanged(onTopChanged)
+ .padding(
+ top = { lockscreenContentViewModel.getSmartSpacePaddingTop(resources) },
+ bottom = {
+ resources.getDimensionPixelSize(
+ R.dimen.keyguard_status_view_bottom_margin
+ )
+ }
+ )
+ ) {
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+ return@Column
+ }
- if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
+ val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
+ val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+
+ if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.fillMaxWidth()
+ // All items will be constrained to be as tall as the shortest item.
+ .height(IntrinsicSize.Min)
+ .padding(
+ start = paddingBelowClockStart,
+ ),
+ ) {
+ Date(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Weather(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ }
+ }
+
+ Card(
modifier =
Modifier.fillMaxWidth()
- // All items will be constrained to be as tall as the shortest item.
- .height(IntrinsicSize.Min)
.padding(
start = paddingBelowClockStart,
- ),
- ) {
- Date(
- modifier =
- Modifier.burnInAware(
+ end = paddingBelowClockEnd,
+ )
+ .burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
),
- )
- Spacer(modifier = Modifier.width(4.dp))
- Weather(
- modifier =
- Modifier.burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
- }
+ )
}
-
- Card(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- start = paddingBelowClockStart,
- end = paddingBelowClockEnd,
- )
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
new file mode 100644
index 0000000..7635841
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeSmallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import javax.inject.Inject
+
+class TopAreaSection
+@Inject
+constructor(
+ private val clockViewModel: KeyguardClockViewModel,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val mediaCarouselSection: MediaCarouselSection,
+ private val notificationSection: NotificationSection,
+ private val clockSection: DefaultClockSection,
+ private val clockInteractor: KeyguardClockInteractor,
+) {
+ @Composable
+ fun DefaultClockLayoutWithNotifications(
+ modifier: Modifier = Modifier,
+ ) {
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+ val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+ val currentScene =
+ when (currentClockLayout) {
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
+ splitShadeLargeClockScene
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK ->
+ splitShadeSmallClockScene
+ KeyguardClockViewModel.ClockLayout.LARGE_CLOCK -> largeClockScene
+ KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene
+ }
+
+ val splitShadeTopMargin: Dp =
+ if (Flags.centralizedStatusBarHeightFix()) {
+ LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
+ } else {
+ dimensionResource(id = R.dimen.large_screen_shade_header_height)
+ }
+ val burnIn = rememberBurnIn(clockInteractor)
+
+ LaunchedEffect(isLargeClockVisible) {
+ if (isLargeClockVisible) {
+ burnIn.onSmallClockTopChanged(null)
+ }
+ }
+
+ SceneTransitionLayout(
+ modifier = modifier.fillMaxSize(),
+ currentScene = currentScene,
+ onChangeScene = {},
+ transitions = ClockTransition.defaultClockTransitions,
+ ) {
+ scene(ClockScenes.splitShadeLargeClockScene) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ Modifier.fillMaxHeight()
+ .weight(weight = 1f)
+ .padding(top = splitShadeTopMargin)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.splitShadeSmallClockScene) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(mediaCarouselSection) { MediaCarousel() }
+ }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ Modifier.fillMaxHeight()
+ .weight(weight = 1f)
+ .padding(top = splitShadeTopMargin)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.smallClockScene) {
+ Column {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(mediaCarouselSection) { MediaCarousel() }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ androidx.compose.ui.Modifier.fillMaxWidth().weight(weight = 1f)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.largeClockScene) {
+ Column {
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index fa94ea0..761292b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -87,7 +87,7 @@
enabled = state.isEnabled,
icon = { isDragging ->
if (isDragging) {
- Text(text = value.toInt().toString(), color = LocalContentColor.current)
+ Text(text = state.valueText, color = LocalContentColor.current)
} else {
state.icon?.let {
IconButton(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 63ec54f..82083f9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -605,6 +605,8 @@
override val isInitiatedByUserInput = true
+ override var bouncingScene: SceneKey? = null
+
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
@@ -694,14 +696,31 @@
): OffsetAnimation {
return startOffsetAnimation {
val animatable = Animatable(dragOffset, OffsetVisibilityThreshold)
+ val isTargetGreater = targetOffset > animatable.value
val job =
coroutineScope
.launch {
- animatable.animateTo(
- targetValue = targetOffset,
- animationSpec = swipeSpec,
- initialVelocity = initialVelocity,
- )
+ try {
+ animatable.animateTo(
+ targetValue = targetOffset,
+ animationSpec = swipeSpec,
+ initialVelocity = initialVelocity,
+ ) {
+ if (bouncingScene == null) {
+ val isBouncing =
+ if (isTargetGreater) {
+ value > targetOffset
+ } else {
+ value < targetOffset
+ }
+ if (isBouncing) {
+ bouncingScene = targetScene
+ }
+ }
+ }
+ } finally {
+ bouncingScene = null
+ }
}
// Make sure that we settle to target scene at the end of the animation or if
// the animation is cancelled.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index c7186da..f1177a8 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -588,7 +588,8 @@
// TODO(b/290184746): Make sure that we don't overflow transformations associated to a
// range.
val directionSign = if (transition.isUpOrLeft) -1 else 1
- val overscrollProgress = transition.progress.let { if (it > 1f) it - 1f else it }
+ val isToScene = overscroll.scene == transition.toScene
+ val overscrollProgress = transition.progress.let { if (isToScene) it - 1f else it }
val progress = directionSign * overscrollProgress
val rangeProgress = propertySpec.range?.progress(progress) ?: progress
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index e6f5d58..617a8ea 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -255,6 +255,12 @@
*/
val overscrollScope: OverscrollScope
+ /**
+ * The scene around which the transition is currently bouncing. When not `null`, this
+ * transition is currently oscillating around this scene and will soon settle to that scene.
+ */
+ val bouncingScene: SceneKey?
+
companion object {
const val DistanceUnspecified = 0f
}
@@ -287,9 +293,10 @@
val transition = currentTransition ?: return null
if (transition !is TransitionState.HasOverscrollProperties) return null
val progress = transition.progress
+ val bouncingScene = transition.bouncingScene
return when {
- progress < 0f -> fromOverscrollSpec
- progress > 1f -> toOverscrollSpec
+ progress < 0f || bouncingScene == transition.fromScene -> fromOverscrollSpec
+ progress > 1f || bouncingScene == transition.toScene -> toOverscrollSpec
else -> null
}
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 26e01fe..0804761 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -16,6 +16,8 @@
package com.android.compose.animation.scene
+import androidx.compose.animation.core.Spring
+import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
@@ -752,4 +754,55 @@
assertThat(state.currentOverscrollSpec).isNotNull()
fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * 1.5f)
}
+
+ @Test
+ fun elementTransitionWithDistanceDuringOverscrollBouncing() {
+ val layoutWidth = 200.dp
+ val layoutHeight = 400.dp
+ val state =
+ setupOverscrollScenario(
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight,
+ sceneTransitions = {
+ defaultSwipeSpec =
+ spring(
+ dampingRatio = Spring.DampingRatioMediumBouncy,
+ stiffness = Spring.StiffnessLow,
+ )
+
+ overscroll(TestScenes.SceneB, Orientation.Vertical) {
+ // On overscroll 100% -> Foo should translate by layoutHeight
+ translate(TestElements.Foo, y = { absoluteDistance })
+ }
+ },
+ firstScroll = 1f, // 100% scroll
+ )
+
+ val fooElement = rule.onNodeWithTag(TestElements.Foo.testTag, useUnmergedTree = true)
+ fooElement.assertTopPositionInRootIsEqualTo(0.dp)
+
+ rule.onRoot().performTouchInput {
+ // Scroll another 50%
+ moveBy(Offset(0f, layoutHeight.toPx() * 0.5f), delayMillis = 1_000)
+ }
+
+ val transition = state.currentTransition
+ assertThat(transition).isNotNull()
+ transition as TransitionState.HasOverscrollProperties
+
+ // Scroll 150% (100% scroll + 50% overscroll)
+ assertThat(transition.progress).isEqualTo(1.5f)
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ fooElement.assertTopPositionInRootIsEqualTo(layoutHeight * (transition.progress - 1f))
+
+ // finger raised
+ rule.onRoot().performTouchInput { up() }
+
+ // The target value is 1f, but the spring (defaultSwipeSpec) allows you to go to a lower
+ // value.
+ rule.waitUntil(timeoutMillis = 10_000) { transition.progress < 1f }
+
+ assertThat(state.currentOverscrollSpec).isNotNull()
+ assertThat(transition.bouncingScene).isEqualTo(transition.toScene)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
index 73a66c6..a32fe22 100644
--- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/Transition.kt
@@ -27,6 +27,7 @@
isInitiatedByUserInput: Boolean = false,
isUserInputOngoing: Boolean = false,
isUpOrLeft: Boolean = false,
+ bouncingScene: SceneKey? = null,
orientation: Orientation = Orientation.Horizontal,
): TransitionState.Transition {
return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
@@ -37,6 +38,7 @@
override val isInitiatedByUserInput: Boolean = isInitiatedByUserInput
override val isUserInputOngoing: Boolean = isUserInputOngoing
override val isUpOrLeft: Boolean = isUpOrLeft
+ override val bouncingScene: SceneKey? = bouncingScene
override val orientation: Orientation = orientation
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
diff --git a/packages/SystemUI/monet/Android.bp b/packages/SystemUI/monet/Android.bp
index 98f7ace..c54fdab 100644
--- a/packages/SystemUI/monet/Android.bp
+++ b/packages/SystemUI/monet/Android.bp
@@ -24,6 +24,7 @@
static_libs: [
"androidx.annotation_annotation",
"androidx.core_core",
+ "libmonet",
],
srcs: [
"src/**/*.java",
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 46a90f6..47a00f4 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -20,11 +20,17 @@
import android.app.WallpaperColors
import android.graphics.Color
import com.android.internal.graphics.ColorUtils
-import com.android.internal.graphics.cam.Cam
-import com.android.internal.graphics.cam.CamUtils
+import com.google.ux.material.libmonet.hct.Hct
+import com.google.ux.material.libmonet.scheme.DynamicScheme
+import com.google.ux.material.libmonet.scheme.SchemeContent
+import com.google.ux.material.libmonet.scheme.SchemeExpressive
+import com.google.ux.material.libmonet.scheme.SchemeFruitSalad
+import com.google.ux.material.libmonet.scheme.SchemeMonochrome
+import com.google.ux.material.libmonet.scheme.SchemeNeutral
+import com.google.ux.material.libmonet.scheme.SchemeRainbow
+import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
+import com.google.ux.material.libmonet.scheme.SchemeVibrant
import kotlin.math.absoluteValue
-import kotlin.math.max
-import kotlin.math.min
import kotlin.math.roundToInt
const val TAG = "ColorScheme"
@@ -33,347 +39,65 @@
const val GOOGLE_BLUE = 0xFF1b6ef3.toInt()
const val MIN_CHROMA = 5
-internal interface Hue {
- fun get(sourceColor: Cam): Double
-
- /**
- * Given a hue, and a mapping of hues to hue rotations, find which hues in the mapping the hue
- * fall betweens, and use the hue rotation of the lower hue.
- *
- * @param sourceHue hue of source color
- * @param hueAndRotations list of pairs, where the first item in a pair is a hue, and the second
- * item in the pair is a hue rotation that should be applied
- */
- fun getHueRotation(sourceHue: Float, hueAndRotations: List<Pair<Int, Int>>): Double {
- val sanitizedSourceHue = (if (sourceHue < 0 || sourceHue >= 360) 0 else sourceHue).toFloat()
- for (i in 0..hueAndRotations.size - 2) {
- val thisHue = hueAndRotations[i].first.toFloat()
- val nextHue = hueAndRotations[i + 1].first.toFloat()
- if (thisHue <= sanitizedSourceHue && sanitizedSourceHue < nextHue) {
- return ColorScheme.wrapDegreesDouble(
- sanitizedSourceHue.toDouble() + hueAndRotations[i].second
- )
- }
- }
-
- // If this statement executes, something is wrong, there should have been a rotation
- // found using the arrays.
- return sourceHue.toDouble()
- }
-}
-
-internal class HueSource : Hue {
- override fun get(sourceColor: Cam): Double {
- return sourceColor.hue.toDouble()
- }
-}
-
-internal class HueAdd(val amountDegrees: Double) : Hue {
- override fun get(sourceColor: Cam): Double {
- return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() + amountDegrees)
- }
-}
-
-internal class HueSubtract(val amountDegrees: Double) : Hue {
- override fun get(sourceColor: Cam): Double {
- return ColorScheme.wrapDegreesDouble(sourceColor.hue.toDouble() - amountDegrees)
- }
-}
-
-internal class HueVibrantSecondary() : Hue {
- val hueToRotations =
- listOf(
- Pair(0, 18),
- Pair(41, 15),
- Pair(61, 10),
- Pair(101, 12),
- Pair(131, 15),
- Pair(181, 18),
- Pair(251, 15),
- Pair(301, 12),
- Pair(360, 12)
- )
-
- override fun get(sourceColor: Cam): Double {
- return getHueRotation(sourceColor.hue, hueToRotations)
- }
-}
-
-internal class HueVibrantTertiary() : Hue {
- val hueToRotations =
- listOf(
- Pair(0, 35),
- Pair(41, 30),
- Pair(61, 20),
- Pair(101, 25),
- Pair(131, 30),
- Pair(181, 35),
- Pair(251, 30),
- Pair(301, 25),
- Pair(360, 25)
- )
-
- override fun get(sourceColor: Cam): Double {
- return getHueRotation(sourceColor.hue, hueToRotations)
- }
-}
-
-internal class HueExpressiveSecondary() : Hue {
- val hueToRotations =
- listOf(
- Pair(0, 45),
- Pair(21, 95),
- Pair(51, 45),
- Pair(121, 20),
- Pair(151, 45),
- Pair(191, 90),
- Pair(271, 45),
- Pair(321, 45),
- Pair(360, 45)
- )
-
- override fun get(sourceColor: Cam): Double {
- return getHueRotation(sourceColor.hue, hueToRotations)
- }
-}
-
-internal class HueExpressiveTertiary() : Hue {
- val hueToRotations =
- listOf(
- Pair(0, 120),
- Pair(21, 120),
- Pair(51, 20),
- Pair(121, 45),
- Pair(151, 20),
- Pair(191, 15),
- Pair(271, 20),
- Pair(321, 120),
- Pair(360, 120)
- )
-
- override fun get(sourceColor: Cam): Double {
- return getHueRotation(sourceColor.hue, hueToRotations)
- }
-}
-
-internal interface Chroma {
- fun get(sourceColor: Cam): Double
-
- companion object {
- val MAX_VALUE = 120.0
- val MIN_VALUE = 0.0
- }
-}
-
-internal class ChromaMaxOut : Chroma {
- override fun get(sourceColor: Cam): Double {
- // Intentionally high. Gamut mapping from impossible HCT to sRGB will ensure that
- // the maximum chroma is reached, even if lower than this constant.
- return Chroma.MAX_VALUE + 10.0
- }
-}
-
-internal class ChromaMultiple(val multiple: Double) : Chroma {
- override fun get(sourceColor: Cam): Double {
- return sourceColor.chroma * multiple
- }
-}
-
-internal class ChromaAdd(val amount: Double) : Chroma {
- override fun get(sourceColor: Cam): Double {
- return sourceColor.chroma + amount
- }
-}
-
-internal class ChromaBound(
- val baseChroma: Chroma,
- val minVal: Double,
- val maxVal: Double,
-) : Chroma {
- override fun get(sourceColor: Cam): Double {
- val result = baseChroma.get(sourceColor)
- return min(max(result, minVal), maxVal)
- }
-}
-
-internal class ChromaConstant(val chroma: Double) : Chroma {
- override fun get(sourceColor: Cam): Double {
- return chroma
- }
-}
-
-internal class ChromaSource : Chroma {
- override fun get(sourceColor: Cam): Double {
- return sourceColor.chroma.toDouble()
- }
-}
-
-internal class TonalSpec(val hue: Hue = HueSource(), val chroma: Chroma) {
- fun shades(sourceColor: Cam): List<Int> {
- val hue = hue.get(sourceColor)
- val chroma = chroma.get(sourceColor)
- return Shades.of(hue.toFloat(), chroma.toFloat()).toList()
- }
-
- fun getAtTone(sourceColor: Cam, tone: Float): Int {
- val hue = hue.get(sourceColor)
- val chroma = chroma.get(sourceColor)
- return ColorUtils.CAMToColor(hue.toFloat(), chroma.toFloat(), (1000f - tone) / 10f)
- }
-}
-
-internal class CoreSpec(
- val a1: TonalSpec,
- val a2: TonalSpec,
- val a3: TonalSpec,
- val n1: TonalSpec,
- val n2: TonalSpec
-)
-
-enum class Style(internal val coreSpec: CoreSpec) {
- SPRITZ(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaConstant(12.0)),
- a2 = TonalSpec(HueSource(), ChromaConstant(8.0)),
- a3 = TonalSpec(HueSource(), ChromaConstant(16.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(2.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(2.0))
- )
- ),
- TONAL_SPOT(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaConstant(36.0)),
- a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
- a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(6.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(8.0))
- )
- ),
- VIBRANT(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaMaxOut()),
- a2 = TonalSpec(HueVibrantSecondary(), ChromaConstant(24.0)),
- a3 = TonalSpec(HueVibrantTertiary(), ChromaConstant(32.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(12.0))
- )
- ),
- EXPRESSIVE(
- CoreSpec(
- a1 = TonalSpec(HueAdd(240.0), ChromaConstant(40.0)),
- a2 = TonalSpec(HueExpressiveSecondary(), ChromaConstant(24.0)),
- a3 = TonalSpec(HueExpressiveTertiary(), ChromaConstant(32.0)),
- n1 = TonalSpec(HueAdd(15.0), ChromaConstant(8.0)),
- n2 = TonalSpec(HueAdd(15.0), ChromaConstant(12.0))
- )
- ),
- RAINBOW(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaConstant(48.0)),
- a2 = TonalSpec(HueSource(), ChromaConstant(16.0)),
- a3 = TonalSpec(HueAdd(60.0), ChromaConstant(24.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
- )
- ),
- FRUIT_SALAD(
- CoreSpec(
- a1 = TonalSpec(HueSubtract(50.0), ChromaConstant(48.0)),
- a2 = TonalSpec(HueSubtract(50.0), ChromaConstant(36.0)),
- a3 = TonalSpec(HueSource(), ChromaConstant(36.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(10.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(16.0))
- )
- ),
- CONTENT(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaSource()),
- a2 = TonalSpec(HueSource(), ChromaMultiple(0.33)),
- a3 = TonalSpec(HueSource(), ChromaMultiple(0.66)),
- n1 = TonalSpec(HueSource(), ChromaMultiple(0.0833)),
- n2 = TonalSpec(HueSource(), ChromaMultiple(0.1666))
- )
- ),
- MONOCHROMATIC(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaConstant(.0)),
- a2 = TonalSpec(HueSource(), ChromaConstant(.0)),
- a3 = TonalSpec(HueSource(), ChromaConstant(.0)),
- n1 = TonalSpec(HueSource(), ChromaConstant(.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(.0))
- )
- ),
- CLOCK(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 20.0, Chroma.MAX_VALUE)),
- a2 = TonalSpec(HueAdd(10.0), ChromaBound(ChromaMultiple(0.85), 17.0, 40.0)),
- a3 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaAdd(20.0), 50.0, Chroma.MAX_VALUE)),
-
- // Not Used
- n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
- )
- ),
- CLOCK_VIBRANT(
- CoreSpec(
- a1 = TonalSpec(HueSource(), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
- a2 = TonalSpec(HueAdd(20.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
- a3 = TonalSpec(HueAdd(60.0), ChromaBound(ChromaSource(), 70.0, Chroma.MAX_VALUE)),
-
- // Not Used
- n1 = TonalSpec(HueSource(), ChromaConstant(0.0)),
- n2 = TonalSpec(HueSource(), ChromaConstant(0.0))
- )
- )
+enum class Style{
+ SPRITZ,
+ TONAL_SPOT,
+ VIBRANT,
+ EXPRESSIVE,
+ RAINBOW,
+ FRUIT_SALAD,
+ CONTENT,
+ MONOCHROMATIC,
+ CLOCK,
+ CLOCK_VIBRANT
}
class TonalPalette
internal constructor(
- private val spec: TonalSpec,
- seedColor: Int,
+ private val materialTonalPalette: com.google.ux.material.libmonet.palettes.TonalPalette
) {
- val seedCam: Cam = Cam.fromInt(seedColor)
- val allShades: List<Int> = spec.shades(seedCam)
- val allShadesMapped: Map<Int, Int> = SHADE_KEYS.zip(allShades).toMap()
- val baseColor: Int
+ @Deprecated("Do not use. For color system only")
+ val allShades: List<Int>
+ val allShadesMapped: Map<Int, Int>
- init {
- val h = spec.hue.get(seedCam).toFloat()
- val c = spec.chroma.get(seedCam).toFloat()
- baseColor = ColorUtils.CAMToColor(h, c, CamUtils.lstarFromInt(seedColor))
+ init{
+ allShades = SHADE_KEYS.map {key -> getAtTone(key.toFloat()) }
+ allShadesMapped = SHADE_KEYS.zip(allShades).toMap()
}
// Dynamically computed tones across the full range from 0 to 1000
- fun getAtTone(tone: Float) = spec.getAtTone(seedCam, tone)
+ fun getAtTone(shade: Float): Int = materialTonalPalette.tone(((1000.0f - shade) / 10f).toInt())
// Predefined & precomputed tones
- val s10: Int
+ val s0: Int
get() = this.allShades[0]
- val s50: Int
+ val s10: Int
get() = this.allShades[1]
- val s100: Int
+ val s50: Int
get() = this.allShades[2]
- val s200: Int
+ val s100: Int
get() = this.allShades[3]
- val s300: Int
+ val s200: Int
get() = this.allShades[4]
- val s400: Int
+ val s300: Int
get() = this.allShades[5]
- val s500: Int
+ val s400: Int
get() = this.allShades[6]
- val s600: Int
+ val s500: Int
get() = this.allShades[7]
- val s700: Int
+ val s600: Int
get() = this.allShades[8]
- val s800: Int
+ val s700: Int
get() = this.allShades[9]
- val s900: Int
+ val s800: Int
get() = this.allShades[10]
- val s1000: Int
+ val s900: Int
get() = this.allShades[11]
+ val s1000: Int
+ get() = this.allShades[12]
companion object {
- val SHADE_KEYS = listOf(10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
+ val SHADE_KEYS = listOf(0, 10, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000)
}
}
@@ -381,9 +105,20 @@
"instead")
class ColorScheme(
@ColorInt val seed: Int,
- val darkTheme: Boolean,
- val style: Style = Style.TONAL_SPOT
+ val isDark: Boolean,
+ val style: Style,
+ val contrastLevel: Double
) {
+ var materialScheme: DynamicScheme
+
+ private val proposedSeedHct: Hct = Hct.fromInt(seed)
+ private val seedHct: Hct = Hct.fromInt(if (seed == Color.TRANSPARENT) {
+ GOOGLE_BLUE
+ } else if (style != Style.CONTENT && proposedSeedHct.chroma < 5) {
+ GOOGLE_BLUE
+ } else {
+ seed
+ })
val accent1: TonalPalette
val accent2: TonalPalette
@@ -395,62 +130,49 @@
@JvmOverloads
constructor(
+ @ColorInt seed: Int,
+ darkTheme: Boolean,
+ style: Style
+ ) : this(seed, darkTheme, style, 0.5)
+
+ @JvmOverloads
+ constructor(
wallpaperColors: WallpaperColors,
darkTheme: Boolean,
style: Style = Style.TONAL_SPOT
) : this(getSeedColor(wallpaperColors, style != Style.CONTENT), darkTheme, style)
- val allHues: List<TonalPalette>
- get() {
- return listOf(accent1, accent2, accent3, neutral1, neutral2)
- }
-
- val allAccentColors: List<Int>
- get() {
- val allColors = mutableListOf<Int>()
- allColors.addAll(accent1.allShades)
- allColors.addAll(accent2.allShades)
- allColors.addAll(accent3.allShades)
- return allColors
- }
-
- val allNeutralColors: List<Int>
- get() {
- val allColors = mutableListOf<Int>()
- allColors.addAll(neutral1.allShades)
- allColors.addAll(neutral2.allShades)
- return allColors
- }
-
val backgroundColor
- get() = ColorUtils.setAlphaComponent(if (darkTheme) neutral1.s700 else neutral1.s10, 0xFF)
+ get() = ColorUtils.setAlphaComponent(if (isDark) neutral1.s700 else neutral1.s10, 0xFF)
val accentColor
- get() = ColorUtils.setAlphaComponent(if (darkTheme) accent1.s100 else accent1.s500, 0xFF)
+ get() = ColorUtils.setAlphaComponent(if (isDark) accent1.s100 else accent1.s500, 0xFF)
init {
- val proposedSeedCam = Cam.fromInt(seed)
- val seedArgb =
- if (seed == Color.TRANSPARENT) {
- GOOGLE_BLUE
- } else if (style != Style.CONTENT && proposedSeedCam.chroma < 5) {
- GOOGLE_BLUE
- } else {
- seed
- }
+ materialScheme = when (style) {
+ Style.SPRITZ -> SchemeNeutral(seedHct, isDark, contrastLevel)
+ Style.TONAL_SPOT -> SchemeTonalSpot(seedHct, isDark, contrastLevel)
+ Style.VIBRANT -> SchemeVibrant(seedHct, isDark, contrastLevel)
+ Style.EXPRESSIVE -> SchemeExpressive(seedHct, isDark, contrastLevel)
+ Style.RAINBOW -> SchemeRainbow(seedHct, isDark, contrastLevel)
+ Style.FRUIT_SALAD -> SchemeFruitSalad(seedHct, isDark, contrastLevel)
+ Style.CONTENT -> SchemeContent(seedHct, isDark, contrastLevel)
+ Style.MONOCHROMATIC -> SchemeMonochrome(seedHct, isDark, contrastLevel)
- accent1 = TonalPalette(style.coreSpec.a1, seedArgb)
- accent2 = TonalPalette(style.coreSpec.a2, seedArgb)
- accent3 = TonalPalette(style.coreSpec.a3, seedArgb)
- neutral1 = TonalPalette(style.coreSpec.n1, seedArgb)
- neutral2 = TonalPalette(style.coreSpec.n2, seedArgb)
+ // SystemUI Schemes
+ Style.CLOCK -> SchemeClock(seedHct, isDark, contrastLevel)
+ Style.CLOCK_VIBRANT -> SchemeClockVibrant(seedHct, isDark, contrastLevel)
+ }
+
+ accent1 = TonalPalette(materialScheme.primaryPalette)
+ accent2 = TonalPalette(materialScheme.secondaryPalette)
+ accent3 = TonalPalette(materialScheme.tertiaryPalette)
+ neutral1 = TonalPalette(materialScheme.neutralPalette)
+ neutral2 = TonalPalette(materialScheme.neutralVariantPalette)
}
- val shadeCount
- get() = this.accent1.allShades.size
-
val seedTone: Float
- get() = 1000f - CamUtils.lstarFromInt(seed) * 10f
+ get() = 1000f - proposedSeedHct.tone.toFloat() * 10f
override fun toString(): String {
return "ColorScheme {\n" +
@@ -507,7 +229,7 @@
if (!filter) {
true
} else {
- Cam.fromInt(it).chroma >= MIN_CHROMA
+ Hct.fromInt(it).chroma >= MIN_CHROMA
}
}
.toList()
@@ -519,15 +241,15 @@
val intToProportion =
wallpaperColors.allColors.mapValues { it.value.toDouble() / totalPopulation }
- val intToCam = wallpaperColors.allColors.mapValues { Cam.fromInt(it.key) }
+ val intToHct = wallpaperColors.allColors.mapValues { Hct.fromInt(it.key) }
// Get an array with 360 slots. A slot contains the percentage of colors with that hue.
- val hueProportions = huePopulations(intToCam, intToProportion, filter)
+ val hueProportions = huePopulations(intToHct, intToProportion, filter)
// Map each color to the percentage of the image with its hue.
val intToHueProportion =
wallpaperColors.allColors.mapValues {
- val cam = intToCam[it.key]!!
- val hue = cam.hue.roundToInt()
+ val hct = intToHct[it.key]!!
+ val hue = hct.hue.roundToInt()
var proportion = 0.0
for (i in hue - 15..hue + 15) {
proportion += hueProportions[wrapDegrees(i)]
@@ -537,18 +259,18 @@
// Remove any inappropriate seed colors. For example, low chroma colors look grayscale
// raising their chroma will turn them to a much louder color that may not have been
// in the image.
- val filteredIntToCam =
- if (!filter) intToCam
+ val filteredIntToHct =
+ if (!filter) intToHct
else
- (intToCam.filter {
- val cam = it.value
+ (intToHct.filter {
+ val hct = it.value
val proportion = intToHueProportion[it.key]!!
- cam.chroma >= MIN_CHROMA &&
+ hct.chroma >= MIN_CHROMA &&
(totalPopulationMeaningless || proportion > 0.01)
})
// Sort the colors by score, from high to low.
val intToScoreIntermediate =
- filteredIntToCam.mapValues { score(it.value, intToHueProportion[it.key]!!) }
+ filteredIntToHct.mapValues { score(it.value, intToHueProportion[it.key]!!) }
val intToScore = intToScoreIntermediate.entries.toMutableList()
intToScore.sortByDescending { it.value }
@@ -564,8 +286,8 @@
val int = entry.key
val existingSeedNearby =
seeds.find {
- val hueA = intToCam[int]!!.hue
- val hueB = intToCam[it]!!.hue
+ val hueA = intToHct[int]!!.hue
+ val hueB = intToHct[it]!!.hue
hueDiff(hueA, hueB) < i
} != null
if (existingSeedNearby) {
@@ -600,30 +322,16 @@
}
}
- public fun wrapDegreesDouble(degrees: Double): Double {
- return when {
- degrees < 0 -> {
- (degrees % 360) + 360
- }
- degrees >= 360 -> {
- degrees % 360
- }
- else -> {
- degrees
- }
- }
- }
-
- private fun hueDiff(a: Float, b: Float): Float {
+ private fun hueDiff(a: Double, b: Double): Double {
return 180f - ((a - b).absoluteValue - 180f).absoluteValue
}
private fun stringForColor(color: Int): String {
val width = 4
- val hct = Cam.fromInt(color)
+ val hct = Hct.fromInt(color)
val h = "H${hct.hue.roundToInt().toString().padEnd(width)}"
val c = "C${hct.chroma.roundToInt().toString().padEnd(width)}"
- val t = "T${CamUtils.lstarFromInt(color).roundToInt().toString().padEnd(width)}"
+ val t = "T${hct.tone.roundToInt().toString().padEnd(width)}"
val hex = Integer.toHexString(color and 0xffffff).padStart(6, '0').uppercase()
return "$h$c$t = #$hex"
}
@@ -633,16 +341,16 @@
colors.map { stringForColor(it) }.joinToString(separator = "\n") { it }
}
- private fun score(cam: Cam, proportion: Double): Double {
+ private fun score(hct: Hct, proportion: Double): Double {
val proportionScore = 0.7 * 100.0 * proportion
val chromaScore =
- if (cam.chroma < ACCENT1_CHROMA) 0.1 * (cam.chroma - ACCENT1_CHROMA)
- else 0.3 * (cam.chroma - ACCENT1_CHROMA)
+ if (hct.chroma < ACCENT1_CHROMA) 0.1 * (hct.chroma - ACCENT1_CHROMA)
+ else 0.3 * (hct.chroma - ACCENT1_CHROMA)
return chromaScore + proportionScore
}
private fun huePopulations(
- camByColor: Map<Int, Cam>,
+ hctByColor: Map<Int, Hct>,
populationByColor: Map<Int, Double>,
filter: Boolean = true
): List<Double> {
@@ -650,9 +358,9 @@
for (entry in populationByColor.entries) {
val population = populationByColor[entry.key]!!
- val cam = camByColor[entry.key]!!
- val hue = cam.hue.roundToInt() % 360
- if (filter && cam.chroma <= MIN_CHROMA) {
+ val hct = hctByColor[entry.key]!!
+ val hue = hct.hue.roundToInt() % 360
+ if (filter && hct.chroma <= MIN_CHROMA) {
continue
}
huePopulation[hue] = huePopulation[hue] + population
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java
new file mode 100644
index 0000000..4747cc5
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClock.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet;
+
+import static com.google.ux.material.libmonet.utils.MathUtils.clampDouble;
+
+import static java.lang.Double.max;
+
+import com.google.ux.material.libmonet.hct.Hct;
+import com.google.ux.material.libmonet.palettes.TonalPalette;
+import com.google.ux.material.libmonet.scheme.DynamicScheme;
+import com.google.ux.material.libmonet.scheme.Variant;
+
+public class SchemeClock extends DynamicScheme {
+ public SchemeClock(Hct sourceColorHct, boolean isDark, double contrastLevel) {
+ super(
+ sourceColorHct,
+ Variant.MONOCHROME,
+ isDark,
+ contrastLevel,
+ /*primary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue(),
+ /*chroma*/ max(sourceColorHct.getChroma(), 20)
+ ),
+ /*secondary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue() + 10.0,
+ /*chroma*/ clampDouble(17, 40, sourceColorHct.getChroma() * 0.85)
+ ),
+ /*tertiary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue() + 20.0,
+ /*chroma*/ max(sourceColorHct.getChroma() + 20, 50)
+ ),
+
+ //not used
+ TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0),
+ TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0));
+ }
+}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java
new file mode 100644
index 0000000..fb5e972
--- /dev/null
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/SchemeClockVibrant.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.monet;
+
+import static java.lang.Double.max;
+
+import com.google.ux.material.libmonet.hct.Hct;
+import com.google.ux.material.libmonet.palettes.TonalPalette;
+import com.google.ux.material.libmonet.scheme.DynamicScheme;
+import com.google.ux.material.libmonet.scheme.Variant;
+
+public class SchemeClockVibrant extends DynamicScheme {
+ public SchemeClockVibrant(Hct sourceColorHct, boolean isDark, double contrastLevel) {
+ super(
+ sourceColorHct,
+ Variant.MONOCHROME,
+ isDark,
+ contrastLevel,
+ /*primary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue(),
+ /*chroma*/ max(sourceColorHct.getChroma(), 70)
+ ),
+ /*secondary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue() + 20.0,
+ /*chroma*/ max(sourceColorHct.getChroma(), 70)
+ ),
+ /*tertiary*/
+ TonalPalette.fromHueAndChroma(
+ /*hue*/ sourceColorHct.getHue() + 60.0,
+ /*chroma*/ max(sourceColorHct.getChroma(), 70)
+ ),
+
+ //not used
+ TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0),
+ TonalPalette.fromHueAndChroma(sourceColorHct.getHue(), 0.0));
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 24c651f3..a9541d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -61,6 +61,7 @@
private val testScope = kosmos.testScope
private val repository by lazy { kosmos.fakeKeyguardRepository }
private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
private val commandQueue by lazy { FakeCommandQueue() }
private val bouncerRepository = FakeKeyguardBouncerRepository()
private val shadeRepository = FakeShadeRepository()
@@ -79,6 +80,7 @@
shadeRepository = shadeRepository,
keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
sceneInteractorProvider = { sceneInteractor },
+ fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 6e15c51..fc604aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -348,6 +348,48 @@
}
@Test
+ fun alpha_shadeClosedOverLockscreen_isOne() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ // Transition to the lockscreen.
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ // Open the shade.
+ shadeRepository.setQsExpansion(1f)
+ assertThat(alpha).isEqualTo(0f)
+
+ // Close the shade, alpha returns to 1.
+ shadeRepository.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(1f)
+ }
+
+ @Test
+ fun alpha_shadeClosedOverDream_isZero() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ // Transition to dreaming.
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DREAMING,
+ testScope,
+ )
+
+ // Open the shade.
+ shadeRepository.setQsExpansion(1f)
+ assertThat(alpha).isEqualTo(0f)
+
+ // Close the shade, alpha is still 0 since we're not on the lockscreen.
+ shadeRepository.setQsExpansion(0f)
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
fun alpha_idleOnOccluded_isZero() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index ad1cef1..751ac1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,7 +26,8 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -47,7 +48,7 @@
fun setup() {
with(kosmos) {
fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeRepository.setShadeMode(ShadeMode.Single)
underTest = lockscreenContentViewModel
}
}
@@ -92,10 +93,10 @@
fun areNotificationsVisible_splitShadeTrue_true() =
with(kosmos) {
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeRepository.setShadeMode(ShadeMode.Split)
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ assertThat(underTest.areNotificationsVisible).isTrue()
}
}
@Test
@@ -103,7 +104,7 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ assertThat(underTest.areNotificationsVisible).isTrue()
}
}
@@ -112,7 +113,34 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
+ assertThat(underTest.areNotificationsVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun shouldUseSplitNotificationShade_withConfigTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ assertThat(underTest.shouldUseSplitNotificationShade).isTrue()
+ }
+ }
+
+ @Test
+ fun shouldUseSplitNotificationShade_withConfigFalse_false() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
+ }
+ }
+
+ @Test
+ fun sceneKey() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 183a58a..be63301 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
+import com.android.systemui.statusbar.policy.HeadsUpManagerTestUtil.createFullScreenIntentEntry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.settings.FakeGlobalSettings
import com.android.systemui.util.time.FakeSystemClock
@@ -97,6 +98,12 @@
return entry
}
+ private fun createFsiHeadsUpEntry(id: Int): BaseHeadsUpManager.HeadsUpEntry {
+ val entry = testableHeadsUpManager!!.createHeadsUpEntry()
+ entry.setEntry(createFullScreenIntentEntry(id, mContext))
+ return entry
+ }
+
@Test
fun testUpdate_isShowing_runsRunnable() {
// Entry is showing
@@ -238,4 +245,68 @@
// Next entry is shown
Truth.assertThat(mAvalancheController.headsUpEntryShowing).isEqualTo(nextEntry)
}
+
+ @Test
+ fun testGetDurationMs_lastEntry_useAutoDismissTime() {
+ // Entry is showing
+ val showingEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.headsUpEntryShowing = showingEntry
+
+ // Nothing is next
+ mAvalancheController.clearNext()
+
+ val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+ Truth.assertThat(durationMs).isEqualTo(5000)
+ }
+
+ @Test
+ fun testGetDurationMs_nextEntryLowerPriority_500() {
+ // Entry is showing
+ val showingEntry = createFsiHeadsUpEntry(id = 1)
+ mAvalancheController.headsUpEntryShowing = showingEntry
+
+ // There's another entry waiting to show next
+ val nextEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+ // Next entry has lower priority
+ Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(1)
+
+ val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+ Truth.assertThat(durationMs).isEqualTo(5000)
+ }
+
+ @Test
+ fun testGetDurationMs_nextEntrySamePriority_1000() {
+ // Entry is showing
+ val showingEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.headsUpEntryShowing = showingEntry
+
+ // There's another entry waiting to show next
+ val nextEntry = createHeadsUpEntry(id = 1)
+ mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+ // Same priority
+ Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(0)
+
+ val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+ Truth.assertThat(durationMs).isEqualTo(1000)
+ }
+
+ @Test
+ fun testGetDurationMs_nextEntryHigherPriority_500() {
+ // Entry is showing
+ val showingEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.headsUpEntryShowing = showingEntry
+
+ // There's another entry waiting to show next
+ val nextEntry = createFsiHeadsUpEntry(id = 1)
+ mAvalancheController.addToNext(nextEntry, runnableMock!!)
+
+ // Next entry has higher priority
+ Truth.assertThat(nextEntry.compareNonTimeFields(showingEntry)).isEqualTo(-1)
+
+ val durationMs = mAvalancheController.getDurationMs(showingEntry, autoDismissMs = 5000)
+ Truth.assertThat(durationMs).isEqualTo(500)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 830bcef..ed0d272 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -117,21 +117,6 @@
return HeadsUpManagerTestUtil.createEntry(id, notif);
}
- private PendingIntent createFullScreenIntent() {
- return PendingIntent.getActivity(
- getContext(), 0, new Intent(getContext(), this.getClass()),
- PendingIntent.FLAG_MUTABLE_UNAUDITED);
- }
-
- private NotificationEntry createFullScreenIntentEntry(int id) {
- final Notification notif = new Notification.Builder(mContext, "")
- .setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(createFullScreenIntent(), /* highPriority */ true)
- .build();
- return HeadsUpManagerTestUtil.createEntry(id, notif);
- }
-
-
private void useAccessibilityTimeout(boolean use) {
if (use) {
doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
@@ -239,7 +224,8 @@
@Test
public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+ final NotificationEntry notifEntry =
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
// Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
hum.showNotification(notifEntry);
@@ -254,7 +240,8 @@
@Test
public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+ final NotificationEntry notifEntry =
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
// Add notifEntry to ANM mAlertEntries map and make it unpinned
hum.showNotification(notifEntry);
@@ -443,7 +430,8 @@
@Test
public void testIsSticky_hasFullScreenIntent_true() {
final BaseHeadsUpManager hum = createHeadsUpManager();
- final NotificationEntry notifEntry = createFullScreenIntentEntry(/* id = */ 0);
+ final NotificationEntry notifEntry =
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext);
hum.showNotification(notifEntry);
@@ -554,7 +542,8 @@
// Needs full screen intent in order to be pinned
final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry();
- entryToPin.setEntry(createFullScreenIntentEntry(/* id = */ 0));
+ entryToPin.setEntry(
+ HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
// Note: the standard way to show a notification would be calling showNotification rather
// than onAlertEntryAdded. However, in practice showNotification in effect adds
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
index c70b03b..bda8619 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTestUtil.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.os.UserHandle;
import android.content.Context;
@@ -67,4 +69,16 @@
return new NotificationEntryBuilder().setSbn(
HeadsUpManagerTestUtil.createSbn(id, context)).build();
}
+
+ protected static NotificationEntry createFullScreenIntentEntry(int id, Context context) {
+ final PendingIntent intent = PendingIntent.getActivity(
+ context, 0, new Intent(),
+ PendingIntent.FLAG_IMMUTABLE);
+
+ final Notification notif = new Notification.Builder(context, "")
+ .setSmallIcon(com.android.systemui.res.R.drawable.ic_person)
+ .setFullScreenIntent(intent, /* highPriority */ true)
+ .build();
+ return HeadsUpManagerTestUtil.createEntry(id, notif);
+ }
}
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index 5fe74aa..2d63c8d 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -26,7 +26,7 @@
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:visibility="visible"
- app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toTopOf="@+id/topGuideline" />
@@ -59,7 +59,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:fillViewport="true"
- android:padding="16dp"
+ android:padding="24dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
@@ -82,20 +82,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
- <LinearLayout
- android:id="@+id/customized_view_container"
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:paddingHorizontal="0dp"
- android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
+ android:ellipsize="marquee"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:paddingLeft="8dp"
+ app:layout_constraintBottom_toBottomOf="@+id/logo"
app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="0.0"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/subtitle"
- app:layout_constraintVertical_bias="0.0" />
+ app:layout_constraintStart_toEndOf="@+id/logo"
+ app:layout_constraintTop_toTopOf="@+id/logo" />
<Space
android:id="@+id/space_above_content"
@@ -108,6 +108,7 @@
style="@style/TextAppearance.AuthCredential.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
android:gravity="@integer/biometric_dialog_text_gravity"
android:paddingHorizontal="0dp"
android:textAlignment="viewStart"
@@ -124,6 +125,7 @@
style="@style/TextAppearance.AuthCredential.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
android:gravity="@integer/biometric_dialog_text_gravity"
android:paddingHorizontal="0dp"
android:textAlignment="viewStart"
@@ -133,6 +135,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingHorizontal="0dp"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle"
+ app:layout_constraintVertical_bias="0.0" />
+
<TextView
android:id="@+id/description"
style="@style/TextAppearance.AuthCredential.Description"
@@ -148,20 +165,6 @@
app:layout_constraintTop_toBottomOf="@+id/subtitle"
app:layout_constraintVertical_bias="0.0" />
- <TextView
- android:id="@+id/logo_description"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:marqueeRepeatLimit="1"
- android:singleLine="true"
- android:textAlignment="viewStart"
- android:paddingLeft="8dp"
- app:layout_constraintBottom_toBottomOf="@+id/logo"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toEndOf="@+id/logo"
- app:layout_constraintTop_toTopOf="@+id/logo" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/contentBarrier"
@@ -178,7 +181,7 @@
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="24dp"
android:accessibilityLiveRegion="polite"
android:fadingEdge="horizontal"
android:gravity="center_horizontal"
@@ -192,80 +195,15 @@
app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
app:layout_constraintVertical_bias="0.0" />
- <!-- Negative Button, reserved for app -->
- <Button
- android:id="@+id/button_negative"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
- app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
- <!-- Cancel Button, replaces negative button when biometric is accepted -->
- <Button
- android:id="@+id/button_cancel"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:text="@string/cancel"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
- app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
- <!-- "Use Credential" Button, replaces if device credential is allowed -->
- <Button
- android:id="@+id/button_use_credential"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
- app:layout_constraintStart_toStartOf="@+id/scrollView" />
-
- <!-- Positive Button -->
- <Button
- android:id="@+id/button_confirm"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
- app:layout_constraintEnd_toEndOf="@+id/scrollView"
- tools:visibility="invisible" />
-
- <!-- Try Again Button -->
- <Button
- android:id="@+id/button_try_again"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/bottomGuideline"
- app:layout_constraintEnd_toEndOf="@+id/scrollView" />
+ <include
+ android:id="@+id/button_bar"
+ layout="@layout/biometric_prompt_button_bar"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+ app:layout_constraintEnd_toEndOf="@id/scrollView"
+ app:layout_constraintStart_toStartOf="@id/scrollView"
+ app:layout_constraintTop_toBottomOf="@id/scrollView" />
<!-- Guidelines for setting panel border -->
<androidx.constraintlayout.widget.Barrier
@@ -282,7 +220,7 @@
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="false"
app:barrierDirection="top"
- app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+ app:constraint_referenced_ids="button_bar" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/leftGuideline"
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 5b30dfb..329fc46 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -23,92 +23,29 @@
android:clickable="true"
android:clipToOutline="true"
android:importantForAccessibility="no"
- android:paddingHorizontal="16dp"
- android:paddingVertical="16dp"
android:visibility="visible"
- app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
app:layout_constraintStart_toStartOf="@+id/leftGuideline"
app:layout_constraintTop_toTopOf="@+id/topBarrier" />
- <Button
- android:id="@+id/button_negative"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <Button
- android:id="@+id/button_cancel"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:text="@string/cancel"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <Button
- android:id="@+id/button_use_credential"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <Button
- android:id="@+id/button_confirm"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- tools:visibility="invisible" />
-
- <Button
- android:id="@+id/button_try_again"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintEnd_toEndOf="@+id/panel" />
-
- <!-- Negative Button, reserved for app -->
+ <include
+ layout="@layout/biometric_prompt_button_bar"
+ android:id="@+id/button_bar"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+ app:layout_constraintEnd_toEndOf="@id/panel"
+ app:layout_constraintStart_toStartOf="@id/panel"/>
<ScrollView
android:id="@+id/scrollView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:fillViewport="true"
- android:padding="16dp"
+ android:paddingBottom="36dp"
+ android:paddingHorizontal="24dp"
+ android:paddingTop="24dp"
app:layout_constrainedHeight="true"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
@@ -134,18 +71,19 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
- <LinearLayout
- android:id="@+id/customized_view_container"
- android:layout_width="wrap_content"
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
- android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
+ android:ellipsize="marquee"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:marqueeRepeatLimit="1"
+ android:singleLine="true"
+ android:paddingTop="16dp"
+ app:layout_constraintBottom_toTopOf="@+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+ app:layout_constraintTop_toBottomOf="@+id/logo" />
<Space
android:id="@+id/space_above_content"
@@ -159,6 +97,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="16dp"
app:layout_constraintBottom_toTopOf="@+id/subtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -170,23 +109,24 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="16dp"
app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
- <TextView
- android:id="@+id/logo_description"
- android:layout_width="match_parent"
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:marqueeRepeatLimit="1"
- android:singleLine="true"
- app:layout_constraintBottom_toTopOf="@+id/title"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
+ android:visibility="gone"
+ app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/logo" />
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
<TextView
android:id="@+id/description"
@@ -215,7 +155,7 @@
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="24dp"
android:accessibilityLiveRegion="polite"
android:fadingEdge="horizontal"
android:gravity="center_horizontal"
@@ -247,7 +187,7 @@
android:layout_height="wrap_content"
app:barrierAllowsGoneWidgets="false"
app:barrierDirection="top"
- app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
+ app:constraint_referenced_ids="button_bar" />
<!-- Guidelines for setting panel border -->
<androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/layout/app_clips_screenshot.xml b/packages/SystemUI/res/layout/app_clips_screenshot.xml
index cb638ee..bcc7bca 100644
--- a/packages/SystemUI/res/layout/app_clips_screenshot.xml
+++ b/packages/SystemUI/res/layout/app_clips_screenshot.xml
@@ -69,7 +69,7 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
- <com.android.systemui.screenshot.CropView
+ <com.android.systemui.screenshot.scroll.CropView
android:id="@+id/crop_view"
android:layout_width="0px"
android:layout_height="0px"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
new file mode 100644
index 0000000..810c7433
--- /dev/null
+++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <!-- Negative Button, reserved for app -->
+ <Button
+ android:id="@+id/button_negative"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="24dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <!-- Cancel Button, replaces negative button when biometric is accepted -->
+ <Button
+ android:id="@+id/button_cancel"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="24dp"
+ android:text="@string/cancel"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <!-- "Use Credential" Button, replaces if device credential is allowed -->
+ <Button
+ android:id="@+id/button_use_credential"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="24dp"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent" />
+
+ <!-- Positive Button -->
+ <Button
+ android:id="@+id/button_confirm"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="24dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_confirm"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ <!-- Try Again Button -->
+ <Button
+ android:id="@+id/button_try_again"
+ style="@*android:style/Widget.DeviceDefault.Button.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="24dp"
+ android:ellipsize="end"
+ android:maxLines="2"
+ android:text="@string/biometric_dialog_try_again"
+ android:visibility="invisible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index 74292b4..6391813 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -23,13 +23,189 @@
android:clickable="true"
android:clipToOutline="true"
android:importantForAccessibility="no"
- android:paddingHorizontal="16dp"
- android:paddingVertical="16dp"
android:visibility="visible"
- app:layout_constraintBottom_toTopOf="@+id/bottomGuideline"
- app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
- app:layout_constraintStart_toStartOf="@+id/leftGuideline"
- app:layout_constraintTop_toTopOf="@+id/topBarrier" />
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+ app:layout_constraintStart_toStartOf="@id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@id/topBarrier" />
+
+ <include
+ android:id="@+id/button_bar"
+ layout="@layout/biometric_prompt_button_bar"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ app:layout_constraintBottom_toTopOf="@id/bottomGuideline"
+ app:layout_constraintEnd_toEndOf="@id/panel"
+ app:layout_constraintStart_toStartOf="@id/panel" />
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:fillViewport="true"
+ android:paddingBottom="36dp"
+ android:paddingHorizontal="24dp"
+ android:paddingTop="24dp"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
+ app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+ app:layout_constraintStart_toStartOf="@id/leftGuideline"
+ app:layout_constraintTop_toTopOf="@+id/topGuideline"
+ app:layout_constraintVertical_bias="1.0">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/innerConstraint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ImageView
+ android:id="@+id/logo"
+ android:layout_width="@dimen/biometric_auth_icon_size"
+ android:layout_height="@dimen/biometric_auth_icon_size"
+ android:layout_gravity="center"
+ android:scaleType="fitXY"
+ app:layout_constraintBottom_toTopOf="@+id/logo_description"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:visibility="visible" />
+
+ <TextView
+ android:id="@+id/logo_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:singleLine="true"
+ app:layout_constraintBottom_toTopOf="@+id/title"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo" />
+
+ <Space
+ android:id="@+id/space_above_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/biometric_prompt_space_above_content"
+ android:visibility="gone" />
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="16dp"
+ app:layout_constraintBottom_toTopOf="@+id/subtitle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/logo_description" />
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="16dp"
+ app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/title" />
+
+ <LinearLayout
+ android:id="@+id/customized_view_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:visibility="gone"
+ android:paddingTop="8dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
+ android:paddingTop="16dp"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@+id/subtitle" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/contentBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="description, customized_view_container" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </ScrollView>
+
+ <TextView
+ android:id="@+id/indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:accessibilityLiveRegion="polite"
+ android:fadingEdge="horizontal"
+ android:gravity="center_horizontal"
+ android:scrollHorizontally="true"
+ app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
+ app:layout_constraintEnd_toEndOf="@+id/panel"
+ app:layout_constraintStart_toStartOf="@+id/panel"
+ app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
+ app:layout_constraintVertical_bias="0.0" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/topBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="scrollView" />
+
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/buttonBarrier"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierAllowsGoneWidgets="false"
+ app:barrierDirection="top"
+ app:constraint_referenced_ids="button_bar" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/leftGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_begin="0dp" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/rightGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ app:layout_constraintGuide_end="0dp" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/bottomGuideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/topGuideline"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.25" />
<com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
android:id="@+id/biometric_icon"
@@ -54,249 +230,4 @@
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
- <ScrollView
- android:id="@+id/scrollView"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:fillViewport="true"
- android:padding="16dp"
- app:layout_constrainedHeight="true"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toTopOf="@+id/biometric_icon"
- app:layout_constraintEnd_toEndOf="@id/rightGuideline"
- app:layout_constraintStart_toStartOf="@id/leftGuideline"
- app:layout_constraintTop_toTopOf="@+id/topGuideline"
- app:layout_constraintVertical_bias="1.0">
-
- <androidx.constraintlayout.widget.ConstraintLayout
- android:id="@+id/innerConstraint"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <ImageView
- android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
- android:layout_gravity="center"
- android:scaleType="fitXY"
- android:visibility="visible"
- app:layout_constraintBottom_toTopOf="@+id/logo_description"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
-
- <LinearLayout
- android:id="@+id/customized_view_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
- android:visibility="gone"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
- android:visibility="gone" />
-
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- app:layout_constraintBottom_toTopOf="@+id/subtitle"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/logo_description" />
-
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/title" />
-
- <TextView
- android:id="@+id/logo_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:marqueeRepeatLimit="1"
- android:singleLine="true"
- app:layout_constraintBottom_toTopOf="@+id/title"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/logo" />
-
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/contentBarrier"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierAllowsGoneWidgets="false"
- app:barrierDirection="top"
- app:constraint_referenced_ids="description, customized_view_container" />
-
- </androidx.constraintlayout.widget.ConstraintLayout>
- </ScrollView>
-
- <TextView
- android:id="@+id/indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:accessibilityLiveRegion="polite"
- android:fadingEdge="horizontal"
- android:gravity="center_horizontal"
- android:marqueeRepeatLimit="marquee_forever"
- android:scrollHorizontally="true"
- android:textColor="@color/biometric_dialog_gray"
- android:textSize="12sp"
- app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel"
- app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
- app:layout_constraintVertical_bias="0.0" />
-
- <!-- Negative Button, reserved for app -->
- <Button
- android:id="@+id/button_negative"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <!-- Cancel Button, replaces negative button when biometric is accepted -->
- <Button
- android:id="@+id/button_cancel"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:text="@string/cancel"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <!-- "Use Credential" Button, replaces if device credential is allowed -->
- <Button
- android:id="@+id/button_use_credential"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintStart_toStartOf="@+id/panel" />
-
- <!-- Positive Button -->
- <Button
- android:id="@+id/button_confirm"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_confirm"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintEnd_toEndOf="@+id/panel"
- tools:visibility="invisible" />
-
- <!-- Try Again Button -->
- <Button
- android:id="@+id/button_try_again"
- style="@*android:style/Widget.DeviceDefault.Button.Colored"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginRight="8dp"
- android:ellipsize="end"
- android:maxLines="2"
- android:text="@string/biometric_dialog_try_again"
- android:visibility="invisible"
- app:layout_constraintBottom_toBottomOf="@+id/panel"
- app:layout_constraintEnd_toEndOf="@+id/panel" />
-
- <!-- Guidelines for setting panel border -->
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/topBarrier"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierAllowsGoneWidgets="false"
- app:barrierDirection="top"
- app:constraint_referenced_ids="scrollView" />
-
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/buttonBarrier"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierAllowsGoneWidgets="false"
- app:barrierDirection="top"
- app:constraint_referenced_ids="button_negative, button_cancel, button_use_credential, button_confirm, button_try_again" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/leftGuideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/rightGuideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/bottomGuideline"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
-
- <androidx.constraintlayout.widget.Guideline
- android:id="@+id/topGuideline"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:orientation="horizontal"
- app:layout_constraintGuide_percent="0.25171" />
-
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8a19c2e..4d207da 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -100,7 +100,7 @@
app:layout_constraintStart_toStartOf="parent"
android:transitionName="screenshot_preview_image"/>
- <com.android.systemui.screenshot.CropView
+ <com.android.systemui.screenshot.scroll.CropView
android:id="@+id/crop_view"
android:layout_width="0px"
android:layout_height="0px"
@@ -122,7 +122,7 @@
tools:minHeight="100dp"
tools:minWidth="100dp" />
- <com.android.systemui.screenshot.MagnifierView
+ <com.android.systemui.screenshot.scroll.MagnifierView
android:id="@+id/magnifier"
android:visibility="invisible"
android:layout_width="200dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b7eff38..2285550 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -898,8 +898,9 @@
<dimen name="communal_tutorial_indicator_horizontal_offset">32dp</dimen>
<!-- Size of the maximum radius for the enforced rounded rectangles on communal hub.
- Keep it the same as in Launcher-->
- <dimen name="communal_enforced_rounded_corner_max_radius">16dp</dimen>
+ Larger than the 16dp Launcher uses, to ensure consistency on the hub, where it's much more
+ obvious when corner radii differ.-->
+ <dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
<!-- Width and height used to filter widgets displayed in the communal widget picker -->
<dimen name="communal_widget_picker_desired_width">424dp</dimen>
@@ -1087,7 +1088,7 @@
<dimen name="biometric_dialog_fingerprint_icon_height">80dp</dimen>
<dimen name="biometric_dialog_button_negative_max_width">160dp</dimen>
<dimen name="biometric_dialog_button_positive_max_width">136dp</dimen>
- <dimen name="biometric_dialog_corner_size">4dp</dimen>
+ <dimen name="biometric_dialog_corner_size">28dp</dimen>
<!-- Y translation when showing/dismissing the dialog-->
<dimen name="biometric_dialog_animation_translation_offset">350dp</dimen>
<dimen name="biometric_dialog_border_padding">4dp</dimen>
@@ -1967,4 +1968,7 @@
<!-- Microns/ums (1000 um = 1mm) per pixel for the given device. If unspecified, UI that
relies on this value will not be sized correctly. -->
<item name="pixel_pitch" format="float" type="dimen">-1</item>
+
+ <!-- SliceView grid gutter for ANC Slice -->
+ <dimen name="abc_slice_grid_gutter">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e3a5e15..774bbe5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3358,7 +3358,7 @@
<string name="microphone_blocked_dream_overlay_content_description">Microphone blocked</string>
<!-- Content description for priority mode icon on dream [CHAR LIMIT=NONE] -->
- <string name="priority_mode_dream_overlay_content_description">Priority mode on</string>
+ <string name="priority_mode_dream_overlay_content_description">Do not disturb</string>
<!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
<string name="assistant_attention_content_description">User presence is detected</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 69aa909..c08b083 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -77,7 +77,7 @@
// settings is expanded.
public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
// Winscope tracing is enabled
- public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12;
+ public static final int SYSUI_STATE_TRACING_ENABLED = 1 << 12;
// The Assistant gesture should be constrained. It is up to the launcher implementation to
// decide how to constrain it
public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
@@ -148,7 +148,7 @@
SYSUI_STATE_OVERVIEW_DISABLED,
SYSUI_STATE_HOME_DISABLED,
SYSUI_STATE_SEARCH_DISABLED,
- SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION,
+ SYSUI_STATE_TRACING_ENABLED,
SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED,
SYSUI_STATE_BUBBLES_EXPANDED,
SYSUI_STATE_DIALOG_SHOWING,
@@ -211,8 +211,8 @@
if ((flags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0) {
str.add("a11y_long_click");
}
- if ((flags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) != 0) {
- str.add("disable_gesture_split_invocation");
+ if ((flags & SYSUI_STATE_TRACING_ENABLED) != 0) {
+ str.add("tracing");
}
if ((flags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0) {
str.add("asst_gesture_constrain");
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 0bb5c17..fb331b6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -15,8 +15,10 @@
*/
package com.android.keyguard;
+import android.annotation.NonNull;
import android.app.Presentation;
import android.content.Context;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.DisplayManager;
import android.media.MediaRouter;
@@ -315,11 +317,11 @@
}
@Override
- public void onStateChanged(int state) {
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
// When concurrent state ends, the display also turns off. This is enforced in various
// ExtensionRearDisplayPresentationTest CTS tests. So, we don't need to invoke
// hide() since that will happen through the onDisplayRemoved callback.
- mIsInConcurrentDisplayState = state == mConcurrentState;
+ mIsInConcurrentDisplayState = state.getIdentifier() == mConcurrentState;
}
boolean isConcurrentDisplayActive(Display display) {
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 8e98150..039a2e5 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -23,6 +23,7 @@
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
import static com.android.systemui.Flags.keyguardBottomAreaRefactor;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
@@ -452,7 +453,7 @@
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (keyguardBottomAreaRefactor()) {
+ if (keyguardBottomAreaRefactor() || migrateClocksToBlueprint()) {
mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
scaledPadding);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index da49201..c6716e4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -54,7 +54,7 @@
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
@@ -190,7 +190,7 @@
private final NotificationShadeWindowController mNotificationShadeController;
private final KeyguardStateController mKeyguardStateController;
private final ShadeController mShadeController;
- private final Lazy<ShadeViewController> mShadeViewController;
+ private final Lazy<PanelExpansionInteractor> mPanelExpansionInteractor;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@@ -200,14 +200,14 @@
NotificationShadeWindowController notificationShadeController,
KeyguardStateController keyguardStateController,
ShadeController shadeController,
- Lazy<ShadeViewController> shadeViewController,
+ Lazy<PanelExpansionInteractor> panelExpansionInteractor,
Optional<Recents> recentsOptional,
DisplayTracker displayTracker) {
mContext = context;
mUserTracker = userTracker;
mKeyguardStateController = keyguardStateController;
mShadeController = shadeController;
- mShadeViewController = shadeViewController;
+ mPanelExpansionInteractor = panelExpansionInteractor;
mRecentsOptional = recentsOptional;
mDisplayTracker = displayTracker;
mReceiver = new SystemActionsBroadcastReceiver();
@@ -330,7 +330,7 @@
private void registerOrUnregisterDismissNotificationShadeAction() {
Assert.isMainThread();
- if (mShadeViewController.get().isPanelExpanded()
+ if (mPanelExpansionInteractor.get().isPanelExpanded()
&& !mKeyguardStateController.isShowing()) {
if (!mDismissNotificationShadeActionRegistered) {
mA11yManager.registerSystemAction(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
index 9dd1454..0f4d63c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -302,6 +302,14 @@
if (mEndAnimationCanceled || mController == null) {
return;
}
+
+ // If the animation is playing backwards, mStartSpec will be the final spec we would
+ // like to reach.
+ AnimationSpec spec = isReverse ? mStartSpec : mEndSpec;
+ mController.enableWindowMagnificationInternal(
+ spec.mScale, spec.mCenterX, spec.mCenterY,
+ mMagnificationFrameOffsetRatioX, mMagnificationFrameOffsetRatioY);
+
if (mState == STATE_DISABLING) {
mController.deleteWindowMagnification();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 8bd675c..99cdc01 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -340,13 +340,19 @@
mPanelInteractionDetector = panelInteractionDetector;
mApplicationCoroutineScope = applicationCoroutineScope;
+ mPromptViewModel = promptViewModel;
mTranslationY = getResources()
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
+ mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
+ mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+ config.mPromptInfo);
+
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
- if (constraintBp()) {
+ if (constraintBp() && (Utils.isBiometricAllowed(config.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue())) {
mLayout = (ConstraintLayout) layoutInflater.inflate(
R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */);
} else {
@@ -375,9 +381,7 @@
mBackgroundExecutor = bgExecutor;
mInteractionJankMonitor = jankMonitor;
mPromptCredentialInteractor = credentialInteractor;
- mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
mCredentialViewModelProvider = credentialViewModelProvider;
- mPromptViewModel = promptViewModel;
mFpProps = fpProps;
mFaceProps = faceProps;
@@ -408,10 +412,6 @@
@Nullable FaceSensorPropertiesInternal faceProps,
@NonNull VibratorHelper vibratorHelper
) {
- // Set this value before showing either of the prompt.
- mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
- config.mPromptInfo);
-
if (Utils.isBiometricAllowed(config.mPromptInfo)
|| mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 478ef8f..1dfd2e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -23,6 +23,7 @@
import android.graphics.Rect
import android.transition.AutoTransition
import android.transition.TransitionManager
+import android.util.TypedValue
import android.view.Surface
import android.view.View
import android.view.ViewGroup
@@ -54,13 +55,10 @@
import com.android.systemui.biometrics.ui.viewmodel.isRight
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
-import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
-import kotlin.math.abs
import kotlin.math.min
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -106,6 +104,13 @@
val iconHolderView = view.requireViewById<View>(R.id.biometric_icon)
val panelView = view.requireViewById<View>(R.id.panel)
val cornerRadius = view.resources.getDimension(R.dimen.biometric_dialog_corner_size)
+ val cornerRadiusPx =
+ TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ cornerRadius,
+ view.resources.displayMetrics
+ )
+ .toInt()
// ConstraintSets for animating between prompt sizes
val mediumConstraintSet = ConstraintSet()
@@ -116,9 +121,10 @@
val largeConstraintSet = ConstraintSet()
largeConstraintSet.clone(mediumConstraintSet)
- largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
- largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
- largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
// TODO: Investigate better way to handle 180 rotations
val flipConstraintSet = ConstraintSet()
@@ -141,7 +147,13 @@
panelView.outlineProvider =
object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
- outline.setRoundRect(0, 0, view.width, view.height, cornerRadius)
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height + cornerRadiusPx,
+ cornerRadiusPx.toFloat()
+ )
}
}
@@ -164,35 +176,81 @@
when {
position.isTop -> {
+ // Round bottom corners
+ panelView.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ 0,
+ -cornerRadiusPx,
+ view.width,
+ view.height,
+ cornerRadiusPx.toFloat()
+ )
+ }
+ }
left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
bottom = iconHolderView.centerY() * 2 - iconHolderView.centerY() / 4
}
position.isBottom -> {
- left = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- right = windowBounds.centerX() - width / 2 + viewModel.promptMargin
- bottom = viewModel.promptMargin
+ // Round top corners
+ panelView.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width,
+ view.height + cornerRadiusPx,
+ cornerRadiusPx.toFloat()
+ )
+ }
+ }
+
+ left = windowBounds.centerX() - width / 2
+ right = windowBounds.centerX() - width / 2
+ bottom = if (view.isLandscape()) bottomInset else 0
}
position.isLeft -> {
- left = viewModel.promptMargin
- mid =
- abs(
- windowBounds.width() - iconHolderView.centerX() * 2 +
- viewModel.promptMargin
- )
+ // Round right corners
+ panelView.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ -cornerRadiusPx,
+ 0,
+ view.width,
+ view.height,
+ cornerRadiusPx.toFloat()
+ )
+ }
+ }
+
+ left = 0
+ mid = (windowBounds.width() * .85).toInt() / 2
right = windowBounds.width() - (windowBounds.width() * .85).toInt()
- bottom = bottomInset + viewModel.promptMargin
+ bottom = if (view.isLandscape()) bottomInset else 0
}
position.isRight -> {
+ // Round left corners
+ panelView.outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ outline.setRoundRect(
+ 0,
+ 0,
+ view.width + cornerRadiusPx,
+ view.height,
+ cornerRadiusPx.toFloat()
+ )
+ }
+ }
+
left = windowBounds.width() - (windowBounds.width() * .85).toInt()
- right = viewModel.promptMargin
- bottom = bottomInset + viewModel.promptMargin
- mid =
- abs(
- iconHolderView.centerX() -
- (windowBounds.width() - iconHolderView.centerX()) -
- viewModel.promptMargin
- )
+ right = 0
+ bottom = if (view.isLandscape()) bottomInset else 0
+ mid = windowBounds.width() - (windowBounds.width() * .85).toInt() / 2
}
}
@@ -226,16 +284,8 @@
}
}
- fun setConstraintSetVisibility() {
- viewsToHideWhenSmall.forEach {
- mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
- largeConstraintSet.setVisibility(it.id, View.GONE)
- smallConstraintSet.setVisibility(it.id, View.GONE)
- }
-
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ fun setVisibilities(size: PromptSize) {
+ viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
if (viewModel.showBpWithoutIconForCredential.value) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
@@ -261,7 +311,7 @@
}
measureBounds(position)
- setConstraintSetVisibility()
+ setVisibilities(size)
when {
size.isSmall -> {
val ratio =
@@ -353,7 +403,7 @@
// prepare for animated size transitions
for (v in viewsToHideWhenSmall) {
- v.visibility = v.showContentOrHide(forceHide = size.isSmall)
+ v.showContentOrHide(forceHide = size.isSmall)
}
if (viewModel.showBpWithoutIconForCredential.value) {
@@ -490,14 +540,15 @@
}
}
-private fun View.showContentOrHide(forceHide: Boolean = false): Int {
+private fun View.showContentOrHide(forceHide: Boolean = false) {
val isTextViewWithBlankText = this is TextView && this.text.isBlank()
val isImageViewWithoutImage = this is ImageView && this.drawable == null
- return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ visibility =
+ if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
}
private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index e457601..d8265c7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -63,12 +63,6 @@
iconOverlayView.layoutParams.width = iconViewLayoutParamSizeOverride.first
iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
- } else {
- iconView.layoutParams.width = viewModel.fingerprintIconWidth.first()
- iconView.layoutParams.height = viewModel.fingerprintIconWidth.first()
-
- iconOverlayView.layoutParams.width = viewModel.fingerprintIconWidth.first()
- iconOverlayView.layoutParams.height = viewModel.fingerprintIconWidth.first()
}
var faceIcon: AnimatedVectorDrawable? = null
@@ -84,10 +78,8 @@
}
launch {
- var width = 0
- var height = 0
- combine(promptViewModel.size, viewModel.activeAuthType, ::Pair).collect {
- (_, activeAuthType) ->
+ combine(viewModel.activeAuthType, viewModel.iconSize, ::Pair).collect {
+ (activeAuthType, iconSize) ->
// Every time after bp shows, [isIconViewLoaded] is set to false in
// [BiometricViewSizeBinder]. Then when biometric prompt view is redrew
// (when size or activeAuthType changes), we need to update
@@ -109,8 +101,6 @@
}
}
AuthType.Face -> {
- width = viewModel.faceIconWidth
- height = viewModel.faceIconHeight
/**
* Set to true by default since face icon is a drawable, which
* doesn't have a LottieOnCompositionLoadedListener equivalent.
@@ -122,11 +112,12 @@
}
}
- if (width != 0 && height != 0) {
- iconView.layoutParams.width = width
- iconView.layoutParams.height = height
- iconOverlayView.layoutParams.width = width
- iconOverlayView.layoutParams.height = height
+ if (iconViewLayoutParamSizeOverride == null) {
+ iconView.layoutParams.width = iconSize.first
+ iconView.layoutParams.height = iconSize.second
+
+ iconOverlayView.layoutParams.width = iconSize.first
+ iconOverlayView.layoutParams.height = iconSize.second
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 257eb4a..d0c140b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -21,6 +21,7 @@
import android.annotation.RawRes
import android.content.res.Configuration
import android.graphics.Rect
+import android.hardware.face.Face
import android.util.RotationUtils
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -65,9 +66,10 @@
*/
val activeAuthType: Flow<AuthType> =
combine(
+ promptViewModel.size,
promptViewModel.modalities.distinctUntilChanged(),
promptViewModel.faceMode.distinctUntilChanged()
- ) { modalities, faceMode ->
+ ) { _, modalities, faceMode ->
if (modalities.hasFaceAndFingerprint && !faceMode) {
AuthType.Coex
} else if (modalities.hasFaceOnly || faceMode) {
@@ -158,13 +160,18 @@
/** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */
val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()
- /** Layout params for fingerprint iconView */
- val fingerprintIconWidth: Flow<Int> = promptViewModel.fingerprintSensorDiameter
- val fingerprintIconHeight: Flow<Int> = promptViewModel.fingerprintSensorDiameter
-
- /** Layout params for face iconView */
- val faceIconWidth: Int = promptViewModel.faceIconWidth
- val faceIconHeight: Int = promptViewModel.faceIconHeight
+ val iconSize: Flow<Pair<Int, Int>> =
+ combine(
+ activeAuthType,
+ promptViewModel.fingerprintSensorWidth,
+ promptViewModel.fingerprintSensorHeight,
+ ) { activeAuthType, fingerprintSensorWidth, fingerprintSensorHeight ->
+ if (activeAuthType == AuthType.Face) {
+ Pair(promptViewModel.faceIconWidth, promptViewModel.faceIconHeight)
+ } else {
+ Pair(fingerprintSensorWidth, fingerprintSensorHeight)
+ }
+ }
/** Current BiometricPromptLayout.iconView asset. */
val iconAsset: Flow<Int> =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 86b0b44..fbd87fd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -86,7 +86,7 @@
val faceIconHeight: Int =
context.resources.getDimensionPixelSize(R.dimen.biometric_dialog_face_icon_size)
- val fingerprintSensorDiameter: Flow<Int> =
+ val fingerprintSensorWidth: Flow<Int> =
combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
->
if (modalities.hasUdfps) {
@@ -96,6 +96,16 @@
}
}
+ val fingerprintSensorHeight: Flow<Int> =
+ combine(modalities, udfpsOverlayInteractor.udfpsOverlayParams) { modalities, overlayParams
+ ->
+ if (modalities.hasUdfps) {
+ overlayParams.sensorBounds.height()
+ } else {
+ fingerprintIconHeight
+ }
+ }
+
private val _accessibilityHint = MutableSharedFlow<String>()
/** Hint for talkback directional guidance */
@@ -342,7 +352,7 @@
position,
message,
) { size, _, message ->
- size.isNotSmall && message.message.isNotBlank()
+ size.isMedium && message.message.isNotBlank()
}
/** If the auth is pending confirmation and the confirm button should be shown. */
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 9afd5ed..d2df276 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -24,9 +24,9 @@
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
-import com.android.systemui.screenshot.LongScreenshotActivity;
import com.android.systemui.screenshot.appclips.AppClipsActivity;
import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
import com.android.systemui.settings.brightness.BrightnessDialog;
import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
index 3d8e4cb..6aa5e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java
@@ -22,8 +22,6 @@
import com.android.systemui.media.dialog.MediaOutputDialogReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
-import com.android.systemui.screenshot.ActionProxyReceiver;
-import com.android.systemui.screenshot.DeleteScreenshotReceiver;
import com.android.systemui.screenshot.SmartActionsReceiver;
import com.android.systemui.volume.VolumePanelDialogReceiver;
@@ -42,24 +40,6 @@
*/
@Binds
@IntoMap
- @ClassKey(ActionProxyReceiver.class)
- public abstract BroadcastReceiver bindActionProxyReceiver(
- ActionProxyReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
- @ClassKey(DeleteScreenshotReceiver.class)
- public abstract BroadcastReceiver bindDeleteScreenshotReceiver(
- DeleteScreenshotReceiver broadcastReceiver);
-
- /**
- *
- */
- @Binds
- @IntoMap
@ClassKey(SmartActionsReceiver.class)
public abstract BroadcastReceiver bindSmartActionsReceiver(
SmartActionsReceiver broadcastReceiver);
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
index 83337f7..fa7603f 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DeviceStateRepository.kt
@@ -62,7 +62,7 @@
conflatedCallbackFlow {
val callback =
DeviceStateManager.DeviceStateCallback { state ->
- trySend(deviceStateToPosture(state))
+ trySend(deviceStateToPosture(state.identifier))
}
deviceStateManager.registerCallback(executor, callback)
awaitClose { deviceStateManager.unregisterCallback(callback) }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 43a8b40..3b34750 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -40,6 +40,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.refactorGetCurrentUser;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -3401,6 +3402,11 @@
mSurfaceBehindRemoteAnimationFinishedCallback = null;
}
}
+
+ // Ensure that keyguard becomes visible if the going away animation is canceled
+ if (showKeyguard && !KeyguardWmStateRefactor.isEnabled() && migrateClocksToBlueprint()) {
+ mKeyguardInteractor.showKeyguard();
+ }
}
private void adjustStatusBarLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 88ddfd4..47f8046a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -33,7 +33,11 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@SysUISingleton
@@ -66,6 +70,25 @@
listenForTransitionToCamera(scope, keyguardInteractor)
}
+ val surfaceBehindVisibility: Flow<Boolean?> =
+ combine(
+ transitionInteractor.startedKeyguardTransitionStep,
+ transitionInteractor.transitionStepsFromState(KeyguardState.ALTERNATE_BOUNCER)
+ ) { startedStep, fromBouncerStep ->
+ if (startedStep.to != KeyguardState.GONE) {
+ return@combine null
+ }
+
+ // The alt bouncer is pretty fast to hide, so start the surface behind animation
+ // around 30%.
+ fromBouncerStep.value > 0.3f
+ }
+ .onStart {
+ // Default to null ("don't care, use a reasonable default").
+ emit(null)
+ }
+ .distinctUntilChanged()
+
private fun listenForAlternateBouncerToLockscreenHubAodOrDozing() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index d5a9bd1..4a3232e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -71,6 +71,10 @@
listenForGoneToDreamingLockscreenHosted()
}
+ fun showKeyguard() {
+ scope.launch { startTransitionTo(KeyguardState.LOCKSCREEN) }
+ }
+
// Primarily for when the user chooses to lock down the device
private fun listenForGoneToLockscreenOrHub() {
if (KeyguardWmStateRefactor.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 391dccc..c7fafba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -26,7 +26,6 @@
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
@@ -96,36 +95,6 @@
}
.distinctUntilChanged()
- val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
- combine(
- transitionInteractor.startedKeyguardTransitionStep,
- transitionInteractor.transitionStepsFromState(KeyguardState.PRIMARY_BOUNCER)
- ) { startedStep, fromBouncerStep ->
- if (startedStep.to != KeyguardState.GONE) {
- // BOUNCER to anything but GONE does not require any special surface
- // visibility handling.
- return@combine null
- }
-
- if (fromBouncerStep.value > 0.5f) {
- KeyguardSurfaceBehindModel(
- animateFromAlpha = 0f,
- alpha = 1f,
- animateFromTranslationY = 500f,
- translationY = 0f,
- )
- } else {
- KeyguardSurfaceBehindModel(
- alpha = 0f,
- )
- }
- }
- .onStart {
- // Default to null ("don't care, use a reasonable default").
- emit(null)
- }
- .distinctUntilChanged()
-
fun dismissPrimaryBouncer() {
scope.launch { startTransitionTo(KeyguardState.GONE) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index c877192..d39bd3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -92,7 +92,7 @@
when {
useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
- useSplitShade -> SplitShadeKeyguardBlueprint.ID
+ useSplitShade && !ComposeLockscreen.isEnabled -> SplitShadeKeyguardBlueprint.ID
else -> DefaultKeyguardBlueprint.DEFAULT
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7734973..283f160 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -83,6 +83,7 @@
shadeRepository: ShadeRepository,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
sceneInteractorProvider: Provider<SceneInteractor>,
+ private val fromGoneTransitionInteractor: Provider<FromGoneTransitionInteractor>,
) {
// TODO(b/296118689): move to a repository
private val _sharedNotificationContainerBounds = MutableStateFlow(NotificationContainerBounds())
@@ -383,6 +384,11 @@
repository.topClippingBounds.value = top
}
+ /** Temporary shim, until [KeyguardWmStateRefactor] is enabled */
+ fun showKeyguard() {
+ fromGoneTransitionInteractor.get().showKeyguard()
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
index c496a6e..80e94a2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt
@@ -95,7 +95,17 @@
}
.distinctUntilChanged()
- val isAnimatingSurface = repository.isAnimatingSurface
+ /**
+ * Whether we're animating the surface, or a notification launch animation is running (which
+ * means we're going to animate the surface, even if animators aren't yet running).
+ */
+ val isAnimatingSurface =
+ combine(
+ repository.isAnimatingSurface,
+ notificationLaunchInteractor.isLaunchAnimationRunning
+ ) { animatingSurface, animatingLaunch ->
+ animatingSurface || animatingLaunch
+ }
fun setAnimatingSurface(animating: Boolean) {
repository.setAnimatingSurface(animating)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index cff74b3..8d02e0e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -40,6 +40,7 @@
surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
fromLockscreenInteractor: FromLockscreenTransitionInteractor,
fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
+ fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
) {
private val defaultSurfaceBehindVisibility =
@@ -65,6 +66,9 @@
KeyguardState.PRIMARY_BOUNCER -> {
fromBouncerInteractor.surfaceBehindVisibility
}
+ KeyguardState.ALTERNATE_BOUNCER -> {
+ fromAlternateBouncerInteractor.surfaceBehindVisibility
+ }
else -> flowOf(null)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 1085f94..8fd8bec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -29,6 +29,7 @@
import com.android.keyguard.LockIconView
import com.android.keyguard.LockIconViewController
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
@@ -70,7 +71,11 @@
private val deviceEntryIconViewId = R.id.device_entry_icon_view
override fun addViews(constraintLayout: ConstraintLayout) {
- if (!keyguardBottomAreaRefactor() && !DeviceEntryUdfpsRefactor.isEnabled) {
+ if (
+ !keyguardBottomAreaRefactor() &&
+ !migrateClocksToBlueprint() &&
+ !DeviceEntryUdfpsRefactor.isEnabled
+ ) {
return
}
@@ -82,7 +87,7 @@
if (DeviceEntryUdfpsRefactor.isEnabled) {
DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
} else {
- // keyguardBottomAreaRefactor()
+ // keyguardBottomAreaRefactor() or migrateClocksToBlueprint()
LockIconView(context, null).apply { id = R.id.lock_icon_view }
}
constraintLayout.addView(view)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 58c45c7..b6622e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -28,8 +28,9 @@
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Utils
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -46,8 +47,8 @@
keyguardInteractor: KeyguardInteractor,
private val keyguardClockInteractor: KeyguardClockInteractor,
@Application private val applicationScope: CoroutineScope,
- private val splitShadeStateController: SplitShadeStateController,
notifsKeyguardInteractor: NotificationsKeyguardInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
var burnInLayer: Layer? = null
val useLargeClock: Boolean
@@ -111,12 +112,33 @@
initialValue = false
)
+ val currentClockLayout: StateFlow<ClockLayout> =
+ combine(isLargeClockVisible, clockShouldBeCentered, shadeInteractor.shadeMode) {
+ isLargeClockVisible,
+ clockShouldBeCentered,
+ shadeMode ->
+ val shouldUseSplitShade = shadeMode == ShadeMode.Split
+ when {
+ shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = ClockLayout.SMALL_CLOCK
+ )
+
/** Calculates the top margin for the small clock. */
fun getSmallClockTopMargin(context: Context): Int {
var topMargin: Int
val statusBarHeight = Utils.getStatusBarHeaderHeightKeyguard(context)
- if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
+ if (shadeInteractor.shadeMode.value == ShadeMode.Split) {
topMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
if (ComposeLockscreen.isEnabled) {
@@ -130,4 +152,11 @@
}
return topMargin
}
+
+ enum class ClockLayout {
+ LARGE_CLOCK,
+ SMALL_CLOCK,
+ SPLIT_SHADE_LARGE_CLOCK,
+ SPLIT_SHADE_SMALL_CLOCK,
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 5ca9215..41cc1d6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -56,6 +56,7 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -125,13 +126,36 @@
.onStart { emit(false) }
.distinctUntilChanged()
- private val alphaOnShadeExpansion: Flow<Float> =
+ private val isOnLockscreen: Flow<Boolean> =
combine(
+ keyguardTransitionInteractor.isFinishedInState(LOCKSCREEN).onStart { emit(false) },
+ keyguardTransitionInteractor
+ .isInTransitionWhere { from, to -> from == LOCKSCREEN || to == LOCKSCREEN }
+ .onStart { emit(false) }
+ ) { onLockscreen, transitioningToOrFromLockscreen ->
+ onLockscreen || transitioningToOrFromLockscreen
+ }
+ .distinctUntilChanged()
+
+ private val alphaOnShadeExpansion: Flow<Float> =
+ combineTransform(
+ isOnLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.shadeExpansion,
- ) { qsExpansion, shadeExpansion ->
+ ) { isOnLockscreen, qsExpansion, shadeExpansion ->
// Fade out quickly as the shade expands
- 1f - MathUtils.constrainedMap(0f, 1f, 0f, 0.2f, max(qsExpansion, shadeExpansion))
+ if (isOnLockscreen) {
+ val alpha =
+ 1f -
+ MathUtils.constrainedMap(
+ /* rangeMin = */ 0f,
+ /* rangeMax = */ 1f,
+ /* valueMin = */ 0f,
+ /* valueMax = */ 0.2f,
+ /* value = */ max(qsExpansion, shadeExpansion)
+ )
+ emit(alpha)
+ }
}
.distinctUntilChanged()
@@ -235,11 +259,7 @@
burnInJob?.cancel()
burnInJob =
- scope.launch {
- aodBurnInViewModel.movement(params).collect {
- burnInModel.value = it
- }
- }
+ scope.launch { aodBurnInViewModel.movement(params).collect { burnInModel.value = it } }
}
val scale: Flow<BurnInScaleViewModel> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 23320be..1f80441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,7 +23,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -40,7 +41,7 @@
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
val longPress: KeyguardLongPressViewModel,
- val splitShadeStateController: SplitShadeStateController,
+ val shadeInteractor: ShadeInteractor,
) {
private val clockSize = clockInteractor.clockSize
@@ -48,10 +49,12 @@
get() = authController.isUdfpsSupported
val isLargeClockVisible: Boolean
get() = clockSize.value == KeyguardClockSwitch.LARGE
- fun areNotificationsVisible(resources: Resources): Boolean {
- return !isLargeClockVisible ||
- splitShadeStateController.shouldUseSplitNotificationShade(resources)
- }
+
+ val areNotificationsVisible: Boolean
+ get() = !isLargeClockVisible || shouldUseSplitNotificationShade
+
+ val shouldUseSplitNotificationShade: Boolean
+ get() = shadeInteractor.shadeMode.value == ShadeMode.Split
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
index 730aa62..5dde14b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -65,7 +65,7 @@
it.topActivity,
it.baseIntent?.component,
it.taskDescription?.backgroundColor,
- isForegroundTask = it.taskId in foregroundTaskIds
+ isForegroundTask = it.taskId in foregroundTaskIds && it.isVisible
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index c657b55..9c88eb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -17,6 +17,8 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -27,6 +29,7 @@
import android.widget.FrameLayout;
import com.android.systemui.res.R;
+import com.android.systemui.shade.LargeScreenHeaderHelper;
import com.android.systemui.util.LargeScreenUtils;
/**
@@ -97,7 +100,9 @@
qqsLP.topMargin = mContext.getResources()
.getDimensionPixelSize(R.dimen.qqs_layout_margin_top);
} else {
- qqsLP.topMargin = mContext.getResources()
+ qqsLP.topMargin = centralizedStatusBarHeightFix()
+ ? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
+ : mContext.getResources()
.getDimensionPixelSize(R.dimen.large_screen_shade_header_min_height);
}
mHeaderQsPanel.setLayoutParams(qqsLP);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
index aed08f8..4a8e33a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/TileSpec.kt
@@ -37,7 +37,11 @@
data class PlatformTileSpec
internal constructor(
override val spec: String,
- ) : TileSpec(spec)
+ ) : TileSpec(spec) {
+ override fun toString(): String {
+ return "P($spec)"
+ }
+ }
/**
* Container for the spec of a tile provided by an app.
@@ -50,7 +54,7 @@
val componentName: ComponentName,
) : TileSpec(spec) {
override fun toString(): String {
- return "CustomTileSpec(${componentName.toShortString()})"
+ return "C(${componentName.flattenToShortString()})"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
index 9076182..f02b871 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayDialogController.java
@@ -16,12 +16,14 @@
package com.android.systemui.reardisplay;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManagerGlobal;
import android.view.LayoutInflater;
@@ -29,7 +31,6 @@
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -179,6 +180,7 @@
*/
private void initializeValues(int startingBaseState) {
mRearDisplayEducationDialog = mSystemUIDialogFactory.create();
+ // TODO(b/329170810): Refactor and remove with updated DeviceStateManager values.
if (mFoldedStates == null) {
mFoldedStates = mResources.getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
@@ -228,21 +230,19 @@
private class DeviceStateManagerCallback implements DeviceStateManager.DeviceStateCallback {
@Override
- public void onBaseStateChanged(int state) {
- if (mStartedFolded && !isFoldedState(state)) {
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
+ if (mStartedFolded && !state.hasProperty(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) {
// We've opened the device, we can close the overlay
mRearDisplayEducationDialog.dismiss();
closeOverlayAndNotifyService(false);
- } else if (!mStartedFolded && isFoldedState(state)) {
+ } else if (!mStartedFolded && state.hasProperty(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)) {
// We've closed the device, finish activity
mRearDisplayEducationDialog.dismiss();
closeOverlayAndNotifyService(true);
}
}
-
- // We only care about physical device changes in this scenario
- @Override
- public void onStateChanged(int state) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9ae049d..d0ff338 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -37,6 +37,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_WAKEFULNESS_TRANSITION;
@@ -114,6 +115,8 @@
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -125,8 +128,6 @@
import javax.inject.Inject;
import javax.inject.Provider;
-import dagger.Lazy;
-
/**
* Class to send information from overview to launcher with a binder.
*/
@@ -669,7 +670,8 @@
// Listen for tracing state changes
@Override
public void onTracingStateChanged(boolean enabled) {
- // TODO(b/286509643) Cleanup callers of this; Unused downstream
+ mSysUiState.setFlag(SYSUI_STATE_TRACING_ENABLED, enabled)
+ .commitUpdate(mContext.getDisplayId());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 1808d98..467089d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -19,9 +19,11 @@
package com.android.systemui.scene.shared.flag
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.keyguardBottomAreaRefactor
+import com.android.systemui.Flags.keyguardWmStateRefactor
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.Flags.sceneContainer
import com.android.systemui.dagger.SysUISingleton
@@ -46,7 +48,8 @@
keyguardBottomAreaRefactor() &&
migrateClocksToBlueprint() &&
ComposeLockscreen.isEnabled &&
- MediaInSceneContainerFlag.isEnabled
+ MediaInSceneContainerFlag.isEnabled &&
+ keyguardWmStateRefactor()
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
/**
@@ -65,6 +68,7 @@
sequenceOf(
FlagToken(FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR, keyguardBottomAreaRefactor()),
FlagToken(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, migrateClocksToBlueprint()),
+ FlagToken(FLAG_KEYGUARD_WM_STATE_REFACTOR, keyguardWmStateRefactor()),
ComposeLockscreen.token,
MediaInSceneContainerFlag.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index ac94f39..b2c01e1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -82,7 +82,7 @@
private final RecordingController mController;
protected final KeyguardDismissUtil mKeyguardDismissUtil;
private final Handler mMainHandler;
- private ScreenRecordingAudioSource mAudioSource;
+ private ScreenRecordingAudioSource mAudioSource = ScreenRecordingAudioSource.NONE;
private boolean mShowTaps;
private boolean mOriginalShowTaps;
private ScreenMediaRecorder mRecorder;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
deleted file mode 100644
index 7e234ae..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_EDIT;
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ACTION_INTENT;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-import android.view.RemoteAnimationAdapter;
-import android.view.WindowManagerGlobal;
-
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.DisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import javax.inject.Inject;
-
-/**
- * Receiver to proxy the share or edit intent, used to clean up the notification and send
- * appropriate signals to the system (ie. to dismiss the keyguard if necessary).
- */
-public class ActionProxyReceiver extends BroadcastReceiver {
- private static final String TAG = "ActionProxyReceiver";
-
- private final ActivityManagerWrapper mActivityManagerWrapper;
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final DisplayTracker mDisplayTracker;
- private final ActivityStarter mActivityStarter;
-
- @Inject
- public ActionProxyReceiver(ActivityManagerWrapper activityManagerWrapper,
- ScreenshotSmartActions screenshotSmartActions,
- DisplayTracker displayTracker,
- ActivityStarter activityStarter) {
- mActivityManagerWrapper = activityManagerWrapper;
- mScreenshotSmartActions = screenshotSmartActions;
- mDisplayTracker = displayTracker;
- mActivityStarter = activityStarter;
- }
-
- @Override
- public void onReceive(Context context, final Intent intent) {
- Runnable startActivityRunnable = () -> {
- mActivityManagerWrapper.closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
-
- PendingIntent actionIntent = intent.getParcelableExtra(EXTRA_ACTION_INTENT);
- ActivityOptions opts = ActivityOptions.makeBasic();
- opts.setDisallowEnterPictureInPictureWhileLaunching(
- intent.getBooleanExtra(EXTRA_DISALLOW_ENTER_PIP, false));
- opts.setPendingIntentBackgroundActivityStartMode(
- ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- try {
- actionIntent.send(context, 0, null, null, null, null, opts.toBundle());
- if (intent.getBooleanExtra(ScreenshotController.EXTRA_OVERRIDE_TRANSITION, false)) {
- RemoteAnimationAdapter runner = new RemoteAnimationAdapter(
- ScreenshotController.SCREENSHOT_REMOTE_RUNNER, 0, 0);
- try {
- WindowManagerGlobal.getWindowManagerService()
- .overridePendingAppTransitionRemote(runner,
- mDisplayTracker.getDefaultDisplayId());
- } catch (Exception e) {
- Log.e(TAG, "Error overriding screenshot app transition", e);
- }
- }
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled", e);
- }
-
- };
-
- mActivityStarter.executeRunnableDismissingKeyguard(startActivityRunnable, null,
- true /* dismissShade */, true /* afterKeyguardGone */,
- true /* deferred */);
-
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- String actionType = Intent.ACTION_EDIT.equals(intent.getAction())
- ? ACTION_TYPE_EDIT
- : ACTION_TYPE_SHARE;
- mScreenshotSmartActions.notifyScreenshotAction(
- intent.getStringExtra(EXTRA_ID), actionType, false, null);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
deleted file mode 100644
index e0346f2..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import com.android.systemui.dagger.qualifiers.Background;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Removes the file at a provided URI.
- */
-public class DeleteScreenshotReceiver extends BroadcastReceiver {
-
- private final ScreenshotSmartActions mScreenshotSmartActions;
- private final Executor mBackgroundExecutor;
-
- @Inject
- public DeleteScreenshotReceiver(ScreenshotSmartActions screenshotSmartActions,
- @Background Executor backgroundExecutor) {
- mScreenshotSmartActions = screenshotSmartActions;
- mBackgroundExecutor = backgroundExecutor;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.hasExtra(SCREENSHOT_URI_ID)) {
- return;
- }
-
- // And delete the image from the media store
- final Uri uri = Uri.parse(intent.getStringExtra(SCREENSHOT_URI_ID));
- mBackgroundExecutor.execute(() -> {
- ContentResolver resolver = context.getContentResolver();
- resolver.delete(uri, null, null);
- });
- if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) {
- mScreenshotSmartActions.notifyScreenshotAction(
- intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index c4287ca..864f29a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -126,7 +126,7 @@
/**
* Writes the given Bitmap to outputFile.
*/
- ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
+ public ListenableFuture<File> exportToRawFile(Executor executor, Bitmap bitmap,
final File outputFile) {
return CallbackToFutureAdapter.getFuture(
(completer) -> {
@@ -196,7 +196,7 @@
* @param bitmap the bitmap to export
* @return a listenable future result
*/
- ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
+ public ListenableFuture<Result> export(Executor executor, UUID requestId, Bitmap bitmap,
ZonedDateTime captureTime, UserHandle owner, int displayId) {
return export(executor, new Task(mResolver, requestId, bitmap, captureTime, mCompressFormat,
mQuality, /* publish */ true, owner, mFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 1f6d212..a1481f6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -34,6 +34,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER
import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
index 6050c2b..440cf1c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LogConfig.java
@@ -16,8 +16,9 @@
package com.android.systemui.screenshot;
+/** Stores debug log configuration for screenshots. */
@SuppressWarnings("PointlessBooleanExpression")
-class LogConfig {
+public class LogConfig {
/** Log ALL the things... */
private static final boolean DEBUG_ALL = false;
@@ -29,36 +30,37 @@
private static final boolean TAG_WITH_CLASS_NAME = false;
/** Action creation and user selection: Share, Save, Edit, Delete, Smart action, etc */
- static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
+ public static final boolean DEBUG_ACTIONS = DEBUG_ALL || false;
/** Debug info about animations such as start, complete and cancel */
- static final boolean DEBUG_ANIM = DEBUG_ALL || false;
+ public static final boolean DEBUG_ANIM = DEBUG_ALL || false;
/** Whenever Uri is supplied to consumer, or onComplete runnable is run() */
- static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
+ public static final boolean DEBUG_CALLBACK = DEBUG_ALL || false;
/** Logs information about dismissing the screenshot tool */
- static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
+ public static final boolean DEBUG_DISMISS = DEBUG_ALL || false;
/** Touch or key event driven action or side effects */
- static final boolean DEBUG_INPUT = DEBUG_ALL || false;
+ public static final boolean DEBUG_INPUT = DEBUG_ALL || false;
/** Scroll capture usage */
- static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
+ public static final boolean DEBUG_SCROLL = DEBUG_ALL || false;
/** Service lifecycle events and callbacks */
- static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
+ public static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
/** Storage related actions, Bitmap.compress, ContentManager, etc */
- static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
+ public static final boolean DEBUG_STORAGE = DEBUG_ALL || false;
/** High level logical UI actions: timeout, onConfigChanged, insets, show actions, reset */
- static final boolean DEBUG_UI = DEBUG_ALL || false;
+ public static final boolean DEBUG_UI = DEBUG_ALL || false;
/** Interactions with Window and WindowManager */
- static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
+ public static final boolean DEBUG_WINDOW = DEBUG_ALL || false;
- static String logTag(Class<?> cls) {
+ /** Get the appropriate class name */
+ public static String logTag(Class<?> cls) {
return TAG_WITH_CLASS_NAME ? cls.getSimpleName() : TAG_SS;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 198a29c..c8e13bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -87,6 +87,10 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
+import com.android.systemui.screenshot.scroll.LongScreenshotActivity;
+import com.android.systemui.screenshot.scroll.LongScreenshotData;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
import com.android.systemui.util.Assert;
import com.google.common.util.concurrent.ListenableFuture;
@@ -200,7 +204,7 @@
void onActionsReady(ScreenshotController.QuickShareData quickShareData);
}
- interface TransitionDestination {
+ public interface TransitionDestination {
/**
* Allows the long screenshot activity to call back with a destination location (the bounds
* on screen of the destination for the transitioning view) and a Runnable to be run once
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 1c5a8a1..cb2dba0 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -92,6 +92,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.res.R;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index 182b889..6be32a9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -25,6 +25,7 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
/** Abstraction of the surface between ScreenshotController and ScreenshotView */
interface ScreenshotViewProxy {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
index aa23d6b..d87d85b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsActivity.java
@@ -52,7 +52,7 @@
import com.android.internal.logging.UiEventLogger.UiEventEnum;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView;
+import com.android.systemui.screenshot.scroll.CropView;
import com.android.systemui.settings.UserTracker;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index 2f411ea..5e561cf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -265,7 +265,7 @@
Log.w(TAG, "No boundary selected");
break;
}
- Log.i(TAG, "Updated mCrop: " + mCrop);
+ Log.i(TAG, "Updated mCrop: " + mCrop);
invalidate();
}
@@ -385,7 +385,7 @@
/**
* @param action either ACTION_DOWN, ACTION_UP or ACTION_MOVE.
- * @param x coordinate of the relevant pointer.
+ * @param x x-coordinate of the relevant pointer.
*/
private void updateListener(int action, float x) {
if (mCropInteractionListener != null && isVertical(mCurrentDraggingBoundary)) {
@@ -643,11 +643,13 @@
/**
* Listen for crop motion events and state.
*/
- public interface CropInteractionListener {
+ interface CropInteractionListener {
void onCropDragStarted(CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx, float horizontalCenter, float x);
+
void onCropDragMoved(CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx, float horizontalCenter, float x);
+
void onCropDragComplete();
}
@@ -675,8 +677,7 @@
out.writeParcelable(mCrop, 0);
}
- public static final Parcelable.Creator<SavedState> CREATOR
- = new Parcelable.Creator<SavedState>() {
+ public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
index 7ee7c31..df86d69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -35,20 +35,20 @@
import javax.inject.Inject;
/** Loads images. */
-public class ImageLoader {
+class ImageLoader {
private final ContentResolver mResolver;
static class Result {
- @Nullable Uri uri;
- @Nullable File fileName;
- @Nullable Bitmap bitmap;
+ @Nullable Uri mUri;
+ @Nullable File mFilename;
+ @Nullable Bitmap mBitmap;
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Result{");
- sb.append("uri=").append(uri);
- sb.append(", fileName=").append(fileName);
- sb.append(", bitmap=").append(bitmap);
+ sb.append("uri=").append(mUri);
+ sb.append(", fileName=").append(mFilename);
+ sb.append(", bitmap=").append(mBitmap);
sb.append('}');
return sb.toString();
}
@@ -69,11 +69,10 @@
return CallbackToFutureAdapter.getFuture(completer -> {
Result result = new Result();
try (InputStream in = mResolver.openInputStream(uri)) {
- result.uri = uri;
- result.bitmap = BitmapFactory.decodeStream(in);
+ result.mUri = uri;
+ result.mBitmap = BitmapFactory.decodeStream(in);
completer.set(result);
- }
- catch (IOException e) {
+ } catch (IOException e) {
completer.setException(e);
}
return "BitmapFactory#decodeStream";
@@ -91,8 +90,8 @@
return CallbackToFutureAdapter.getFuture(completer -> {
try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
Result result = new Result();
- result.fileName = file;
- result.bitmap = BitmapFactory.decodeStream(in);
+ result.mFilename = file;
+ result.mBitmap = BitmapFactory.decodeStream(in);
completer.set(result);
} catch (IOException e) {
completer.setException(e);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
index a95c91b..c9c297e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static android.graphics.ColorSpace.Named.SRGB;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
index 356f67e..76a72f7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ImageTileSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.AnyThread;
import android.graphics.Bitmap;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
index 00d480a..1e1a577 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -47,11 +47,14 @@
import com.android.internal.view.OneShotPreDrawListener;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.res.R;
-import com.android.systemui.screenshot.CropView.CropBoundary;
-import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
-import com.android.systemui.settings.UserTracker;
+import com.android.systemui.screenshot.ActionIntentCreator;
+import com.android.systemui.screenshot.ActionIntentExecutor;
+import com.android.systemui.screenshot.ImageExporter;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.CropView.CropBoundary;
+import com.android.systemui.screenshot.scroll.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
@@ -81,8 +84,6 @@
private final ImageExporter mImageExporter;
private final LongScreenshotData mLongScreenshotHolder;
private final ActionIntentExecutor mActionExecutor;
- private final FeatureFlags mFeatureFlags;
- private final UserTracker mUserTracker;
private ImageView mPreview;
private ImageView mTransitionView;
@@ -113,16 +114,13 @@
@Inject
public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
@Main Executor mainExecutor, @Background Executor bgExecutor,
- LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor,
- FeatureFlags featureFlags, UserTracker userTracker) {
+ LongScreenshotData longScreenshotHolder, ActionIntentExecutor actionExecutor) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
mLongScreenshotHolder = longScreenshotHolder;
mActionExecutor = actionExecutor;
- mFeatureFlags = featureFlags;
- mUserTracker = userTracker;
}
@@ -265,13 +263,13 @@
private void onCachedImageLoaded(ImageLoader.Result imageResult) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_ACTIVITY_CACHED_IMAGE_LOADED);
- BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
+ BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.mBitmap);
mPreview.setImageDrawable(drawable);
mPreview.setAlpha(1f);
- mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
- imageResult.bitmap.getHeight());
+ mMagnifierView.setDrawable(drawable, imageResult.mBitmap.getWidth(),
+ imageResult.mBitmap.getHeight());
mCropView.setVisibility(View.VISIBLE);
- mSavedImagePath = imageResult.fileName;
+ mSavedImagePath = imageResult.mFilename;
setButtonsEnabled(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
index f549faf..ebac5bf 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotData.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotData.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.screenshot.ScreenshotController;
import java.util.concurrent.atomic.AtomicReference;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
index 0c543cd..0a1a747 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/MagnifierView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -64,16 +64,16 @@
private ViewPropertyAnimator mTranslationAnimator;
private final Animator.AnimatorListener mTranslationAnimatorListener =
new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- mTranslationAnimator = null;
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mTranslationAnimator = null;
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- mTranslationAnimator = null;
- }
- };
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTranslationAnimator = null;
+ }
+ };
public MagnifierView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
index e93f737..0e43343 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
@@ -46,6 +46,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.screenshot.LogConfig;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
index 8a2678c..f4c77da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/ScrollCaptureController.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.content.Context;
import android.graphics.Bitmap;
@@ -30,8 +30,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.LogConfig;
+import com.android.systemui.screenshot.ScreenshotEvent;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import com.google.common.util.concurrent.ListenableFuture;
@@ -85,7 +87,7 @@
mImageTileSet = imageTileSet;
}
- /** Returns a bitmap containing the combinded result. */
+ /** Returns a bitmap containing the combined result. */
public Bitmap toBitmap() {
return mImageTileSet.toBitmap();
}
@@ -167,7 +169,7 @@
* {@link ScrollCaptureResponse#isConnected() connected}.
* @return a future ImageTile set containing the result
*/
- ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
+ public ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
mCancelled = false;
return CallbackToFutureAdapter.getFuture(completer -> {
mCaptureCompleter = completer;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
index 71df369..00455bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/TiledImageDrawable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.annotation.Nullable;
import android.graphics.Canvas;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 8197b66..db06c6b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -31,12 +31,6 @@
* @see NotificationPanelViewController
*/
interface ShadeViewController {
- /**
- * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
- * down event.
- */
- val isPanelExpanded: Boolean
-
/** Returns whether the shade is tracking touches for expand/collapse of the shade or QS. */
val isTracking: Boolean
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
index 79ffe06..60cb061 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractor.kt
@@ -43,6 +43,12 @@
@Deprecated("Use SceneInteractor.currentScene instead.") val legacyPanelExpansion: Flow<Float>
/**
+ * Returns whether the shade height is greater than zero or the shade is expecting a synthesized
+ * down event.
+ */
+ @Deprecated("Use ShadeInteractor.isAnyExpanded instead.") val isPanelExpanded: Boolean
+
+ /**
* This method should not be used anymore, you should probably use [.isShadeFullyOpen] instead.
* It was overused as indicating if shade is open or we're on keyguard/AOD. Moving forward we
* should be explicit about the what state we're checking.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
index 3ad2b56..3877677 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImpl.kt
@@ -111,4 +111,6 @@
private fun SceneKey.isExpandable(): Boolean {
return this == Scenes.Shade || this == Scenes.QuickSettings
}
+
+ override val isPanelExpanded = shadeInteractor.isAnyExpanded.value
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4275fc6..4406813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -178,6 +178,7 @@
private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
+ private static final int MSG_SET_SPLITSCREEN_FOCUS = 81 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -508,6 +509,11 @@
default void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop) {}
/**
+ * @see IStatusBar#setSplitscreenFocus
+ */
+ default void setSplitscreenFocus(boolean leftOrTop) {}
+
+ /**
* @see IStatusBar#showMediaOutputSwitcher
*/
default void showMediaOutputSwitcher(String packageName) {}
@@ -1349,6 +1355,12 @@
}
@Override
+ public void setSplitscreenFocus(boolean leftOrTop) {
+ synchronized (mLock) {
+ mHandler.obtainMessage(MSG_SET_SPLITSCREEN_FOCUS, leftOrTop).sendToTarget();
+ }
+ }
+ @Override
public void showMediaOutputSwitcher(String packageName) {
int callingUid = Binder.getCallingUid();
if (callingUid != 0 && callingUid != Process.SYSTEM_UID) {
@@ -1919,6 +1931,11 @@
}
break;
}
+ case MSG_SET_SPLITSCREEN_FOCUS:
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).setSplitscreenFocus((Boolean) msg.obj);
+ }
+ break;
case MSG_SHOW_MEDIA_OUTPUT_SWITCHER:
args = (SomeArgs) msg.obj;
String clientPackageName = (String) args.arg1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
index 1bebbfd..ca73081 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractor.kt
@@ -18,12 +18,16 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardOcclusionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardSurfaceBehindInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -46,6 +50,8 @@
keyguardTransitionInteractor: KeyguardTransitionInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
powerInteractor: PowerInteractor,
+ wmLockscreenVisibilityInteractor: WindowManagerLockscreenVisibilityInteractor,
+ surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
) {
/** Occlusion state to apply whenever a keyguard transition is STARTED, if any. */
private val occlusionStateFromStartedStep: Flow<OccludedState> =
@@ -98,11 +104,21 @@
OccludedState(occluded = occluded, animate = false)
}
- /** Occlusion state to apply to SKBVM's setOccluded call. */
+ /** Occlusion state to apply to SBKVM's setOccluded call. */
val keyguardViewOcclusionState =
merge(occlusionStateFromStartedStep, occlusionStateFromFinishedStep)
.distinctUntilChangedBy {
// Don't switch 'animate' values mid-transition.
it.occluded
}
+
+ /** Visibility state to apply to SBKVM via show() and hide(). */
+ val keyguardViewVisibility =
+ combine(
+ wmLockscreenVisibilityInteractor.lockscreenVisibility,
+ surfaceBehindInteractor.isAnimatingSurface,
+ ) { lockscreenVisible, animatingSurface ->
+ lockscreenVisible || animatingSurface
+ }
+ .distinctUntilChanged()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index 1faca00..1906b7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -59,7 +59,6 @@
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -97,7 +96,6 @@
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final PowerManager mPowerManager;
private final Optional<Vibrator> mVibratorOptional;
- private final DisableFlagsLogger mDisableFlagsLogger;
private final int mDisplayId;
private final UserTracker mUserTracker;
private final boolean mVibrateOnOpening;
@@ -137,7 +135,6 @@
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
PowerManager powerManager,
Optional<Vibrator> vibratorOptional,
- DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId,
Lazy<CameraLauncher> cameraLauncherLazy,
UserTracker userTracker,
@@ -165,7 +162,6 @@
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mPowerManager = powerManager;
mVibratorOptional = vibratorOptional;
- mDisableFlagsLogger = disableFlagsLogger;
mDisplayId = displayId;
mCameraLauncherLazy = cameraLauncherLazy;
mUserTracker = userTracker;
@@ -489,7 +485,7 @@
@Override
public void togglePanel() {
- if (mShadeViewController.isPanelExpanded()) {
+ if (mPanelExpansionInteractor.isPanelExpanded()) {
mShadeController.animateCollapseShade();
} else {
mShadeController.animateExpandShade();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
index 56b0d598..db237e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone
import android.content.Context
+import android.hardware.devicestate.DeviceState
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
import com.android.internal.R
@@ -45,13 +46,13 @@
private var wasFolded: Boolean? = null
- override fun onStateChanged(state: Int) {
- val isFolded = foldedDeviceStates.contains(state)
+ override fun onDeviceStateChanged(state: DeviceState) {
+ val isFolded = foldedDeviceStates.contains(state.identifier)
if (wasFolded == isFolded) {
return
}
wasFolded = isFolded
- val willGoToSleep = goToSleepDeviceStates.contains(state)
+ val willGoToSleep = goToSleepDeviceStates.contains(state.identifier)
listener.onFoldStateChanged(isFolded, willGoToSleep)
}
}
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 14e934fa..f99817a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -107,8 +106,6 @@
import dagger.Lazy;
-import kotlin.Unit;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -118,6 +115,7 @@
import javax.inject.Inject;
+import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.Job;
@@ -523,16 +521,7 @@
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
mJavaAdapter.alwaysCollectFlow(
- combineFlows(
- mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
- mSurfaceBehindInteractor.get().isAnimatingSurface(),
- (lockscreenVis, animatingSurface) ->
- // TODO(b/322546110): Waiting until we're not animating the
- // surface is a workaround to avoid jank. We should actually
- // fix the source of the jank, and then hide the keyguard
- // view without waiting for the animation to end.
- lockscreenVis || animatingSurface
- ),
+ mStatusBarKeyguardViewManagerInteractor.getKeyguardViewVisibility(),
this::consumeShowStatusBarKeyguardView);
mJavaAdapter.alwaysCollectFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 88374d6..67d2299 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -12,16 +12,19 @@
import android.view.Surface
import android.view.View
import android.view.WindowManager.fixScale
+import com.android.app.animation.Interpolators
+import com.android.app.tracing.namedRunnable
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
-import com.android.app.animation.Interpolators
+import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -32,9 +35,8 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.Flags.lightRevealMigration
-import com.android.app.tracing.namedRunnable
import com.android.systemui.util.settings.GlobalSettings
+import dagger.Lazy
import javax.inject.Inject
/**
@@ -60,14 +62,15 @@
private val context: Context,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val statusBarStateControllerImpl: StatusBarStateControllerImpl,
- private val keyguardViewMediatorLazy: dagger.Lazy<KeyguardViewMediator>,
+ private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val keyguardStateController: KeyguardStateController,
- private val dozeParameters: dagger.Lazy<DozeParameters>,
+ private val dozeParameters: Lazy<DozeParameters>,
private val globalSettings: GlobalSettings,
- private val notifShadeWindowControllerLazy: dagger.Lazy<NotificationShadeWindowController>,
+ private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>,
private val interactionJankMonitor: InteractionJankMonitor,
private val powerManager: PowerManager,
- private val handler: Handler = Handler()
+ private val panelExpansionInteractorLazy: Lazy<PanelExpansionInteractor>,
+ private val handler: Handler = Handler(),
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
private lateinit var centralSurfaces: CentralSurfaces
private lateinit var shadeViewController: ShadeViewController
@@ -346,7 +349,7 @@
// already expanded and showing notifications/QS, the animation looks really messy. For now,
// disable it if the notification panel is expanded.
if ((!this::centralSurfaces.isInitialized ||
- shadeViewController.isPanelExpanded) &&
+ panelExpansionInteractorLazy.get().isPanelExpanded) &&
// Status bar might be expanded because we have started
// playing the animation already
!isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 6aaf5d6..55a0f59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -131,21 +131,37 @@
}
/**
- * Returns true if given HeadsUpEntry is the last one tracked by AvalancheController. Used by
- * BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration during active
- * avalanche.
+ * Returns duration based on
+ * 1) Whether HeadsUpEntry is the last one tracked byAvalancheController
+ * 2) The priority of the top HUN in the next batch Used by
+ * BaseHeadsUpManager.HeadsUpEntry.calculateFinishTime to shorten display duration.
*/
- fun shortenDuration(entry: HeadsUpEntry): Boolean {
+ fun getDurationMs(entry: HeadsUpEntry, autoDismissMs: Int): Int {
if (!NotificationThrottleHun.isEnabled) {
- // Use default display duration, like we always did before AvalancheController existed
- return false
+ // Use default duration, like we did before AvalancheController existed
+ return autoDismissMs
}
val showingList: MutableList<HeadsUpEntry> = mutableListOf()
headsUpEntryShowing?.let { showingList.add(it) }
- val allEntryList = showingList + nextList
- // Shorten duration if not last entry
- return allEntryList.indexOf(entry) != allEntryList.size - 1
+ val entryList = showingList + nextList
+ if (entryList.indexOf(entry) == entryList.size - 1) {
+ // Use default duration if last entry
+ return autoDismissMs
+ }
+
+ nextList.sort()
+ val nextEntry = nextList[0]
+
+ if (nextEntry.compareNonTimeFields(entry) == -1) {
+ // Next entry is higher priority
+ return 500
+ } else if (nextEntry.compareNonTimeFields(entry) == 0) {
+ // Next entry is same priority
+ return 1000
+ } else {
+ return autoDismissMs
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 05cc73e..50de3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -776,7 +776,7 @@
return mEarliestRemovalTime < mSystemClock.elapsedRealtime();
}
- public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+ public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
boolean isPinned = mEntry.isRowPinned();
boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
if (isPinned && !otherPinned) {
@@ -806,7 +806,14 @@
} else if (!mRemoteInputActive && headsUpEntry.mRemoteInputActive) {
return 1;
}
+ return 0;
+ }
+ public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+ int nonTimeCompareResult = compareNonTimeFields(headsUpEntry);
+ if (nonTimeCompareResult != 0) {
+ return nonTimeCompareResult;
+ }
if (mPostTime > headsUpEntry.mPostTime) {
return -1;
} else if (mPostTime == headsUpEntry.mPostTime) {
@@ -929,10 +936,8 @@
int requestedTimeOutMs;
if (isStickyForSomeTime()) {
requestedTimeOutMs = mStickyForSomeTimeAutoDismissTime;
- } else if (mAvalancheController.shortenDuration(this)) {
- requestedTimeOutMs = 1000;
} else {
- requestedTimeOutMs = mAutoDismissTime;
+ requestedTimeOutMs = mAvalancheController.getDurationMs(this, mAutoDismissTime);
}
final long duration = getRecommendedHeadsUpTimeoutMs(requestedTimeOutMs);
return mPostTime + duration;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
index 422aa4d..de0eb49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureControllerImpl.java
@@ -16,12 +16,13 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
import android.content.Context;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateUtil;
import android.util.SparseIntArray;
-import androidx.annotation.NonNull;
-
import com.android.app.tracing.ListenersTracing;
import com.android.internal.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -42,8 +43,9 @@
/** From androidx.window.common.COMMON_STATE_USE_BASE_STATE */
private static final int COMMON_STATE_USE_BASE_STATE = 1000;
private final List<Callback> mListeners = new ArrayList<>();
+ private final List<DeviceState> mSupportedStates;
+ private DeviceState mCurrentDeviceState;
private int mCurrentDevicePosture = DEVICE_POSTURE_UNKNOWN;
- private int mCurrentBasePosture = DEVICE_POSTURE_UNKNOWN;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
@@ -76,21 +78,17 @@
mDeviceStateToPostureMap.put(deviceState, posture);
}
+ mSupportedStates = deviceStateManager.getSupportedDeviceStates();
deviceStateManager.registerCallback(executor, new DeviceStateManager.DeviceStateCallback() {
@Override
- public void onStateChanged(int state) {
+ public void onDeviceStateChanged(@NonNull DeviceState state) {
+ mCurrentDeviceState = state;
Assert.isMainThread();
- mCurrentDevicePosture =
- mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
- sendUpdatePosture();
- }
-
- @Override
- public void onBaseStateChanged(int state) {
- Assert.isMainThread();
- mCurrentBasePosture = mDeviceStateToPostureMap.get(state, DEVICE_POSTURE_UNKNOWN);
-
- if (useBaseState()) {
+ int newDevicePosture =
+ mDeviceStateToPostureMap.get(state.getIdentifier(), DEVICE_POSTURE_UNKNOWN);
+ if (newDevicePosture != mCurrentDevicePosture
+ || newDevicePosture == COMMON_STATE_USE_BASE_STATE) {
+ mCurrentDevicePosture = newDevicePosture;
sendUpdatePosture();
}
}
@@ -120,7 +118,8 @@
@Override
public int getDevicePosture() {
if (useBaseState()) {
- return mCurrentBasePosture;
+ return DeviceStateUtil.calculateBaseStateIdentifier(mCurrentDeviceState,
+ mSupportedStates);
} else {
return mCurrentDevicePosture;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 3008c866d..88cf46a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -20,6 +20,7 @@
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import android.annotation.Nullable;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.os.Trace;
import android.util.IndentingPrintWriter;
@@ -120,18 +121,18 @@
mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
}
- private void updateDeviceState(int state) {
- mLogger.logUpdateDeviceState(mDeviceState, state);
- if (Trace.isEnabled()) {
- Trace.traceBegin(
- Trace.TRACE_TAG_APP, "updateDeviceState [state=" + state + "]");
- }
+ private void updateDeviceState(@NonNull DeviceState state) {
+ mLogger.logUpdateDeviceState(mDeviceState, state.getIdentifier());
try {
- if (mDeviceState == state) {
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP,
+ "updateDeviceState [state=" + state.getIdentifier() + "]");
+ }
+ if (mDeviceState == state.getIdentifier()) {
return;
}
- readPersistedSetting("updateDeviceState", state);
+ readPersistedSetting("updateDeviceState", state.getIdentifier());
} finally {
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index b5efc44..aac18e3 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -86,15 +86,6 @@
import com.android.systemui.util.settings.SecureSettings;
import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
-import com.google.ux.material.libmonet.hct.Hct;
-import com.google.ux.material.libmonet.scheme.DynamicScheme;
-import com.google.ux.material.libmonet.scheme.SchemeExpressive;
-import com.google.ux.material.libmonet.scheme.SchemeFruitSalad;
-import com.google.ux.material.libmonet.scheme.SchemeMonochrome;
-import com.google.ux.material.libmonet.scheme.SchemeNeutral;
-import com.google.ux.material.libmonet.scheme.SchemeRainbow;
-import com.google.ux.material.libmonet.scheme.SchemeTonalSpot;
-import com.google.ux.material.libmonet.scheme.SchemeVibrant;
import org.json.JSONException;
import org.json.JSONObject;
@@ -150,7 +141,7 @@
// Dominant color extracted from wallpaper, NOT the color used on the overlay
protected int mMainWallpaperColor = Color.TRANSPARENT;
// UI contrast as reported by UiModeManager
- private float mContrast = 0;
+ private double mContrast = 0.0;
// Theme variant: Vibrant, Tonal, Expressive, etc
@VisibleForTesting
protected Style mThemeStyle = Style.TONAL_SPOT;
@@ -170,8 +161,8 @@
private final JavaAdapter mJavaAdapter;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final UiModeManager mUiModeManager;
- private DynamicScheme mDynamicSchemeDark;
- private DynamicScheme mDynamicSchemeLight;
+ private ColorScheme mDarkColorScheme;
+ private ColorScheme mLightColorScheme;
// Defers changing themes until Setup Wizard is done.
private boolean mDeferredThemeEvaluation;
@@ -210,7 +201,7 @@
boolean currentUser = userId == mUserTracker.getUserId();
boolean isAsleep = themeOverlayControllerWakefulnessDeprecation()
? mKeyguardTransitionInteractor.isFinishedInStateWhereValue(
- state -> KeyguardState.Companion.deviceIsAsleepInState(state))
+ KeyguardState.Companion::deviceIsAsleepInState)
: mWakefulnessLifecycle.getWakefulness() != WAKEFULNESS_ASLEEP;
if (currentUser && !mAcceptColorEvents && isAsleep) {
@@ -483,19 +474,14 @@
reevaluateSystemTheme(true /* forceReload */);
});
+ // All wallpaper color and keyguard logic only applies when Monet is enabled.
if (!mIsMonetEnabled) {
return;
}
mUserTracker.addCallback(mUserTrackerCallback, mMainExecutor);
-
mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
- // All wallpaper color and keyguard logic only applies when Monet is enabled.
- if (!mIsMonetEnabled) {
- return;
- }
-
// Upon boot, make sure we have the most up to date colors
Runnable updateColors = () -> {
WallpaperColors systemColor = mWallpaperManager.getWallpaperColors(
@@ -589,29 +575,6 @@
return ColorScheme.getSeedColor(wallpaperColors);
}
- private static DynamicScheme dynamicSchemeFromStyle(Style style, int color,
- boolean isDark, double contrastLevel) {
- Hct sourceColorHct = Hct.fromInt(color);
- switch (style) {
- case EXPRESSIVE:
- return new SchemeExpressive(sourceColorHct, isDark, contrastLevel);
- case SPRITZ:
- return new SchemeNeutral(sourceColorHct, isDark, contrastLevel);
- case TONAL_SPOT:
- return new SchemeTonalSpot(sourceColorHct, isDark, contrastLevel);
- case FRUIT_SALAD:
- return new SchemeFruitSalad(sourceColorHct, isDark, contrastLevel);
- case RAINBOW:
- return new SchemeRainbow(sourceColorHct, isDark, contrastLevel);
- case VIBRANT:
- return new SchemeVibrant(sourceColorHct, isDark, contrastLevel);
- case MONOCHROMATIC:
- return new SchemeMonochrome(sourceColorHct, isDark, contrastLevel);
- default:
- return null;
- }
- }
-
@VisibleForTesting
protected boolean isNightMode() {
return (mResources.getConfiguration().uiMode
@@ -626,22 +589,17 @@
@VisibleForTesting
protected boolean isPrivateProfile(UserHandle userHandle) {
Context usercontext = mContext.createContextAsUser(userHandle,0);
- if (usercontext.getSystemService(UserManager.class).isPrivateProfile()) {
- return true;
- }
- return false;
+ return usercontext.getSystemService(UserManager.class).isPrivateProfile();
}
private void createOverlays(int color) {
- boolean nightMode = isNightMode();
- mColorScheme = new ColorScheme(color, nightMode, mThemeStyle);
+ mDarkColorScheme = new ColorScheme(color, true /* isDark */, mThemeStyle, mContrast);
+ mLightColorScheme = new ColorScheme(color, false /* isDark */, mThemeStyle, mContrast);
+ mColorScheme = isNightMode() ? mDarkColorScheme : mLightColorScheme;
+
mNeutralOverlay = createNeutralOverlay();
mSecondaryOverlay = createAccentOverlay();
- mDynamicSchemeDark = dynamicSchemeFromStyle(
- mThemeStyle, color, true /* isDark */, mContrast);
- mDynamicSchemeLight = dynamicSchemeFromStyle(
- mThemeStyle, color, false /* isDark */, mContrast);
mDynamicOverlay = createDynamicOverlay();
}
@@ -682,10 +640,10 @@
private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
String suffix = isDark ? "dark" : "light";
- DynamicScheme scheme = isDark ? mDynamicSchemeDark : mDynamicSchemeLight;
+ ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme;
DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> {
String resourceName = "android:color/system_" + p.first + "_" + suffix;
- int colorValue = p.second.getArgb(scheme);
+ int colorValue = p.second.getArgb(scheme.getMaterialScheme());
overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
null /* configuration */);
});
@@ -694,7 +652,7 @@
private void assignFixedColorsToOverlay(FabricatedOverlay overlay) {
DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> {
String resourceName = "android:color/system_" + p.first;
- int colorValue = p.second.getArgb(mDynamicSchemeLight);
+ int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme());
overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
null /* configuration */);
});
@@ -702,6 +660,7 @@
/**
* Checks if the color scheme in mColorScheme matches the current system palettes.
+ *
* @param managedProfiles List of managed profiles for this user.
*/
private boolean colorSchemeIsApplied(Set<UserHandle> managedProfiles) {
@@ -723,15 +682,18 @@
&& res.getColor(android.R.color.system_neutral2_500, theme)
== mColorScheme.getNeutral2().getS500()
&& res.getColor(android.R.color.system_outline_variant_dark, theme)
- == dynamicColors.outlineVariant().getArgb(mDynamicSchemeDark)
+ == dynamicColors.outlineVariant().getArgb(mDarkColorScheme.getMaterialScheme())
&& res.getColor(android.R.color.system_outline_variant_light, theme)
- == dynamicColors.outlineVariant().getArgb(mDynamicSchemeLight)
+ == dynamicColors.outlineVariant().getArgb(mLightColorScheme.getMaterialScheme())
&& res.getColor(android.R.color.system_primary_container_dark, theme)
- == dynamicColors.primaryContainer().getArgb(mDynamicSchemeDark)
+ == dynamicColors.primaryContainer().getArgb(
+ mDarkColorScheme.getMaterialScheme())
&& res.getColor(android.R.color.system_primary_container_light, theme)
- == dynamicColors.primaryContainer().getArgb(mDynamicSchemeLight)
+ == dynamicColors.primaryContainer().getArgb(
+ mLightColorScheme.getMaterialScheme())
&& res.getColor(android.R.color.system_primary_fixed, theme)
- == dynamicColors.primaryFixed().getArgb(mDynamicSchemeLight))) {
+ == dynamicColors.primaryFixed().getArgb(
+ mLightColorScheme.getMaterialScheme()))) {
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e18bc04..7931fab 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -21,7 +21,6 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ONE_HANDED_ACTIVE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
@@ -260,14 +259,12 @@
public void moveFocusedTaskToFullscreen(int displayId) {
splitScreen.goToFullscreenFromSplit();
}
- });
- splitScreen.registerSplitAnimationListener(new SplitScreen.SplitInvocationListener() {
+
@Override
- public void onSplitAnimationInvoked(boolean animationRunning) {
- mSysUiState.setFlag(SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION, animationRunning)
- .commitUpdate(mDisplayTracker.getDefaultDisplayId());
+ public void setSplitscreenFocus(boolean leftOrTop) {
+ splitScreen.setSplitscreenFocus(leftOrTop);
}
- }, mSysUiMainExecutor);
+ });
}
@VisibleForTesting
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 5882b56..572a6c1 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -113,7 +113,7 @@
android:excludeFromRecents="true"
/>
- <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
+ <activity android:name="com.android.systemui.screenshot.scroll.ScrollViewActivity"
android:exported="false" />
<activity android:name="com.android.systemui.screenshot.RecyclerViewActivity"
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index 31e7bd2..cd2a62d 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -32,4 +32,11 @@
<option name="test-filter-dir" value="/data/data/com.android.systemui.tests" />
<option name="hidden-api-checks" value="false"/>
</test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys"
+ value="/data/user/0/com.android.systemui.tests/files"/>
+ <option name="collect-on-run-ended-only" value="true"/>
+ <option name="clean-up" value="true"/>
+ </metrics_collector>
</configuration>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
index 90587d7..f1dfdf4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerBaseTest.java
@@ -20,16 +20,18 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.keyguard.logging.KeyguardLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractorFactory;
import com.android.systemui.res.R;
@@ -46,6 +48,7 @@
public class KeyguardStatusViewControllerBaseTest extends SysuiTestCase {
+ private KosmosJavaAdapter mKosmos;
@Mock protected KeyguardStatusView mKeyguardStatusView;
@Mock protected KeyguardSliceViewController mKeyguardSliceViewController;
@@ -57,7 +60,6 @@
@Mock protected ScreenOffAnimationController mScreenOffAnimationController;
@Mock protected KeyguardLogger mKeyguardLogger;
@Mock protected KeyguardStatusViewController mControllerMock;
- @Mock protected InteractionJankMonitor mInteractionJankMonitor;
@Mock protected ViewTreeObserver mViewTreeObserver;
@Mock protected DumpManager mDumpManager;
protected FakeKeyguardRepository mFakeKeyguardRepository;
@@ -71,6 +73,7 @@
@Before
public void setup() {
+ mKosmos = new KosmosJavaAdapter(this);
MockitoAnnotations.initMocks(this);
KeyguardInteractorFactory.WithDependencies deps = KeyguardInteractorFactory.create();
@@ -87,7 +90,7 @@
mDozeParameters,
mScreenOffAnimationController,
mKeyguardLogger,
- mInteractionJankMonitor,
+ mKosmos.getInteractionJankMonitor(),
deps.getKeyguardInteractor(),
mDumpManager,
PowerInteractorFactory.create(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 336a97e..fde45d3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -135,6 +135,7 @@
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus;
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
@@ -195,7 +196,7 @@
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
TEST_CARRIER_ID, PROFILE_CLASS_PROVISIONING);
private static final int FINGERPRINT_SENSOR_ID = 1;
-
+ private KosmosJavaAdapter mKosmos;
@Mock
private UserTracker mUserTracker;
@Mock
@@ -240,7 +241,6 @@
private AuthController mAuthController;
@Mock
private TelephonyListenerManager mTelephonyListenerManager;
- @Mock
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private LatencyTracker mLatencyTracker;
@@ -300,6 +300,8 @@
@Before
public void setup() throws RemoteException {
+ mKosmos = new KosmosJavaAdapter(this);
+ mInteractionJankMonitor = mKosmos.getInteractionJankMonitor();
MockitoAnnotations.initMocks(this);
when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index d742da7..1c77351 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -147,6 +147,7 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index b478d5c..c674294 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
@@ -38,7 +39,7 @@
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeViewController;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -65,7 +66,7 @@
@Mock
private ShadeController mShadeController;
@Mock
- private ShadeViewController mShadeViewController;
+ private PanelExpansionInteractor mPanelExpansionInteractor;
@Mock
private Optional<Recents> mRecentsOptional;
@Mock
@@ -87,7 +88,7 @@
mNotificationShadeController,
mKeyguardStateController,
mShadeController,
- () -> mShadeViewController,
+ () -> mPanelExpansionInteractor,
mRecentsOptional,
mDisplayTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 64936862..69cd592 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -476,12 +476,18 @@
mValueAnimator.end();
});
- verify(mSpyController).enableWindowMagnificationInternal(
+ // Verify the method is called in
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+ // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
+ verify(mSpyController, times(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
- //Animating in reverse, so we only check if the start values are greater than current.
+
+ // The animation is playing forwards, so we only check the animated values are greater than
+ // the current one. (Asserting the first captured scale)
assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
+ // The last captured scale equalsto the targetScale when we enable window magnification.
assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
@@ -588,10 +594,10 @@
final float expectedY = (int) (windowBounds.exactCenterY() + expectedOffset
- defaultMagnificationWindowSize / 2);
- // This is called 4 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
+ // This is called 5 times when (1) first creating WindowlessMirrorWindow (2) SurfaceView is
// created and we place the mirrored content as a child of the SurfaceView
- // (3) the animation starts (4) the animation updates
- verify(mTransaction, times(4))
+ // (3) the animation starts (4) the animation updates (5) the animation ends
+ verify(mTransaction, times(5))
.setPosition(any(SurfaceControl.class), eq(expectedX), eq(expectedY));
}
@@ -781,16 +787,18 @@
// wait for animation returns
waitForIdleSync();
- // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate(ValueAnimator)} will only
- // be triggered once in {@link ValueAnimator#end()}
- verify(mSpyController).enableWindowMagnificationInternal(
+ // Verify the method is called in
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+ // {@link Animator.AnimatorListener#onAnimationEnd} once in {@link ValueAnimator#end()}
+ verify(mSpyController, times(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
- //The animation is in verse, so we only check the start values should no be greater than
- // the current one.
+ // The animation is playing backwards, so we only check the animated values should not be
+ // greater than the current one. (Asserting the first captured scale)
assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
+ // The last captured scale is 1.0 when we delete window magnification.
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyStartValue(mCenterXCaptor, Float.NaN);
verifyStartValue(mCenterYCaptor, Float.NaN);
@@ -823,10 +831,15 @@
resetMockObjects();
deleteWindowMagnificationAndWaitAnimating(mWaitAnimationDuration, mAnimationCallback2);
- verify(mSpyController).enableWindowMagnificationInternal(
+ // Verify the method is called in
+ // {@link ValueAnimator.AnimatorUpdateListener#onAnimationUpdate} once and
+ // {@link Animator.AnimatorListener#onAnimationEnd} once when running the animation at
+ // the final duration time.
+ verify(mSpyController, times(2)).enableWindowMagnificationInternal(
mScaleCaptor.capture(),
mCenterXCaptor.capture(), mCenterYCaptor.capture(),
mOffsetXCaptor.capture(), mOffsetYCaptor.capture());
+ // The last captured scale is 1.0 when we delete window magnification.
assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
verifyStartValue(mOffsetXCaptor, 0f);
verifyStartValue(mOffsetYCaptor, 0f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
index 08b49e7..cbdc696 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerWindowlessMagnifierTest.java
@@ -132,7 +132,8 @@
public class WindowMagnificationControllerWindowlessMagnifierTest extends SysuiTestCase {
@Rule
- public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
+ // NOTE: pass 'null' to allow this test advances time on the main thread.
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(/* test= */ null);
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index 96ce3ab..b73e4e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -18,6 +18,8 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.policy.DecorView
import com.android.systemui.SysuiTestCase
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.kosmos.Kosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
@@ -31,7 +33,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@@ -43,7 +44,7 @@
private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
private val attachedViews = mutableSetOf<View>()
- @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+ val interactionJankMonitor = Kosmos().interactionJankMonitor
@get:Rule val rule = MockitoJUnit.rule()
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 416b334..072569d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -399,6 +399,7 @@
@Test
@Ignore("b/302735104")
fun testShowCredentialUI_withCustomBp() {
+ mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
val container = initializeFingerprintContainer(
authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
isUsingContentView = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
index 21b8aca..c79cbab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DeviceStateRepositoryTest.kt
@@ -85,7 +85,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(TEST_FOLDED)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(TEST_FOLDED)
+ )
assertThat(state()).isEqualTo(DeviceState.FOLDED)
}
@@ -95,7 +97,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(TEST_HALF_FOLDED)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(TEST_HALF_FOLDED)
+ )
assertThat(state()).isEqualTo(DeviceState.HALF_FOLDED)
}
@@ -105,7 +109,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(TEST_UNFOLDED)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(TEST_UNFOLDED)
+ )
assertThat(state()).isEqualTo(DeviceState.UNFOLDED)
}
@@ -115,7 +121,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(TEST_REAR_DISPLAY)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(TEST_REAR_DISPLAY)
+ )
assertThat(state()).isEqualTo(DeviceState.REAR_DISPLAY)
}
@@ -125,7 +133,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(TEST_CONCURRENT_DISPLAY)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(TEST_CONCURRENT_DISPLAY)
+ )
assertThat(state()).isEqualTo(DeviceState.CONCURRENT_DISPLAY)
}
@@ -135,7 +145,9 @@
testScope.runTest {
val state = displayState()
- deviceStateManagerListener.value.onStateChanged(123456)
+ deviceStateManagerListener.value.onDeviceStateChanged(
+ getDeviceStateForIdentifier(123456)
+ )
assertThat(state()).isEqualTo(DeviceState.UNKNOWN)
}
@@ -152,6 +164,13 @@
private fun Int.toIntArray() = listOf(this).toIntArray()
+ private fun getDeviceStateForIdentifier(id: Int): android.hardware.devicestate.DeviceState {
+ return android.hardware.devicestate.DeviceState(
+ android.hardware.devicestate.DeviceState.Configuration.Builder(id, /* name= */ "")
+ .build()
+ )
+ }
+
private companion object {
// Used to fake the ids in the test. Note that there is no guarantees different devices will
// have the same ids (that's why the ones in this test start from 41)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 1849245..272b488 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -75,7 +75,6 @@
import androidx.test.filters.SmallTest;
import com.android.internal.foldables.FoldGracePeriodProvider;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.widget.LockPatternUtils;
@@ -181,7 +180,6 @@
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
- private @Mock InteractionJankMonitor mInteractionJankMonitor;
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private @Mock KeyguardTransitions mKeyguardTransitions;
private @Mock ShadeController mShadeController;
@@ -235,8 +233,6 @@
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
when(mPowerManager.isInteractive()).thenReturn(true);
- when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
- when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
final ViewRootImpl testViewRoot = mock(ViewRootImpl.class);
when(testViewRoot.getView()).thenReturn(mock(View.class));
@@ -1245,7 +1241,7 @@
() -> mNotificationShadeDepthController,
mScreenOnCoordinator,
mKeyguardTransitions,
- mInteractionJankMonitor,
+ mKosmos.getInteractionJankMonitor(),
mDreamOverlayStateController,
mJavaAdapter,
mWallpaperRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 7ee8963..1839d8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -37,8 +37,6 @@
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import junit.framework.Assert.assertEquals
-import junit.framework.Assert.assertTrue
-import junit.framework.Assert.fail
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -144,73 +142,6 @@
}
@Test
- fun testSurfaceBehindModel() =
- testScope.runTest {
- val values by collectValues(underTest.surfaceBehindModel)
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN,
- )
- )
- runCurrent()
-
- assertEquals(
- listOf(
- null, // PRIMARY_BOUNCER -> LOCKSCREEN does not have specific view params.
- ),
- values
- )
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.STARTED,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- )
- )
- runCurrent()
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.RUNNING,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- value = 0.01f,
- )
- )
- runCurrent()
-
- transitionRepository.sendTransitionStep(
- TransitionStep(
- transitionState = TransitionState.RUNNING,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.GONE,
- value = 0.99f,
- )
- )
- runCurrent()
-
- assertEquals(3, values.size)
- val model1percent = values[1]
- val model99percent = values[2]
-
- try {
- // We should initially have an alpha of 0f when unlocking, so the surface is not
- // visible
- // while lockscreen UI animates out.
- assertEquals(0f, model1percent!!.alpha)
-
- // By the end it should probably be visible.
- assertTrue(model99percent!!.alpha > 0f)
- } catch (e: NullPointerException) {
- fail("surfaceBehindModel was unexpectedly null.")
- }
- }
-
- @Test
@EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun testReturnToLockscreen_whenBouncerHides() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 170d348..b6b4571 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
@@ -76,6 +78,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testAppliesSplitShadeBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
@@ -88,6 +91,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun fingerprintPropertyInitialized_updatesBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
@@ -99,9 +103,9 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.disableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -121,9 +125,9 @@
}
@Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -143,9 +147,9 @@
}
@Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testAppliesWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -163,4 +167,18 @@
assertThat(blueprint?.id).isEqualTo(WEATHER_CLOCK_BLUEPRINT_ID)
}
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testDoesNotApplySplitShadeBlueprint() {
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ val blueprint by collectLastValue(underTest.blueprint)
+ clockRepository.setCurrentClock(clockController)
+ configurationRepository.onConfigurationChange()
+ runCurrent()
+
+ assertThat(blueprint?.id).isEqualTo(DefaultKeyguardBlueprint.DEFAULT)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 09c56b0..58273d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -107,6 +107,7 @@
@Test
fun addViewsConditionally_migrateFlagOff() {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR)
mSetFlagsRule.disableFlags(AConfigFlags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val constraintLayout = ConstraintLayout(context, null)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index f252163..0322301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -33,6 +33,8 @@
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.policy.SplitShadeStateController
@@ -42,6 +44,7 @@
import kotlin.test.Test
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
@@ -72,6 +75,7 @@
@Mock private lateinit var splitShadeStateController: SplitShadeStateController
@Mock private lateinit var notifsKeyguardInteractor: NotificationsKeyguardInteractor
@Mock private lateinit var areNotificationsFullyHidden: Flow<Boolean>
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
@Before
fun setup() {
@@ -96,13 +100,14 @@
keyguardClockInteractor = KeyguardClockInteractor(keyguardClockRepository)
whenever(notifsKeyguardInteractor.areNotificationsFullyHidden)
.thenReturn(areNotificationsFullyHidden)
+ whenever(shadeInteractor.shadeMode).thenReturn(MutableStateFlow(ShadeMode.Single))
underTest =
KeyguardClockViewModel(
keyguardInteractor,
keyguardClockInteractor,
scope.backgroundScope,
- splitShadeStateController,
- notifsKeyguardInteractor
+ notifsKeyguardInteractor,
+ shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
new file mode 100644
index 0000000..e53cd11
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardClockViewModelWithKosmosTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest = kosmos.keyguardClockViewModel
+ private val testScope = kosmos.testScope
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockCentered_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(true)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_smallClock_smallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_largeClock_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index d75553f..b593def 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -81,7 +81,7 @@
@Test
fun loadRecentTasks_singleTask_returnsTaskAsNotForeground() {
givenRecentTasks(
- createSingleTask(taskId = 1),
+ createSingleTask(taskId = 1, isVisible = true),
)
val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
@@ -90,10 +90,10 @@
}
@Test
- fun loadRecentTasks_multipleTasks_returnsSecondTaskAsForegroundTask() {
+ fun loadRecentTasks_multipleTasks_returnsSecondVisibleTaskAsForegroundTask() {
givenRecentTasks(
createSingleTask(taskId = 1),
- createSingleTask(taskId = 2),
+ createSingleTask(taskId = 2, isVisible = true),
createSingleTask(taskId = 3),
)
@@ -103,10 +103,25 @@
}
@Test
- fun loadRecentTasks_secondTaskIsGrouped_marksBothGroupedTasksAsForeground() {
+ fun loadRecentTasks_multipleTasks_returnsSecondInvisibleTaskAsNotForegroundTask() {
givenRecentTasks(
createSingleTask(taskId = 1),
- createTaskPair(taskId1 = 2, taskId2 = 3),
+ createSingleTask(taskId = 2, isVisible = false),
+ createSingleTask(taskId = 3),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result.map { it.isForegroundTask })
+ .containsExactly(false, false, false)
+ .inOrder()
+ }
+
+ @Test
+ fun loadRecentTasks_secondTaskIsGroupedAndVisible_marksBothGroupedTasksAsForeground() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = true),
createSingleTask(taskId = 4),
)
@@ -117,6 +132,21 @@
.inOrder()
}
+ @Test
+ fun loadRecentTasks_secondTaskIsGroupedAndInvisible_marksBothGroupedTasksAsNotForeground() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3, isVisible = false),
+ createSingleTask(taskId = 4),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result.map { it.isForegroundTask })
+ .containsExactly(false, false, false, false)
+ .inOrder()
+ }
+
@Suppress("UNCHECKED_CAST")
private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
@@ -136,11 +166,23 @@
isForegroundTask = false,
)
- private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
- GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+ private fun createSingleTask(taskId: Int, isVisible: Boolean = false): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId, isVisible))
- private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
- GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+ private fun createTaskPair(
+ taskId1: Int,
+ taskId2: Int,
+ isVisible: Boolean = false
+ ): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSplitTasks(
+ createTaskInfo(taskId1, isVisible),
+ createTaskInfo(taskId2, isVisible),
+ null
+ )
- private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+ private fun createTaskInfo(taskId: Int, isVisible: Boolean = false) =
+ RecentTaskInfo().apply {
+ this.taskId = taskId
+ this.isVisible = isVisible
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
new file mode 100644
index 0000000..261e8c0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
@@ -0,0 +1,249 @@
+/*
+ * 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.systemui.monet
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.util.Log
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.theme.DynamicColors
+import com.google.ux.material.libmonet.hct.Hct
+import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
+import java.io.File
+import java.io.FileWriter
+import java.io.StringWriter
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.OutputKeys
+import javax.xml.transform.TransformerException
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+import kotlin.math.abs
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+import org.w3c.dom.Node
+
+private const val fileHeader =
+ """
+ ~ 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.
+"""
+
+private fun testName(name: String): String {
+ return "Auto generated by: atest ColorSchemeTest#$name"
+}
+
+private const val commentRoles =
+ "Colors used in Android system, from design system. These " +
+ "values can be overlaid at runtime by OverlayManager RROs."
+
+private const val commentOverlay = "This value can be overlaid at runtime by OverlayManager RROs."
+
+private fun commentWhite(paletteName: String): String {
+ return "Lightest shade of the $paletteName color used by the system. White. $commentOverlay"
+}
+
+private fun commentBlack(paletteName: String): String {
+ return "Darkest shade of the $paletteName color used by the system. Black. $commentOverlay"
+}
+
+private fun commentShade(paletteName: String, tone: Int): String {
+ return "Shade of the $paletteName system color at $tone% perceptual luminance (L* in L*a*b* " +
+ "color space). $commentOverlay"
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ColorSchemeTest : SysuiTestCase() {
+ @Test
+ fun generateThemeStyles() {
+ val document = buildDoc<Any>()
+
+ val themes = document.createElement("themes")
+ document.appendWithBreak(themes)
+
+ var hue = 0.0
+ while (hue < 360) {
+ val sourceColor = Hct.from(hue, 50.0, 50.0)
+ val sourceColorHex = sourceColor.toInt().toRGBHex()
+
+ val theme = document.createElement("theme")
+ theme.setAttribute("color", sourceColorHex)
+ themes.appendChild(theme)
+
+ for (styleValue in Style.entries) {
+ if (
+ styleValue == Style.CLOCK ||
+ styleValue == Style.CLOCK_VIBRANT ||
+ styleValue == Style.CONTENT
+ ) {
+ continue
+ }
+
+ val style = document.createElement(styleValue.name.lowercase())
+ val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue)
+
+ style.appendChild(
+ document.createTextNode(
+ listOf(
+ colorScheme.accent1,
+ colorScheme.accent2,
+ colorScheme.accent3,
+ colorScheme.neutral1,
+ colorScheme.neutral2
+ )
+ .flatMap { a -> listOf(*a.allShades.toTypedArray()) }
+ .joinToString(",", transform = Int::toRGBHex)
+ )
+ )
+ theme.appendChild(style)
+ }
+
+ hue += 60
+ }
+
+ saveFile(document, "current_themes.xml")
+ }
+
+ @Test
+ fun generateDefaultValues() {
+ val document = buildDoc<Any>()
+
+ val resources = document.createElement("resources")
+ document.appendWithBreak(resources)
+
+ // shade colors
+ val colorScheme = ColorScheme(GOOGLE_BLUE, false)
+ arrayOf(
+ Triple("accent1", "Primary", colorScheme.accent1),
+ Triple("accent2", "Secondary", colorScheme.accent2),
+ Triple("accent3", "Tertiary", colorScheme.accent3),
+ Triple("neutral1", "Neutral", colorScheme.neutral1),
+ Triple("neutral2", "Secondary Neutral", colorScheme.neutral2)
+ )
+ .forEach {
+ val (paletteName, readable, palette) = it
+ palette.allShadesMapped.entries.forEachIndexed { index, (shade, colorValue) ->
+ val comment =
+ when (index) {
+ 0 -> commentWhite(readable)
+ palette.allShadesMapped.entries.size - 1 -> commentBlack(readable)
+ else -> commentShade(readable, abs(shade / 10 - 100))
+ }
+ resources.createColorEntry("system_${paletteName}_$shade", colorValue, comment)
+ }
+ }
+
+ resources.appendWithBreak(document.createComment(commentRoles), 2)
+
+ // dynamic colors
+ arrayOf(false, true).forEach { isDark ->
+ val suffix = if (isDark) "_dark" else "_light"
+ val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
+ DynamicColors.allDynamicColorsMapped(false).forEach {
+ resources.createColorEntry(
+ "system_${it.first}$suffix",
+ it.second.getArgb(dynamicScheme)
+ )
+ }
+ }
+
+ // fixed colors
+ val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
+ DynamicColors.getFixedColorsMapped(false).forEach {
+ resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
+ }
+
+ saveFile(document, "role_values.xml")
+ }
+
+ // Helper Functions
+
+ private inline fun <reified T> buildDoc(): Document {
+ val functionName = T::class.simpleName + ""
+ val factory = DocumentBuilderFactory.newInstance()
+ val builder = factory.newDocumentBuilder()
+ val document = builder.newDocument()
+
+ document.appendWithBreak(document.createComment(fileHeader))
+ document.appendWithBreak(document.createComment(testName(functionName)))
+
+ return document
+ }
+
+ private fun documentToString(document: Document): String {
+ try {
+ val transformerFactory = TransformerFactory.newInstance()
+ val transformer = transformerFactory.newTransformer()
+ transformer.setOutputProperty(OutputKeys.MEDIA_TYPE, "application/xml")
+ transformer.setOutputProperty(OutputKeys.METHOD, "xml")
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes")
+ transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4")
+
+ val stringWriter = StringWriter()
+ transformer.transform(DOMSource(document), StreamResult(stringWriter))
+ return stringWriter.toString()
+ } catch (e: TransformerException) {
+ throw RuntimeException("Error transforming XML", e)
+ }
+ }
+
+ private fun saveFile(document: Document, fileName: String) {
+ val outPath = context.filesDir.path + "/" + fileName
+ Log.d("ColorSchemeXml", "Artifact $fileName created")
+ val writer = FileWriter(File(outPath))
+ writer.write(documentToString(document))
+ writer.close()
+ }
+}
+
+private fun Element.createColorEntry(name: String, value: Int, comment: String? = null) {
+ val doc = this.ownerDocument
+
+ if (comment != null) {
+ this.appendChild(doc.createComment(comment))
+ }
+
+ val color = doc.createElement("color")
+ this.appendChild(color)
+
+ color.setAttribute("name", name)
+ color.appendChild(doc.createTextNode("#" + value.toRGBHex()))
+}
+
+private fun Node.appendWithBreak(child: Node, lineBreaks: Int = 1): Node {
+ val doc = if (this is Document) this else this.ownerDocument
+ val node = doc.createTextNode("\n".repeat(lineBreaks))
+ this.appendChild(node)
+ return this.appendChild(child)
+}
+
+private fun Int.toRGBHex(): String {
+ return "%06X".format(0xFFFFFF and this)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index dc211303..f88a5a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -19,7 +19,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotSame;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.reset;
@@ -28,6 +27,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -38,9 +38,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.res.R;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.model.SysUiState;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.CommandQueue;
@@ -176,6 +174,6 @@
DeviceStateManager.DeviceStateCallback {
@Override
- public void onStateChanged(int state) { }
+ public void onDeviceStateChanged(DeviceState state) { }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
deleted file mode 100644
index 9ea30d6..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.FakeDisplayTracker;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.stubbing.Answer;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeoutException;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class ActionProxyReceiverTest extends SysuiTestCase {
- @Mock
- private ActivityManagerWrapper mMockActivityManagerWrapper;
- @Mock
- private ScreenshotSmartActions mMockScreenshotSmartActions;
- @Mock
- private PendingIntent mMockPendingIntent;
- @Mock
- private ActivityStarter mActivityStarter;
-
- private Intent mIntent;
- private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
-
- @Before
- public void setup() throws InterruptedException, ExecutionException, TimeoutException {
- MockitoAnnotations.initMocks(this);
- mIntent = new Intent(mContext, ActionProxyReceiver.class)
- .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, mMockPendingIntent);
- }
-
- @Test
- public void testPendingIntentSentWithStatusBar() throws PendingIntent.CanceledException {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
- // ensure that the pending intent call is passed through
- doAnswer((Answer<Object>) invocation -> {
- ((Runnable) invocation.getArgument(0)).run();
- return null;
- }).when(mActivityStarter).executeRunnableDismissingKeyguard(
- any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mActivityStarter).executeRunnableDismissingKeyguard(
- any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
- verify(mMockPendingIntent).send(
- eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
- }
-
- @Test
- public void testSmartActionsNotNotifiedByDefault() {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockScreenshotSmartActions, never())
- .notifyScreenshotAction(anyString(), anyString(), anyBoolean(),
- any(Intent.class));
- }
-
- @Test
- public void testSmartActionsNotifiedIfEnabled() {
- ActionProxyReceiver actionProxyReceiver = constructActionProxyReceiver();
- mIntent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
- String testId = "testID";
- mIntent.putExtra(EXTRA_ID, testId);
-
- actionProxyReceiver.onReceive(mContext, mIntent);
-
- verify(mMockScreenshotSmartActions).notifyScreenshotAction(
- testId, ACTION_TYPE_SHARE, false, null);
- }
-
- private ActionProxyReceiver constructActionProxyReceiver() {
- return new ActionProxyReceiver(
- mMockActivityManagerWrapper,
- mMockScreenshotSmartActions,
- mDisplayTracker,
- mActivityStarter
- );
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
deleted file mode 100644
index d58f47a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_DELETE;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
-import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.screenshot.ScreenshotController.SCREENSHOT_URI_ID;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Environment;
-import android.provider.MediaStore;
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.util.concurrent.Executor;
-
-@RunWith(AndroidTestingRunner.class)
-@SmallTest
-public class DeleteScreenshotReceiverTest extends SysuiTestCase {
-
- @Mock
- private ScreenshotSmartActions mMockScreenshotSmartActions;
- @Mock
- private Executor mMockExecutor;
-
- private DeleteScreenshotReceiver mDeleteScreenshotReceiver;
- private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mDeleteScreenshotReceiver =
- new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mMockExecutor);
- }
-
- @Test
- public void testNoUriProvided() {
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
-
- mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
- verify(mMockExecutor, never()).execute(any(Runnable.class));
- verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
- any(String.class), any(String.class), anyBoolean(),
- any(Intent.class));
- }
-
- @Test
- public void testFileDeleted() {
- DeleteScreenshotReceiver deleteScreenshotReceiver =
- new DeleteScreenshotReceiver(mMockScreenshotSmartActions, mFakeExecutor);
- ContentResolver contentResolver = mContext.getContentResolver();
- final Uri testUri = contentResolver.insert(
- MediaStore.Images.Media.EXTERNAL_CONTENT_URI, getFakeContentValues());
- assertNotNull(testUri);
-
- try {
- Cursor cursor =
- contentResolver.query(testUri, null, null, null, null);
- assertEquals(1, cursor.getCount());
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class)
- .putExtra(SCREENSHOT_URI_ID, testUri.toString());
-
- deleteScreenshotReceiver.onReceive(mContext, intent);
- int runCount = mFakeExecutor.runAllReady();
-
- assertEquals(1, runCount);
- cursor =
- contentResolver.query(testUri, null, null, null, null);
- assertEquals(0, cursor.getCount());
- } finally {
- contentResolver.delete(testUri, null, null);
- }
-
- // ensure smart actions not called by default
- verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(
- any(String.class), any(String.class), anyBoolean(), any(Intent.class));
- }
-
- @Test
- public void testNotifyScreenshotAction() {
- Intent intent = new Intent(mContext, DeleteScreenshotReceiver.class);
- String uriString = "testUri";
- String testId = "testID";
- intent.putExtra(SCREENSHOT_URI_ID, uriString);
- intent.putExtra(EXTRA_ID, testId);
- intent.putExtra(EXTRA_SMART_ACTIONS_ENABLED, true);
-
- mDeleteScreenshotReceiver.onReceive(mContext, intent);
-
- verify(mMockExecutor).execute(any(Runnable.class));
- verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId,
- ACTION_TYPE_DELETE, false, null);
- }
-
- private static ContentValues getFakeContentValues() {
- final ContentValues values = new ContentValues();
- values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
- + File.separator + Environment.DIRECTORY_SCREENSHOTS);
- values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test_screenshot");
- values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
- values.put(MediaStore.MediaColumns.DATE_ADDED, 0);
- values.put(MediaStore.MediaColumns.DATE_MODIFIED, 0);
- return values;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
index 4c8a4b0..aad46139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeSessionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/FakeSessionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.google.common.util.concurrent.Futures.getUnchecked;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
index 670a130..1023260 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureClientTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static org.junit.Assert.assertEquals;
@@ -37,8 +37,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.CaptureResult;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
index 6f081c7..f39f543 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static com.google.common.util.concurrent.Futures.getUnchecked;
import static com.google.common.util.concurrent.Futures.immediateFuture;
@@ -36,7 +36,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient.Session;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
index de97bc3..5699cfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollCaptureFrameworkSmokeTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
index 4c84df2..04aba11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/scroll/ScrollViewActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.app.Activity;
import android.os.Bundle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index c31c625..1ee26db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -196,7 +196,8 @@
new ConfigurationInteractor(configurationRepository),
shadeRepository,
keyguardTransitionInteractor,
- () -> sceneInteractor);
+ () -> sceneInteractor,
+ () -> mKosmos.getFromGoneTransitionInteractor());
CommunalInteractor communalInteractor = mKosmos.getCommunalInteractor();
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index a077164..b9451ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.shade;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -223,7 +222,8 @@
new ConfigurationInteractor(configurationRepository),
mShadeRepository,
keyguardTransitionInteractor,
- () -> sceneInteractor);
+ () -> sceneInteractor,
+ () -> mKosmos.getFromGoneTransitionInteractor());
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
mFromPrimaryBouncerTransitionInteractor =
@@ -289,10 +289,6 @@
when(mNotificationRemoteInputManager.isRemoteInputActive())
.thenReturn(false);
- when(mInteractionJankMonitor.begin(any(), anyInt()))
- .thenReturn(true);
- when(mInteractionJankMonitor.end(anyInt()))
- .thenReturn(true);
when(mPanelView.getParent()).thenReturn(mPanelViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index 103dcb7..dcd000a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -153,6 +154,7 @@
shadeRepository,
keyguardTransitionInteractor,
{ kosmos.sceneInteractor },
+ { kosmos.fromGoneTransitionInteractor },
)
whenever(deviceEntryUdfpsInteractor.isUdfpsSupported).thenReturn(MutableStateFlow(false))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index dcbb93a..d5c4053 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -49,9 +49,8 @@
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeViewController;
-import com.android.systemui.shade.domain.interactor.PanelExpansionInteractorImpl;
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -79,7 +78,7 @@
@Mock private CommandQueue mCommandQueue;
@Mock private QuickSettingsController mQuickSettingsController;
@Mock private ShadeViewController mShadeViewController;
- @Mock private PanelExpansionInteractorImpl mPanelExpansionInteractor;
+ @Mock private PanelExpansionInteractor mPanelExpansionInteractor;
@Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final MetricsLogger mMetricsLogger = new FakeMetricsLogger();
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -129,7 +128,6 @@
mStatusBarHideIconsForBouncerManager,
mPowerManager,
Optional.of(mVibrator),
- new DisableFlagsLogger(),
DEFAULT_DISPLAY,
mCameraLauncherLazy,
mUserTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c8c54dbd..611cf91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -49,6 +49,7 @@
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
@@ -1113,10 +1114,12 @@
}
private void setDeviceState(int state) {
+ DeviceState deviceState = new DeviceState(
+ new DeviceState.Configuration.Builder(state, "TEST").build());
ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
- callbackCaptor.getValue().onStateChanged(state);
+ callbackCaptor.getValue().onDeviceStateChanged(deviceState);
}
private void setGoToSleepStates(int... states) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index 649dc23..5d42d51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.phone
+import android.hardware.devicestate.DeviceState
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.internal.R
@@ -40,52 +41,52 @@
@Before
fun setUp() {
initMocks(this)
- setFoldedStates(DEVICE_STATE_FOLDED)
- setGoToSleepStates(DEVICE_STATE_FOLDED)
+ setFoldedStates(DEVICE_STATE_FOLDED.identifier)
+ setGoToSleepStates(DEVICE_STATE_FOLDED.identifier)
sut = FoldStateListener(mContext, listener)
}
@Test
fun onStateChanged_stateFolded_notifiesWithFoldedAndGoingToSleep() {
- sut.onStateChanged(DEVICE_STATE_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
}
@Test
fun onStateChanged_stateHalfFolded_notifiesWithNotFoldedAndNotGoingToSleep() {
- sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
}
@Test
fun onStateChanged_stateUnfolded_notifiesWithNotFoldedAndNotGoingToSleep() {
- sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
}
@Test
fun onStateChanged_stateUnfoldedThenHalfFolded_notifiesOnce() {
- sut.onStateChanged(DEVICE_STATE_UNFOLDED)
- sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
}
@Test
fun onStateChanged_stateHalfFoldedThenUnfolded_notifiesOnce() {
- sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
- sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_UNFOLDED)
verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
}
@Test
fun onStateChanged_stateHalfFoldedThenFolded_notifiesTwice() {
- sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
- sut.onStateChanged(DEVICE_STATE_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
val inOrder = Mockito.inOrder(listener)
inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
@@ -94,8 +95,8 @@
@Test
fun onStateChanged_stateFoldedThenHalfFolded_notifiesTwice() {
- sut.onStateChanged(DEVICE_STATE_FOLDED)
- sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_FOLDED)
+ sut.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
val inOrder = Mockito.inOrder(listener)
inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
@@ -117,9 +118,18 @@
}
companion object {
- private const val DEVICE_STATE_FOLDED = 123
- private const val DEVICE_STATE_HALF_FOLDED = 456
- private const val DEVICE_STATE_UNFOLDED = 789
+ private val DEVICE_STATE_FOLDED = DeviceState(
+ DeviceState.Configuration.Builder(123 /* id */, "FOLDED" /* name */)
+ .build()
+ )
+ private val DEVICE_STATE_HALF_FOLDED = DeviceState(
+ DeviceState.Configuration.Builder(456 /* id */, "HALF_FOLDED" /* name */)
+ .build()
+ )
+ private val DEVICE_STATE_UNFOLDED = DeviceState(
+ DeviceState.Configuration.Builder(789 /* id */, "UNFOLDED" /* name */)
+ .build()
+ )
private const val FOLDED = true
private const val NOT_FOLDED = false
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 1748cff..d9e9c59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -177,7 +177,8 @@
new ConfigurationInteractor(new FakeConfigurationRepository()),
new FakeShadeRepository(),
keyguardTransitionInteractor,
- () -> mKosmos.getSceneInteractor());
+ () -> mKosmos.getSceneInteractor(),
+ () -> mKosmos.getFromGoneTransitionInteractor());
mViewModel =
new KeyguardStatusBarViewModel(
mTestScope.getBackgroundScope(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 57a89b2..de18913 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarStateControllerImpl
@@ -67,6 +68,8 @@
@Mock
private lateinit var shadeViewController: ShadeViewController
@Mock
+ private lateinit var panelExpansionInteractor: PanelExpansionInteractor
+ @Mock
private lateinit var notifShadeWindowController: NotificationShadeWindowController
@Mock
private lateinit var lightRevealScrim: LightRevealScrim
@@ -87,17 +90,18 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
controller = UnlockedScreenOffAnimationController(
- context,
- wakefulnessLifecycle,
- statusBarStateController,
- { keyguardViewMediator },
- keyguardStateController,
- { dozeParameters },
- globalSettings,
- { notifShadeWindowController },
- interactionJankMonitor,
- powerManager,
- handler = handler
+ context,
+ wakefulnessLifecycle,
+ statusBarStateController,
+ { keyguardViewMediator },
+ keyguardStateController,
+ { dozeParameters },
+ globalSettings,
+ { notifShadeWindowController },
+ interactionJankMonitor,
+ powerManager,
+ { panelExpansionInteractor },
+ handler,
)
controller.initialize(centralSurfaces, shadeViewController, lightRevealScrim)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
index ce47170..c606511 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DevicePostureControllerImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy
+import android.hardware.devicestate.DeviceState
import android.hardware.devicestate.DeviceStateManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
@@ -30,6 +31,7 @@
import com.android.systemui.statusbar.policy.DevicePostureController.SUPPORTED_POSTURES_SIZE
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -72,6 +74,18 @@
com.android.internal.R.array.config_device_state_postures,
deviceStateToPostureMapping
)
+ whenever(deviceStateManager.supportedDeviceStates)
+ .thenReturn(
+ listOf(
+ DEVICE_STATE_CLOSED,
+ DEVICE_STATE_HALF_FOLDED,
+ DEVICE_STATE_OPENED,
+ DEVICE_STATE_FLIPPED,
+ DEVICE_STATE_UNKNOWN,
+ DEVICE_STATE_USE_BASE_STATE
+ )
+ )
+
underTest =
DevicePostureControllerImpl(
context,
@@ -86,20 +100,20 @@
var posture = -1
underTest.addCallback { posture = it }
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_UNKNOWN)
- assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
-
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_CLOSED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_CLOSED)
assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_OPENED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_OPENED)
assertThat(posture).isEqualTo(DEVICE_POSTURE_OPENED)
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_FLIPPED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_FLIPPED)
assertThat(posture).isEqualTo(DEVICE_POSTURE_FLIPPED)
+
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_UNKNOWN)
+ assertThat(posture).isEqualTo(DEVICE_POSTURE_UNKNOWN)
}
@Test
@@ -107,15 +121,26 @@
var posture = -1
underTest.addCallback { posture = it }
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
- // base state change doesn't change the posture
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
+ val physicalProperties =
+ setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+ val updatedState =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_STATE_HALF_FOLDED.identifier,
+ DEVICE_STATE_HALF_FOLDED.name
+ )
+ .setPhysicalProperties(physicalProperties)
+ .build()
+ )
+ // state change with updated physical properties shouldn't cause a posture change
+ deviceStateCallback.value.onDeviceStateChanged(updatedState)
assertThat(posture).isEqualTo(DEVICE_POSTURE_HALF_OPENED)
- // WHEN the display state maps to using the base state, then posture updates
- deviceStateCallback.value.onStateChanged(useBaseStateDeviceState)
+ // WHEN the display state maps to the physical state, then posture updates
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_USE_BASE_STATE)
assertThat(posture).isEqualTo(DEVICE_POSTURE_CLOSED)
}
@@ -124,20 +149,97 @@
var numPostureChanges = 0
underTest.addCallback { numPostureChanges++ }
- deviceStateCallback.value.onStateChanged(DEVICE_POSTURE_HALF_OPENED)
+ deviceStateCallback.value.onDeviceStateChanged(DEVICE_STATE_HALF_FOLDED)
assertThat(numPostureChanges).isEqualTo(1)
- // base state changes doesn't send another posture update since the device state isn't
- // useBaseStateDeviceState
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_CLOSED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_HALF_OPENED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_FLIPPED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_OPENED)
- deviceStateCallback.value.onBaseStateChanged(DEVICE_POSTURE_UNKNOWN)
+ // update to physical properties doesn't send another posture update since the device state
+ // isn't useBaseStateDeviceState
+ deviceStateCallback.value.onDeviceStateChanged(
+ getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_CLOSED)
+ )
+ deviceStateCallback.value.onDeviceStateChanged(
+ getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_HALF_FOLDED)
+ )
+ deviceStateCallback.value.onDeviceStateChanged(
+ getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_OPENED)
+ )
+ deviceStateCallback.value.onDeviceStateChanged(
+ getStateUpdatedPhysicalProperties(DEVICE_STATE_HALF_FOLDED, DEVICE_STATE_UNKNOWN)
+ )
assertThat(numPostureChanges).isEqualTo(1)
}
private fun verifyRegistersForDeviceStateCallback() {
verify(deviceStateManager).registerCallback(eq(mainExecutor), deviceStateCallback.capture())
}
+
+ private fun getStateUpdatedPhysicalProperties(
+ currentState: DeviceState,
+ physicalState: DeviceState
+ ): DeviceState {
+ return DeviceState(
+ DeviceState.Configuration.Builder(currentState.identifier, currentState.name)
+ .setSystemProperties(currentState.configuration.systemProperties)
+ .setPhysicalProperties(physicalState.configuration.physicalProperties)
+ .build()
+ )
+ }
+
+ companion object {
+ val DEVICE_STATE_CLOSED =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_POSTURE_CLOSED /* id */,
+ "CLOSED" /* name */
+ )
+ .setPhysicalProperties(
+ setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED)
+ )
+ .build()
+ )
+ val DEVICE_STATE_HALF_FOLDED =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_POSTURE_HALF_OPENED /* id */,
+ "HALF_FOLDED" /* name */
+ )
+ .setPhysicalProperties(
+ setOf(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN
+ )
+ )
+ .build()
+ )
+ val DEVICE_STATE_OPENED =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_POSTURE_OPENED /* id */,
+ "OPENED" /* name */
+ )
+ .setPhysicalProperties(
+ setOf(DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN)
+ )
+ .build()
+ )
+ val DEVICE_STATE_FLIPPED =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_POSTURE_FLIPPED /* id */,
+ "FLIPPED" /* name */
+ )
+ .build()
+ )
+ val DEVICE_STATE_UNKNOWN =
+ DeviceState(
+ DeviceState.Configuration.Builder(
+ DEVICE_POSTURE_UNKNOWN /* id */,
+ "UNKNOWN" /* name */
+ )
+ .build()
+ )
+ val DEVICE_STATE_USE_BASE_STATE =
+ DeviceState(
+ DeviceState.Configuration.Builder(SUPPORTED_POSTURES_SIZE, "USE_BASE_STATE").build()
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 4ccbd1b..2955162 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.os.UserHandle;
import android.provider.Settings;
@@ -119,11 +120,11 @@
0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateCallback.onStateChanged(1);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
// Settings only exist for state 0 and 1
- mDeviceStateCallback.onStateChanged(2);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
}
@@ -134,10 +135,10 @@
0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED, 1, DEVICE_STATE_ROTATION_LOCK_LOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
- mDeviceStateCallback.onStateChanged(1);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
}
@@ -147,7 +148,7 @@
mFakeRotationPolicy.setRotationLock(true);
// State 2 -> Ignored -> Fall back to state 1 which is unlocked
- mDeviceStateCallback.onStateChanged(2);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
}
@@ -161,7 +162,7 @@
mFakeRotationPolicy.setRotationLock(false);
// State 2 -> Ignored -> Fall back to state 1 which is locked
- mDeviceStateCallback.onStateChanged(2);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
}
@@ -173,7 +174,7 @@
mSettingsManager.onPersistedSettingsChanged();
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -189,10 +190,10 @@
@Test
public void whenDeviceStateSwitchedToIgnoredState_useFallbackSetting() {
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
- mDeviceStateCallback.onStateChanged(2);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
}
@@ -202,10 +203,10 @@
8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(true);
- mDeviceStateCallback.onStateChanged(1);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
- mDeviceStateCallback.onStateChanged(8);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -225,7 +226,7 @@
0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
mFakeRotationPolicy.setRotationLock(false);
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
@@ -241,7 +242,7 @@
initializeSettingsWith(
0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
- mDeviceStateCallback.onStateChanged(0);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(0));
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
/* rotationLocked= */ false,
@@ -262,7 +263,7 @@
0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
- mDeviceStateCallback.onStateChanged(2);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(2));
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
/* rotationLocked= */ true,
@@ -283,8 +284,8 @@
0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
- mDeviceStateCallback.onStateChanged(1);
- mDeviceStateCallback.onStateChanged(8);
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(1));
+ mDeviceStateCallback.onDeviceStateChanged(createDeviceStateForIdentifier(8));
mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
/* rotationLocked= */ true,
@@ -320,6 +321,10 @@
mSettingsManager.onPersistedSettingsChanged();
}
+ private DeviceState createDeviceStateForIdentifier(int id) {
+ return new DeviceState(new DeviceState.Configuration.Builder(id, "" /* name */).build());
+ }
+
private static class FakeRotationPolicy implements RotationPolicyWrapper {
private boolean mRotationLock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index 76913e8..e4b9f10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -24,6 +24,7 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.kosmos.testScope
@@ -63,9 +64,9 @@
ConfigurationInteractor(FakeConfigurationRepository()),
FakeShadeRepository(),
kosmos.keyguardTransitionInteractor,
- ) {
- kosmos.sceneInteractor
- }
+ { kosmos.sceneInteractor },
+ { kosmos.fromGoneTransitionInteractor },
+ )
private val keyguardStatusBarInteractor =
KeyguardStatusBarInteractor(
FakeKeyguardStatusBarRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ab28a2f..ed7c956 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -985,7 +985,7 @@
FabricatedOverlay neutrals = overlays[1];
FabricatedOverlay dynamic = overlays[2];
- final int colorsPerPalette = 12;
+ final int colorsPerPalette = 13;
// Color resources were added for all 3 accent palettes
verify(accents, times(colorsPerPalette * 3))
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 19f31d5..ec27f48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -441,7 +441,8 @@
new ConfigurationInteractor(configurationRepository),
shadeRepository,
keyguardTransitionInteractor,
- () -> sceneInteractor);
+ () -> sceneInteractor,
+ () -> mKosmos.getFromGoneTransitionInteractor());
mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
mFromPrimaryBouncerTransitionInteractor =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index cceb3ff..f65c74f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -19,6 +19,7 @@
import android.platform.test.annotations.EnableFlags
import com.android.systemui.Flags.FLAG_COMPOSE_LOCKSCREEN
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
+import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MEDIA_IN_SCENE_CONTAINER
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
@@ -33,6 +34,7 @@
FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
FLAG_COMPOSE_LOCKSCREEN,
FLAG_MEDIA_IN_SCENE_CONTAINER,
+ FLAG_KEYGUARD_WM_STATE_REFACTOR,
)
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
index 5c5016d..e2b5869 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/jank/InteractionJankMonitorKosmos.kt
@@ -16,9 +16,24 @@
package com.android.systemui.jank
+import android.os.HandlerThread
import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.jank.InteractionJankMonitor.Configuration.Builder
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.spy
-val Kosmos.interactionJankMonitor by Fixture<InteractionJankMonitor> { mock() }
+val Kosmos.interactionJankMonitor by
+ Fixture<InteractionJankMonitor> {
+ spy(InteractionJankMonitor(HandlerThread("InteractionJankMonitor-Kosmos"))).apply {
+ doReturn(true).`when`(this).shouldMonitor()
+ doReturn(true).`when`(this).begin(any(), anyInt())
+ doReturn(true).`when`(this).begin(any<Builder>())
+ doReturn(true).`when`(this).end(anyInt())
+ doReturn(true).`when`(this).cancel(anyInt())
+ doReturn(true).`when`(this).cancel(anyInt(), anyInt())
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index 3893a9b7..00cdc33 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -51,6 +51,7 @@
configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
shadeRepository: FakeShadeRepository = FakeShadeRepository(),
sceneInteractor: SceneInteractor = mock(),
+ fromGoneTransitionInteractor: FromGoneTransitionInteractor = mock(),
powerInteractor: PowerInteractor = PowerInteractorFactory.create().powerInteractor,
): WithDependencies {
// Mock this until the class is replaced by kosmos
@@ -77,6 +78,7 @@
sceneInteractorProvider = { sceneInteractor },
keyguardTransitionInteractor = keyguardTransitionInteractor,
powerInteractor = powerInteractor,
+ fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
index 5140a9f..d61bc9f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorKosmos.kt
@@ -26,7 +26,7 @@
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.statusbar.commandQueue
-val Kosmos.keyguardInteractor by
+val Kosmos.keyguardInteractor: KeyguardInteractor by
Kosmos.Fixture {
KeyguardInteractor(
repository = keyguardRepository,
@@ -38,5 +38,6 @@
shadeRepository = shadeRepository,
keyguardTransitionInteractor = keyguardTransitionInteractor,
sceneInteractorProvider = { sceneInteractor },
+ fromGoneTransitionInteractor = { fromGoneTransitionInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index d84988d..29167d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -27,6 +27,7 @@
surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
fromLockscreenInteractor = fromLockscreenTransitionInteractor,
fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
+ fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 4a85909..60dd48a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -20,8 +20,8 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.splitShadeStateController
val Kosmos.keyguardClockViewModel by
Kosmos.Fixture {
@@ -29,7 +29,7 @@
keyguardInteractor = keyguardInteractor,
keyguardClockInteractor = keyguardClockInteractor,
applicationScope = applicationCoroutineScope,
- splitShadeStateController = splitShadeStateController,
notifsKeyguardInteractor = notificationsKeyguardInteractor,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 8da5dd4..f0fedd2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,7 +20,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.policy.splitShadeStateController
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -29,6 +29,6 @@
interactor = keyguardBlueprintInteractor,
authController = authController,
longPress = keyguardLongPressViewModel,
- splitShadeStateController = splitShadeStateController,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 3fc5af1..e861892 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -34,6 +34,7 @@
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.fromGoneTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromLockscreenTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.fromPrimaryBouncerTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
@@ -92,6 +93,7 @@
val fromPrimaryBouncerTransitionInteractor by lazy {
kosmos.fromPrimaryBouncerTransitionInteractor
}
+ val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
val globalActionsInteractor by lazy { kosmos.globalActionsInteractor }
val sceneDataSource by lazy { kosmos.sceneDataSource }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
index 63f7c97..ea59c0a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeScrollCaptureConnection.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
similarity index 97%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
rename to packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
index 478658e..3b7b158 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/FakeSession.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenshot/scroll/FakeSession.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.screenshot;
+package com.android.systemui.screenshot.scroll;
import static android.util.MathUtils.constrain;
@@ -32,6 +32,8 @@
import android.media.Image;
import android.util.Log;
+import com.android.systemui.screenshot.scroll.ScrollCaptureClient;
+
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
similarity index 77%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
index 9e34fe8..2ed56ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/KeyguardViewOcclusionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/domain/interactor/StatusBarKeyguardViewManagerInteractorKosmos.kt
@@ -16,7 +16,9 @@
package com.android.systemui.statusbar.domain.interactor
+import com.android.systemui.keyguard.domain.interactor.keyguardSurfaceBehindInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.power.domain.interactor.powerInteractor
@@ -26,5 +28,7 @@
keyguardTransitionInteractor = this.keyguardTransitionInteractor,
keyguardOcclusionInteractor = this.keyguardOcclusionInteractor,
powerInteractor = this.powerInteractor,
+ wmLockscreenVisibilityInteractor = windowManagerLockscreenVisibilityInteractor,
+ surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
)
}
diff --git a/services/Android.bp b/services/Android.bp
index 8709692..98a7979 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -119,6 +119,7 @@
":services.companion-sources",
":services.contentcapture-sources",
":services.contentsuggestions-sources",
+ ":services.contextualsearch-sources",
":services.coverage-sources",
":services.credentials-sources",
":services.devicepolicy-sources",
@@ -208,6 +209,7 @@
"services.companion",
"services.contentcapture",
"services.contentsuggestions",
+ "services.contextualsearch",
"services.coverage",
"services.credentials",
"services.devicepolicy",
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2d531e7..4e14dee 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -935,34 +935,11 @@
}
final AccessibilityUserState userState = getUserStateLocked(userId);
- if (Flags.disableContinuousShortcutOnForceStop()) {
- if (doit && onPackagesForceStoppedLocked(packages, userState)) {
- onUserStateChangedLocked(userState);
- return false;
- } else {
- return true;
- }
- } else {
- final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
- while (it.hasNext()) {
- final ComponentName comp = it.next();
- final String compPkg = comp.getPackageName();
- for (String pkg : packages) {
- if (compPkg.equals(pkg)) {
- if (!doit) {
- return true;
- }
- it.remove();
- userState.getBindingServicesLocked().remove(comp);
- userState.getCrashedServicesLocked().remove(comp);
- persistComponentNamesToSettingLocked(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- userState.mEnabledServices, userId);
- onUserStateChangedLocked(userState);
- }
- }
- }
+ if (doit && onPackagesForceStoppedLocked(packages, userState)) {
+ onUserStateChangedLocked(userState);
return false;
+ } else {
+ return true;
}
}
}
@@ -5225,18 +5202,16 @@
private int mSystemUiUid = 0;
AccessibilityDisplayListener(Context context, Handler handler) {
- if (Flags.addWindowTokenWithoutLock()) {
- // Avoid concerns about one thread adding displays while another thread removes
- // them by ensuring the looper is the main looper and the DisplayListener
- // callbacks are always executed on the one main thread.
- final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
- final String errorMessage =
- "AccessibilityDisplayListener must use the main handler";
- if (Build.IS_USERDEBUG || Build.IS_ENG) {
- Preconditions.checkArgument(isMainHandler, errorMessage);
- } else if (!isMainHandler) {
- Slog.e(LOG_TAG, errorMessage);
- }
+ // Avoid concerns about one thread adding displays while another thread removes
+ // them by ensuring the looper is the main looper and the DisplayListener
+ // callbacks are always executed on the one main thread.
+ final boolean isMainHandler = handler.getLooper() == Looper.getMainLooper();
+ final String errorMessage =
+ "AccessibilityDisplayListener must use the main handler";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainHandler, errorMessage);
+ } else if (!isMainHandler) {
+ Slog.e(LOG_TAG, errorMessage);
}
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
@@ -5280,14 +5255,12 @@
@Override
public void onDisplayAdded(int displayId) {
- if (Flags.addWindowTokenWithoutLock()) {
- final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
- final String errorMessage = "onDisplayAdded must be called from the main thread";
- if (Build.IS_USERDEBUG || Build.IS_ENG) {
- Preconditions.checkArgument(isMainThread, errorMessage);
- } else if (!isMainThread) {
- Slog.e(LOG_TAG, errorMessage);
- }
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayAdded must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
}
final Display display = mDisplayManager.getDisplay(displayId);
if (!isValidDisplay(display)) {
@@ -5303,41 +5276,27 @@
mInputFilter.onDisplayAdded(display);
}
AccessibilityUserState userState = getCurrentUserStateLocked();
- if (Flags.addWindowTokenWithoutLock()) {
- services = new ArrayList<>(userState.mBoundServices);
- } else {
- services = userState.mBoundServices;
- if (displayId != Display.DEFAULT_DISPLAY) {
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection boundClient = services.get(i);
- boundClient.addWindowTokenForDisplay(displayId);
- }
- }
- }
+ services = new ArrayList<>(userState.mBoundServices);
updateMagnificationLocked(userState);
updateWindowsForAccessibilityCallbackLocked(userState);
notifyClearAccessibilityCacheLocked();
}
- if (Flags.addWindowTokenWithoutLock()) {
- if (displayId != Display.DEFAULT_DISPLAY) {
- for (int i = 0; i < services.size(); i++) {
- AccessibilityServiceConnection boundClient = services.get(i);
- boundClient.addWindowTokenForDisplay(displayId);
- }
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ for (int i = 0; i < services.size(); i++) {
+ AccessibilityServiceConnection boundClient = services.get(i);
+ boundClient.addWindowTokenForDisplay(displayId);
}
}
}
@Override
public void onDisplayRemoved(int displayId) {
- if (Flags.addWindowTokenWithoutLock()) {
- final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
- final String errorMessage = "onDisplayRemoved must be called from the main thread";
- if (Build.IS_USERDEBUG || Build.IS_ENG) {
- Preconditions.checkArgument(isMainThread, errorMessage);
- } else if (!isMainThread) {
- Slog.e(LOG_TAG, errorMessage);
- }
+ final boolean isMainThread = Looper.getMainLooper().isCurrentThread();
+ final String errorMessage = "onDisplayRemoved must be called from the main thread";
+ if (Build.IS_USERDEBUG || Build.IS_ENG) {
+ Preconditions.checkArgument(isMainThread, errorMessage);
+ } else if (!isMainThread) {
+ Slog.e(LOG_TAG, errorMessage);
}
synchronized (mLock) {
if (!removeDisplayFromList(displayId)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index b90a66a..fb28055 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -220,7 +220,7 @@
@Override
public void onServiceConnected(ComponentName componentName, IBinder service) {
AccessibilityUserState userState = mUserStateWeakReference.get();
- if (userState != null && Flags.addWindowTokenWithoutLock()) {
+ if (userState != null) {
addWindowTokensForAllDisplays();
}
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 4b128f7..9a1d379 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -243,9 +243,6 @@
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
if (!mBoundServices.contains(serviceConnection)) {
- if (!Flags.addWindowTokenWithoutLock()) {
- serviceConnection.addWindowTokensForAllDisplays();
- }
mBoundServices.add(serviceConnection);
mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f69104d..aad9e24 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -136,9 +136,6 @@
return;
}
- if (!Flags.addWindowTokenWithoutLock()) {
- mUiAutomationService.addWindowTokensForAllDisplays();
- }
// UiAutomationService#connectServiceUnknownThread posts to a handler
// so this call should return immediately.
mUiAutomationService.connectServiceUnknownThread();
@@ -286,9 +283,7 @@
// If the serviceInterface is null, the UiAutomation has been shut down on
// another thread.
if (serviceInterface != null) {
- if (Flags.addWindowTokenWithoutLock()) {
- mUiAutomationService.addWindowTokensForAllDisplays();
- }
+ mUiAutomationService.addWindowTokensForAllDisplays();
if (mTrace.isA11yTracingEnabledForTypes(
AccessibilityTrace.FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
mTrace.logTrace("UiAutomationService.connectServiceUnknownThread",
diff --git a/services/contextualsearch/Android.bp b/services/contextualsearch/Android.bp
new file mode 100644
index 0000000..b4dd20e
--- /dev/null
+++ b/services/contextualsearch/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.contextualsearch-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.contextualsearch",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.contextualsearch-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
new file mode 100644
index 0000000..b28bc1f
--- /dev/null
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contextualsearch;
+
+import static android.Manifest.permission.ACCESS_CONTEXTUAL_SEARCH;
+import static android.content.Context.CONTEXTUAL_SEARCH_SERVICE;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
+import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.ActivityOptions;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.app.contextualsearch.ContextualSearchManager;
+import android.app.contextualsearch.ContextualSearchState;
+import android.app.contextualsearch.IContextualSearchCallback;
+import android.app.contextualsearch.IContextualSearchManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelableException;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Log;
+import android.util.Slog;
+import android.window.ScreenCapture;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityAssistInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class ContextualSearchManagerService extends SystemService {
+
+ private static final int MSG_RESET_TEMPORARY_PACKAGE = 0;
+ private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+ private final Context mContext;
+ private final ActivityTaskManagerInternal mAtmInternal;
+ private final WindowManagerInternal mWmInternal;
+ private final DevicePolicyManagerInternal mDpmInternal;
+
+ private Handler mTemporaryHandler;
+
+ @GuardedBy("this")
+ private String mTemporaryPackage = null;
+ private static final String TAG = ContextualSearchManagerService.class.getSimpleName();
+
+ public ContextualSearchManagerService(@NonNull Context context) {
+ super(context);
+ if (DEBUG_USER) Log.d(TAG, "ContextualSearchManagerService created");
+ mContext = context;
+ mAtmInternal = Objects.requireNonNull(
+ LocalServices.getService(ActivityTaskManagerInternal.class));
+ mWmInternal = Objects.requireNonNull(LocalServices.getService(WindowManagerInternal.class));
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(CONTEXTUAL_SEARCH_SERVICE, new ContextualSearchManagerStub());
+ }
+
+ void resetTemporaryPackage() {
+ synchronized (this) {
+ enforceOverridingPermission("resetTemporaryPackage");
+ if (mTemporaryHandler != null) {
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
+ mTemporaryHandler = null;
+ }
+ if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage reset.");
+ mTemporaryPackage = null;
+ }
+ }
+
+ void setTemporaryPackage(@NonNull String temporaryPackage, int durationMs) {
+ synchronized (this) {
+ enforceOverridingPermission("setTemporaryPackage");
+ final int maxDurationMs = MAX_TEMP_PACKAGE_DURATION_MS;
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_RESET_TEMPORARY_PACKAGE) {
+ synchronized (this) {
+ resetTemporaryPackage();
+ }
+ } else {
+ Slog.wtf(TAG, "invalid handler msg: " + msg);
+ }
+ }
+ };
+ } else {
+ mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_PACKAGE);
+ }
+ mTemporaryPackage = temporaryPackage;
+ mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_PACKAGE, durationMs);
+ if (DEBUG_USER) Log.d(TAG, "mTemporaryPackage set to " + mTemporaryPackage);
+ }
+ }
+
+ private Intent getResolvedLaunchIntent() {
+ synchronized (this) {
+ // If mTemporaryPackage is not null, use it to get the ContextualSearch intent.
+ String csPkgName = mTemporaryPackage != null ? mTemporaryPackage : mContext
+ .getResources().getString(R.string.config_defaultContextualSearchPackageName);
+ if (csPkgName.isEmpty()) {
+ // Return null if csPackageName is not specified.
+ return null;
+ }
+ Intent launchIntent = new Intent(
+ ContextualSearchManager.ACTION_LAUNCH_CONTEXTUAL_SEARCH);
+ launchIntent.setPackage(csPkgName);
+ ResolveInfo resolveInfo = mContext.getPackageManager().resolveActivity(
+ launchIntent, MATCH_FACTORY_ONLY);
+ if (resolveInfo == null) {
+ return null;
+ }
+ ComponentName componentName = resolveInfo.getComponentInfo().getComponentName();
+ if (componentName == null) {
+ return null;
+ }
+ launchIntent.setComponent(componentName);
+ return launchIntent;
+ }
+ }
+
+ private Intent getContextualSearchIntent(int entrypoint, IBinder mToken) {
+ final Intent launchIntent = getResolvedLaunchIntent();
+ if (launchIntent == null) {
+ return null;
+ }
+
+ if (DEBUG_USER) Log.d(TAG, "Launch component: " + launchIntent.getComponent());
+ launchIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION
+ | FLAG_ACTIVITY_NO_USER_ACTION);
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_ENTRYPOINT, entrypoint);
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_TOKEN, mToken);
+ boolean isAssistDataAllowed = mAtmInternal.isAssistDataAllowed();
+ final List<ActivityAssistInfo> records = mAtmInternal.getTopVisibleActivities();
+ ArrayList<String> visiblePackageNames = new ArrayList<>();
+ boolean isManagedProfileVisible = false;
+ for (ActivityAssistInfo record : records) {
+ // Add the package name to the list only if assist data is allowed.
+ if (isAssistDataAllowed) {
+ visiblePackageNames.add(record.getComponentName().getPackageName());
+ }
+ if (mDpmInternal != null
+ && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+ isManagedProfileVisible = true;
+ }
+ }
+ final ScreenCapture.ScreenshotHardwareBuffer shb;
+ if (mWmInternal != null) {
+ shb = mWmInternal.takeAssistScreenshot();
+ } else {
+ shb = null;
+ }
+ final Bitmap bm = shb != null ? shb.asBitmap() : null;
+ // Now that everything is fetched, putting it in the launchIntent.
+ if (bm != null) {
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_FLAG_SECURE_FOUND,
+ shb.containsSecureLayers());
+ // Only put the screenshot if assist data is allowed
+ if (isAssistDataAllowed) {
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_SCREENSHOT, bm.asShared());
+ }
+ }
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_IS_MANAGED_PROFILE_VISIBLE,
+ isManagedProfileVisible);
+ // Only put the list of visible package names if assist data is allowed
+ if (isAssistDataAllowed) {
+ launchIntent.putExtra(ContextualSearchManager.EXTRA_VISIBLE_PACKAGE_NAMES,
+ visiblePackageNames);
+ }
+ return launchIntent;
+ }
+
+ @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS)
+ private int invokeContextualSearchIntent(Intent launchIntent) {
+ // Contextual search starts with a frozen screen - so we launch without
+ // any system animations or starting window.
+ final ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(mContext,
+ /* enterResId= */ 0, /* exitResId= */ 0, null, null, null);
+ opts.setDisableStartingWindow(true);
+ return mAtmInternal.startActivityWithScreenshot(launchIntent,
+ mContext.getPackageName(), Binder.getCallingUid(), Binder.getCallingPid(), null,
+ opts.toBundle(), Binder.getCallingUserHandle().getIdentifier());
+ }
+
+ private void enforcePermission(@NonNull final String func) {
+ Context ctx = getContext();
+ if (!(ctx.checkCallingPermission(ACCESS_CONTEXTUAL_SEARCH) == PERMISSION_GRANTED
+ || isCallerTemporary())) {
+ String msg = "Permission Denial: Cannot call " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+ throw new SecurityException(msg);
+ }
+ }
+
+ private void enforceOverridingPermission(@NonNull final String func) {
+ if (!(Binder.getCallingUid() == Process.SHELL_UID
+ || Binder.getCallingUid() == Process.ROOT_UID
+ || Binder.getCallingUid() == Process.SYSTEM_UID)) {
+ String msg = "Permission Denial: Cannot override Contextual Search. Called " + func
+ + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+ throw new SecurityException(msg);
+ }
+ }
+
+ private boolean isCallerTemporary() {
+ synchronized (this) {
+ return mTemporaryPackage != null
+ && mTemporaryPackage.equals(
+ getContext().getPackageManager().getNameForUid(Binder.getCallingUid()));
+ }
+ }
+
+ private class ContextualSearchManagerStub extends IContextualSearchManager.Stub {
+ private @Nullable IBinder mToken;
+
+ @Override
+ public void startContextualSearch(int entrypoint) {
+ synchronized (this) {
+ if (DEBUG_USER) Log.d(TAG, "startContextualSearch");
+ enforcePermission("startContextualSearch");
+ mToken = new Binder();
+ // We get the launch intent with the system server's identity because the system
+ // server has READ_FRAME_BUFFER permission to get the screenshot and because only
+ // the system server can invoke non-exported activities.
+ Binder.withCleanCallingIdentity(() -> {
+ Intent launchIntent = getContextualSearchIntent(entrypoint, mToken);
+ if (launchIntent != null) {
+ int result = invokeContextualSearchIntent(launchIntent);
+ if (DEBUG_USER) Log.d(TAG, "Launch result: " + result);
+ }
+ });
+ }
+ }
+
+ @Override
+ public void getContextualSearchState(
+ @NonNull IBinder token,
+ @NonNull IContextualSearchCallback callback) {
+ if (DEBUG_USER) {
+ Log.i(TAG, "getContextualSearchState token: " + token + ", callback: " + callback);
+ }
+ if (mToken == null || !mToken.equals(token)) {
+ if (DEBUG_USER) {
+ Log.e(TAG, "getContextualSearchState: invalid token, returning error");
+ }
+ try {
+ callback.onError(
+ new ParcelableException(new IllegalArgumentException("Invalid token")));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not invoke onError callback", e);
+ }
+ return;
+ }
+ mToken = null;
+ // Process data request
+ try {
+ callback.onResult(new ContextualSearchState(null, null, Bundle.EMPTY));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not invoke onResult callback", e);
+ }
+ }
+
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new ContextualSearchManagerShellCommand(ContextualSearchManagerService.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+ }
+}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
new file mode 100644
index 0000000..5777e1d
--- /dev/null
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.contextualsearch;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+public class ContextualSearchManagerShellCommand extends ShellCommand {
+
+ private final ContextualSearchManagerService mService;
+
+ ContextualSearchManagerShellCommand(@NonNull ContextualSearchManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "set": {
+ final String what = getNextArgRequired();
+ switch (what) {
+ case "temporary-package": {
+ String packageName = getNextArg();
+ if (packageName == null) {
+ mService.resetTemporaryPackage();
+ pw.println("ContextualSearchManagerService reset.");
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryPackage(packageName, duration);
+ pw.println("ContextualSearchManagerService temporarily set to "
+ + packageName + " for " + duration + "ms");
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("ContextualSearchService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-package [PACKAGE_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the Contextual Search "
+ + "implementation.");
+ pw.println(" To reset, call without any arguments.");
+ pw.println("");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0012b3d..133a77d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4658,6 +4658,8 @@
INVALID_UID /* sdkSandboxClientAppUid */,
null /* sdkSandboxClientAppPackage */,
false /* inSharedIsolatedProcess */);
+ r.foregroundId = fgsDelegateOptions.mClientNotificationId;
+ r.foregroundNoti = fgsDelegateOptions.mClientNotification;
res.setService(r);
smap.mServicesByInstanceName.put(cn, r);
smap.mServicesByIntent.put(filter, r);
@@ -8171,7 +8173,7 @@
* @param targetProcess the process of the service to start.
* @return {@link ReasonCode}
*/
- private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
+ @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
BackgroundStartPrivileges backgroundStartPrivileges) {
int ret = REASON_DENIED;
@@ -9046,6 +9048,10 @@
});
}
signalForegroundServiceObserversLocked(r);
+ if (r.foregroundId != 0 && r.foregroundNoti != null) {
+ r.foregroundNoti.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ r.postNotification(true);
+ }
return true;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index ff83797..272e84b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -51,6 +51,7 @@
import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -436,6 +437,18 @@
private static final String KEY_MAX_SERVICE_CONNECTIONS_PER_PROCESS =
"max_service_connections_per_process";
+ private static final String KEY_PROC_STATE_DEBUG_UIDS = "proc_state_debug_uids";
+
+ /**
+ * UIDs we want to print detailed info in OomAdjuster.
+ * It's only used for debugging, and it's almost never updated, so we just create a new
+ * array when it's changed to avoid synchronization.
+ */
+ volatile SparseBooleanArray mProcStateDebugUids = new SparseBooleanArray(0);
+ volatile boolean mEnableProcStateStacktrace = false;
+ volatile int mProcStateDebugSetProcStateDelay = 0;
+ volatile int mProcStateDebugSetUidStateDelay = 0;
+
// Maximum number of cached processes we will allow.
public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;
@@ -1339,6 +1352,9 @@
case KEY_PSS_TO_RSS_THRESHOLD_MODIFIER:
updatePssToRssThresholdModifier();
break;
+ case KEY_PROC_STATE_DEBUG_UIDS:
+ updateProcStateDebugUids();
+ break;
default:
updateFGSPermissionEnforcementFlagsIfNecessary(name);
break;
@@ -2039,6 +2055,76 @@
DEFAULT_MAX_PREVIOUS_TIME);
}
+ private void updateProcStateDebugUids() {
+ final String val = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_PROC_STATE_DEBUG_UIDS,
+ "").trim();
+
+ // Parse KEY_PROC_STATE_DEBUG_UIDS as comma-separated values. Each values can be:
+ // Number: Enable debugging on the given UID.
+ // "stack": Enable stack trace when updating proc/uid-states.s
+ // "u" + delay-ms: Enable sleep when updating uid-state
+ // "p" + delay-ms: Enable sleep when updating procstate
+ //
+ // Example:
+ // device_config put activity_manager proc_state_debug_uids '10177,10202,stack,p500,u100'
+ // means:
+ // - Monitor UID 10177 and 10202
+ // - Also enable stack trace
+ // - Sleep 500 ms when updating the procstate.
+ // - Sleep 100 ms when updating the UID state.
+
+ mEnableProcStateStacktrace = false;
+ mProcStateDebugSetProcStateDelay = 0;
+ mProcStateDebugSetUidStateDelay = 0;
+ if (val.length() == 0) {
+ mProcStateDebugUids = new SparseBooleanArray(0);
+ return;
+ }
+ final String[] uids = val.split(",");
+
+ final SparseBooleanArray newArray = new SparseBooleanArray(0);
+
+ for (String token : uids) {
+ if (token.length() == 0) {
+ continue;
+ }
+ // "stack" -> enable stacktrace.
+ if ("stack".equals(token)) {
+ mEnableProcStateStacktrace = true;
+ continue;
+ }
+ boolean isUid = true;
+ char prefix = token.charAt(0);
+ if ('a' <= prefix && prefix <= 'z') {
+ // If the token starts with an alphabet, it's not a UID.
+ isUid = false;
+ token = token.substring(1);
+ }
+
+ int value = -1;
+ try {
+ value = Integer.parseInt(token.trim());
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Invalid number " + token + " in " + val);
+ continue;
+ }
+ if (isUid) {
+ newArray.put(value, true);
+ } else if (prefix == 'p') {
+ // Enable delay in set-proc-state
+ mProcStateDebugSetProcStateDelay = value;
+ } else if (prefix == 'u') {
+ // Enable delay in set-uid-state
+ mProcStateDebugSetUidStateDelay = value;
+ } else {
+ Slog.w(TAG, "Invalid prefix " + prefix + " in " + val);
+ }
+ }
+ mProcStateDebugUids = newArray;
+ }
+
private void updateMinAssocLogDuration() {
MIN_ASSOC_LOG_DURATION = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MIN_ASSOC_LOG_DURATION,
@@ -2178,6 +2264,28 @@
mDefaultPssToRssThresholdModifier);
}
+ boolean shouldDebugUidForProcState(int uid) {
+ SparseBooleanArray ar = mProcStateDebugUids;
+ final var size = ar.size();
+ if (size == 0) { // Most common case.
+ return false;
+ }
+ // If the array is small (also common), avoid the binary search.
+ if (size <= 8) {
+ for (int i = 0; i < size; i++) {
+ if (ar.keyAt(i) == uid) {
+ return ar.valueAt(i);
+ }
+ }
+ return false;
+ }
+ return ar.get(uid, false);
+ }
+
+ boolean shouldEnableProcStateDebug() {
+ return mProcStateDebugUids.size() > 0;
+ }
+
@NeverCompile // Avoid size overhead of debugging code.
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2393,5 +2501,12 @@
pw.print(" OOMADJ_UPDATE_QUICK="); pw.println(OOMADJ_UPDATE_QUICK);
pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION=");
pw.println(mEnableWaitForFinishAttachApplication);
+
+ synchronized (mProcStateDebugUids) {
+ pw.print(" "); pw.print(KEY_PROC_STATE_DEBUG_UIDS);
+ pw.print("="); pw.println(mProcStateDebugUids);
+ pw.print(" uid-state-delay="); pw.println(mProcStateDebugSetUidStateDelay);
+ pw.print(" proc-state-delay="); pw.println(mProcStateDebugSetProcStateDelay);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 3e633cc..ddf1d5f 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -211,11 +211,17 @@
if (!mEnabled) {
return;
}
- if (!mInProgRecords.containsKey(id)) {
+ int index = mInProgRecords.indexOfKey(id);
+ if (index < 0) {
return;
}
- mInProgRecords.get(id).setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
- mInProgRecords.remove(id);
+ ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ if (info == null) {
+ mInProgRecords.removeAt(index);
+ return;
+ }
+ info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
+ mInProgRecords.removeAt(index);
}
}
@@ -224,16 +230,24 @@
if (!mEnabled) {
return;
}
- if (!mInProgRecords.containsKey(id)) {
+ int index = mInProgRecords.indexOfKey(id);
+ if (index < 0) {
return;
}
- if (app != null) {
- ApplicationStartInfo info = mInProgRecords.get(id);
- info.setStartType((int) temperature);
- addBaseFieldsFromProcessRecord(info, app);
- mInProgRecords.put(id, addStartInfoLocked(info));
+ ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ if (info == null || app == null) {
+ mInProgRecords.removeAt(index);
+ return;
+ }
+ info.setStartType((int) temperature);
+ addBaseFieldsFromProcessRecord(info, app);
+ ApplicationStartInfo newInfo = addStartInfoLocked(info);
+ if (newInfo == null) {
+ // newInfo can be null if records are added before load from storage is
+ // complete. In this case the newly added record will be lost.
+ mInProgRecords.removeAt(index);
} else {
- mInProgRecords.remove(id);
+ mInProgRecords.setValueAt(index, newInfo);
}
}
}
@@ -243,12 +257,17 @@
if (!mEnabled) {
return;
}
- if (!mInProgRecords.containsKey(id)) {
+ int index = mInProgRecords.indexOfKey(id);
+ if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.get(id);
+ ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ if (info == null) {
+ mInProgRecords.removeAt(index);
+ return;
+ }
info.setStartupState(ApplicationStartInfo.STARTUP_STATE_ERROR);
- mInProgRecords.remove(id);
+ mInProgRecords.removeAt(index);
}
}
@@ -258,10 +277,15 @@
if (!mEnabled) {
return;
}
- if (!mInProgRecords.containsKey(id)) {
+ int index = mInProgRecords.indexOfKey(id);
+ if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.get(id);
+ ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ if (info == null) {
+ mInProgRecords.removeAt(index);
+ return;
+ }
info.setStartupState(ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN);
info.setLaunchMode(launchMode);
checkCompletenessAndCallback(info);
@@ -273,13 +297,18 @@
if (!mEnabled) {
return;
}
- if (!mInProgRecords.containsKey(id)) {
+ int index = mInProgRecords.indexOfKey(id);
+ if (index < 0) {
return;
}
- ApplicationStartInfo info = mInProgRecords.get(id);
+ ApplicationStartInfo info = mInProgRecords.valueAt(index);
+ if (info == null) {
+ mInProgRecords.removeAt(index);
+ return;
+ }
info.addStartupTimestamp(ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN,
timestampNanos);
- mInProgRecords.remove(id);
+ mInProgRecords.removeAt(index);
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 8038732..b142781 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -135,3 +135,9 @@
# Caller information to clear application data
30120 am_clear_app_data_caller (pid|1),(uid|1),(package|3)
+
+30111 am_uid_state_changed (UID|1|5),(Seq|1|5),(UidState|1|5),(OldUidState|1|5),(Capability|1|5),(OldCapability|1|5),(Flags|1|5),(reason|3)
+30112 am_proc_state_changed (UID|1|5),(PID|1|5),(Seq|1|5),(ProcState|1|5),(OldProcState|1|5),(OomAdj|1|5),(OldOomAdj|1|5),(reason|3)
+
+# "Misc" events. See OomAdjusterDebugLogger.java
+30113 am_oom_adj_misc (Event|1|5),(UID|1|5),(PID|1|5),(Seq|1|5),(Arg1|1|5),(Arg2|1|5),(reason|3)
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7f6d62c..1a7629f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -401,6 +401,14 @@
@GuardedBy("mService")
private boolean mPendingFullOomAdjUpdate = false;
+ /**
+ * Most recent reason string. We update it in sync with the trace.
+ */
+ @OomAdjReason
+ protected int mLastReason;
+
+ private final OomAdjusterDebugLogger mLogger;
+
/** Overrideable by a test */
@VisibleForTesting
protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId,
@@ -433,6 +441,8 @@
mCachedAppOptimizer = new CachedAppOptimizer(mService);
mCacheOomRanker = new CacheOomRanker(service);
+ mLogger = new OomAdjusterDebugLogger(this, mService.mConstants);
+
mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> {
final int pid = msg.arg1;
final int group = msg.arg2;
@@ -636,6 +646,7 @@
protected boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
+ mLastReason = oomAdjReason;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
mAdjSeq++;
@@ -916,6 +927,7 @@
protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
final ProcessRecord topApp = mService.getTopApp();
+ mLastReason = oomAdjReason;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
@@ -958,6 +970,7 @@
}
}
+ mLastReason = oomAdjReason;
if (startProfiling) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
@@ -1491,6 +1504,7 @@
|| uidRec.isSetAllowListed() != uidRec.isCurAllowListed()
|| uidRec.getProcAdjChanged()) {
int uidChange = 0;
+ final boolean shouldLog = mLogger.shouldLog(uidRec.getUid());
if (DEBUG_UID_OBSERVERS) {
Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec
+ ": proc state from " + uidRec.getSetProcState() + " to "
@@ -1511,14 +1525,21 @@
|| uidRec.isSetAllowListed()
|| uidRec.getLastBackgroundTime() == 0) {
uidRec.setLastBackgroundTime(nowElapsed);
+ if (shouldLog) {
+ mLogger.logSetLastBackgroundTime(uidRec.getUid(), nowElapsed);
+ }
if (mService.mDeterministicUidIdle
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
// Note: the background settle time is in elapsed realtime, while
// the handler time base is uptime. All this means is that we may
// stop background uids later than we had intended, but that only
// happens because the device was sleeping so we are okay anyway.
+ if (shouldLog) {
+ mLogger.logScheduleUidIdle1(uidRec.getUid(),
+ mConstants.BACKGROUND_SETTLE_TIME);
+ }
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
- mConstants.BACKGROUND_SETTLE_TIME);
+ mConstants.BACKGROUND_SETTLE_TIME); // XXX
}
}
if (uidRec.isIdle() && !uidRec.isSetIdle()) {
@@ -1536,6 +1557,9 @@
}
uidRec.setLastBackgroundTime(0);
uidRec.setLastIdleTime(0);
+ if (shouldLog) {
+ mLogger.logClearLastBackgroundTime(uidRec.getUid());
+ }
}
final boolean wasCached = uidRec.getSetProcState()
> ActivityManager.PROCESS_STATE_RECEIVER;
@@ -1555,11 +1579,25 @@
if (uidRec.getProcAdjChanged()) {
uidChange |= UidRecord.CHANGE_PROCADJ;
}
+ int oldProcState = uidRec.getSetProcState();
+ int oldCapability = uidRec.getSetCapability();
uidRec.setSetProcState(uidRec.getCurProcState());
uidRec.setSetCapability(uidRec.getCurCapability());
uidRec.setSetAllowListed(uidRec.isCurAllowListed());
uidRec.setSetIdle(uidRec.isIdle());
uidRec.clearProcAdjChanged();
+ if (shouldLog
+ && ((uidRec.getSetProcState() != oldProcState)
+ || (uidRec.getSetCapability() != oldCapability))) {
+ int flags = 0;
+ if (uidRec.isSetAllowListed()) {
+ flags |= 1;
+ }
+ mLogger.logUidStateChanged(uidRec.getUid(),
+ uidRec.getSetProcState(), oldProcState,
+ uidRec.getSetCapability(), oldCapability,
+ flags);
+ }
if ((uidChange & UidRecord.CHANGE_PROCSTATE) != 0
|| (uidChange & UidRecord.CHANGE_CAPABILITY) != 0) {
mService.mAtmInternal.onUidProcStateChanged(
@@ -3300,6 +3338,7 @@
mCachedAppOptimizer.onOomAdjustChanged(state.getSetAdj(), state.getCurAdj(), app);
}
+ final int oldOomAdj = state.getSetAdj();
if (state.getCurAdj() != state.getSetAdj()) {
ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj());
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
@@ -3457,6 +3496,7 @@
mService.mAppProfiler.updateNextPssTimeLPf(
state.getCurProcState(), app.mProfile, now, forceUpdatePssTime);
}
+ int oldProcState = state.getSetProcState();
if (state.getSetProcState() != state.getCurProcState()) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.uid) {
String msg = "Proc state change of " + app.processName
@@ -3556,6 +3596,11 @@
// Kick off the delayed checkup message if needed.
if (mService.mDeterministicUidIdle
|| !mService.mHandler.hasMessages(IDLE_UIDS_MSG)) {
+ if (mLogger.shouldLog(app.uid)) {
+ mLogger.logScheduleUidIdle2(
+ uidRec.getUid(), app.getPid(),
+ mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
+ }
mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
mConstants.mKillBgRestrictedAndCachedIdleSettleTimeMs);
}
@@ -3563,6 +3608,12 @@
}
state.setSetCached(state.isCached());
state.setSetNoKillOnBgRestrictedAndIdle(state.shouldNotKillOnBgRestrictedAndIdle());
+ if (((oldProcState != state.getSetProcState()) || (oldOomAdj != state.getSetAdj()))
+ && mLogger.shouldLog(app.uid)) {
+ mLogger.logProcStateChanged(app.uid, app.getPid(),
+ state.getSetProcState(), oldProcState,
+ state.getSetAdj(), oldOomAdj);
+ }
return success;
}
@@ -3704,6 +3755,7 @@
if (mService.mLocalPowerManager != null) {
mService.mLocalPowerManager.startUidChanges();
}
+ boolean shouldLogMisc = false;
for (int i = N - 1; i >= 0; i--) {
final UidRecord uidRec = mActiveUids.valueAt(i);
final long bgTime = uidRec.getLastBackgroundTime();
@@ -3721,6 +3773,9 @@
if (nextTime == 0 || nextTime > bgTime) {
nextTime = bgTime;
}
+ if (mLogger.shouldLog(uidRec.getUid())) {
+ shouldLogMisc = true;
+ }
}
}
}
@@ -3742,8 +3797,11 @@
}
}
if (nextTime > 0) {
- mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG,
- nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed);
+ long delay = nextTime + mConstants.BACKGROUND_SETTLE_TIME - nowElapsed;
+ if (shouldLogMisc) {
+ mLogger.logScheduleUidIdle3(delay);
+ }
+ mService.mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, delay);
}
}
diff --git a/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java b/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java
new file mode 100644
index 0000000..1294a4d
--- /dev/null
+++ b/services/core/java/com/android/server/am/OomAdjusterDebugLogger.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.am;
+
+import android.app.StackTrace;
+import android.util.Slog;
+
+/**
+ * Helper for writing debug log about proc/uid state changes.
+ */
+class OomAdjusterDebugLogger {
+ // Use the "am_" tag to make it similar to event logs.
+ private static final String STACK_TRACE_TAG = "am_stack";
+
+ private final OomAdjuster mOomAdjuster;
+ private final ActivityManagerConstants mConstants;
+
+ private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_1 = 1;
+ private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_2 = 2;
+ private static final int MISC_SCHEDULE_IDLE_UIDS_MSG_3 = 3;
+
+ private static final int MISC_SET_LAST_BG_TIME = 10;
+ private static final int MISC_CLEAR_LAST_BG_TIME = 11;
+
+ OomAdjusterDebugLogger(OomAdjuster oomAdjuster, ActivityManagerConstants constants) {
+ mOomAdjuster = oomAdjuster;
+ mConstants = constants;
+ }
+
+ boolean shouldLog(int uid) {
+ return mConstants.shouldDebugUidForProcState(uid);
+ }
+
+ private void maybeLogStacktrace(String msg) {
+ if (!mConstants.mEnableProcStateStacktrace) {
+ return;
+ }
+ Slog.i(STACK_TRACE_TAG,
+ msg + ": " + OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason),
+ new StackTrace("Called here"));
+ }
+
+ private void maybeSleep(int millis) {
+ if (millis == 0) {
+ return;
+ }
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ void logUidStateChanged(int uid, int uidstate, int olduidstate,
+ int capability, int oldcapability, int flags) {
+ EventLogTags.writeAmUidStateChanged(
+ uid, mOomAdjuster.mAdjSeq, uidstate, olduidstate, capability, oldcapability, flags,
+ OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+ maybeLogStacktrace("uidStateChanged");
+ maybeSleep(mConstants.mProcStateDebugSetUidStateDelay);
+ }
+
+ void logProcStateChanged(int uid, int pid, int procstate, int oldprocstate,
+ int oomadj, int oldoomadj) {
+ EventLogTags.writeAmProcStateChanged(
+ uid, pid, mOomAdjuster.mAdjSeq, procstate, oldprocstate, oomadj, oldoomadj,
+ OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+ maybeLogStacktrace("procStateChanged");
+ maybeSleep(mConstants.mProcStateDebugSetProcStateDelay);
+ }
+
+ void logScheduleUidIdle1(int uid, long delay) {
+ EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_1,
+ uid, 0, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+ }
+
+ void logScheduleUidIdle2(int uid, int pid, long delay) {
+ EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_2,
+ uid, pid, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+ }
+
+ void logScheduleUidIdle3(long delay) {
+ EventLogTags.writeAmOomAdjMisc(MISC_SCHEDULE_IDLE_UIDS_MSG_3,
+ 0, 0, mOomAdjuster.mAdjSeq, (int) delay, 0, "");
+ }
+
+ void logSetLastBackgroundTime(int uid, long time) {
+ EventLogTags.writeAmOomAdjMisc(MISC_SET_LAST_BG_TIME,
+ uid, 0, mOomAdjuster.mAdjSeq, (int) time, 0,
+ OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+ }
+
+ void logClearLastBackgroundTime(int uid) {
+ EventLogTags.writeAmOomAdjMisc(MISC_CLEAR_LAST_BG_TIME,
+ uid, 0, mOomAdjuster.mAdjSeq, 0, 0,
+ OomAdjuster.oomAdjReasonToString(mOomAdjuster.mLastReason));
+ }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 46bdfe8..5feac1f 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -741,6 +741,7 @@
mPendingProcessSet.clear();
mService.mAppProfiler.mHasPreviousProcess = mService.mAppProfiler.mHasHomeProcess = false;
+ mLastReason = oomAdjReason;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
@@ -761,6 +762,7 @@
@GuardedBy("mService")
@Override
protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) {
+ mLastReason = oomAdjReason;
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason));
mService.mOomAdjProfiler.oomAdjStarted();
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b1823b4..e955b00 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -60,10 +60,3 @@
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- namespace: "backstage_power"
- name: "defer_outgoing_bcasts"
- description: "Defer outgoing broadcasts from processes in freezable state"
- bug: "327496592"
-}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index e915688..b7e11a7 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -20,7 +20,7 @@
// TODO(b/141025588): Create separate internal and external permissions for AuthService.
// TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
-import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_LOGO;
+import static android.Manifest.permission.SET_BIOMETRIC_DIALOG_ADVANCED;
import static android.Manifest.permission.TEST_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
@@ -1030,8 +1030,8 @@
}
private void checkManageBiometricPermission() {
- getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_LOGO,
- "Must have SET_BIOMETRIC_DIALOG_LOGO permission");
+ getContext().enforceCallingOrSelfPermission(SET_BIOMETRIC_DIALOG_ADVANCED,
+ "Must have SET_BIOMETRIC_DIALOG_ADVANCED permission");
}
private void checkPermission() {
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 177c345..e2ae3de 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -76,7 +76,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -397,11 +396,10 @@
@NonNull
private DeviceStateInfo getDeviceStateInfoLocked() {
final List<DeviceState> supportedStates = getSupportedStatesLocked();
- final DeviceState baseState = mBaseState.orElse(null);
- final DeviceState currentState = mCommittedState.orElse(null);
+ final DeviceState baseState = mBaseState.orElse(INVALID_DEVICE_STATE);
+ final DeviceState currentState = mCommittedState.orElse(INVALID_DEVICE_STATE);
- return new DeviceStateInfo(supportedStates,
- baseState != null ? baseState : INVALID_DEVICE_STATE,
+ return new DeviceStateInfo(supportedStates, baseState,
createMergedDeviceState(currentState, baseState));
}
@@ -412,7 +410,7 @@
*/
private DeviceState createMergedDeviceState(@Nullable DeviceState committedState,
@Nullable DeviceState baseState) {
- if (committedState == null) {
+ if (committedState.equals(INVALID_DEVICE_STATE)) {
return INVALID_DEVICE_STATE;
}
@@ -420,8 +418,7 @@
committedState.getConfiguration().getSystemProperties();
Set<@DeviceState.DeviceStateProperties Integer> physicalProperties =
- baseState != null ? baseState.getConfiguration().getPhysicalProperties()
- : Collections.emptySet();
+ baseState.getConfiguration().getPhysicalProperties();
DeviceState.Configuration deviceStateConfiguration = new DeviceState.Configuration.Builder(
committedState.getIdentifier(), committedState.getName())
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 9b2dcc5..4116669 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -57,6 +57,9 @@
import com.android.server.display.config.HbmTiming;
import com.android.server.display.config.HdrBrightnessData;
import com.android.server.display.config.HighBrightnessMode;
+import com.android.server.display.config.IdleScreenRefreshRateTimeout;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds;
import com.android.server.display.config.IntegerArray;
import com.android.server.display.config.LuxThrottling;
import com.android.server.display.config.NitsMap;
@@ -553,6 +556,18 @@
* <minorVersion>0</minorVersion>
* </usiVersion>
* <screenBrightnessCapForWearBedtimeMode>0.1</screenBrightnessCapForWearBedtimeMode>
+ * <idleScreenRefreshRateTimeout>
+ * <luxThresholds>
+ * <point>
+ * <lux>6</lux>
+ * <timeout>1000</timeout>
+ * </point>
+ * <point>
+ * <lux>10</lux>
+ * <timeout>800</timeout>
+ * </point>
+ * </luxThresholds>
+ * </idleScreenRefreshRateTimeout>
* </displayConfiguration>
* }
* </pre>
@@ -843,6 +858,14 @@
private final Map<BrightnessLimitMapType, Map<Float, Float>>
mLuxThrottlingData = new HashMap<>();
+ /**
+ * The idle screen timeout configuration for switching to lower refresh rate
+ */
+ @NonNull
+ private List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+ mIdleScreenRefreshRateTimeoutLuxThresholds = new ArrayList<>();
+
+
@Nullable
private HostUsiVersion mHostUsiVersion;
@@ -1999,6 +2022,7 @@
loadUsiVersion(config);
mHdrBrightnessData = HdrBrightnessData.loadConfig(config);
loadBrightnessCapForWearBedtimeMode(config);
+ loadIdleScreenRefreshRateTimeoutConfigs(config);
} else {
Slog.w(TAG, "DisplayDeviceConfig file is null");
}
@@ -2024,6 +2048,7 @@
loadAutoBrightnessAvailableFromConfigXml();
loadRefreshRateSetting(null);
loadBrightnessCapForWearBedtimeModeFromConfigXml();
+ loadIdleScreenRefreshRateTimeoutConfigs(null);
mLoadedFrom = "<config.xml>";
}
@@ -3326,6 +3351,47 @@
}
}
+ private void loadIdleScreenRefreshRateTimeoutConfigs(@Nullable DisplayConfiguration config) {
+ if (mFlags.isIdleScreenRefreshRateTimeoutEnabled()
+ && config != null && config.getIdleScreenRefreshRateTimeout() != null) {
+ validateIdleScreenRefreshRateTimeoutConfig(
+ config.getIdleScreenRefreshRateTimeout());
+ mIdleScreenRefreshRateTimeoutLuxThresholds = config
+ .getIdleScreenRefreshRateTimeout().getLuxThresholds().getPoint();
+ }
+ }
+
+ private void validateIdleScreenRefreshRateTimeoutConfig(
+ IdleScreenRefreshRateTimeout idleScreenRefreshRateTimeoutConfig) {
+ IdleScreenRefreshRateTimeoutLuxThresholds idleScreenRefreshRateTimeoutLuxThresholds =
+ idleScreenRefreshRateTimeoutConfig.getLuxThresholds();
+
+ if (idleScreenRefreshRateTimeoutLuxThresholds != null) {
+ int previousLux = -1;
+ // Validate that the lux values are in the increasing order
+ for (IdleScreenRefreshRateTimeoutLuxThresholdPoint point :
+ idleScreenRefreshRateTimeoutLuxThresholds.getPoint()) {
+ int newLux = point.getLux().intValue();
+ if (previousLux >= newLux) {
+ throw new RuntimeException("Lux values should be in ascending order in the"
+ + " idle screen refresh rate timeout config");
+ }
+ previousLux = newLux;
+ }
+ }
+ }
+
+ /**
+ * Gets the idle screen refresh rate timeout(in ms) configuration list. For each entry, the lux
+ * value represent the lower bound of the lux range, and the value of the lux in the next
+ * point(INF if not present) represents the upper bound for the corresponding timeout(in ms)
+ */
+ @NonNull
+ public List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+ getIdleScreenRefreshRateTimeoutLuxThresholdPoint() {
+ return mIdleScreenRefreshRateTimeoutLuxThresholds;
+ }
+
/**
* Extracts a float array from the specified {@link TypedArray}.
*
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index ce7c224..84eebe8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_DISPLAYS;
+import static android.Manifest.permission.RESTRICT_DISPLAY_MODES;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
@@ -75,6 +76,7 @@
import android.hardware.OverlayProperties;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManagerInternal;
import android.hardware.display.AmbientBrightnessDayStats;
@@ -4530,6 +4532,14 @@
disableConnectedDisplay_enforcePermission();
DisplayManagerService.this.enableConnectedDisplay(displayId, false);
}
+
+ @EnforcePermission(RESTRICT_DISPLAY_MODES)
+ @Override // Binder call
+ public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
+ requestDisplayModes_enforcePermission();
+ DisplayManagerService.this.mDisplayModeDirector.requestDisplayModes(
+ token, displayId, modeIds);
+ }
}
private static boolean isValidBrightness(float brightness) {
@@ -5034,30 +5044,22 @@
* Listens to changes in device state and reports the state to LogicalDisplayMapper.
*/
class DeviceStateListener implements DeviceStateManager.DeviceStateCallback {
- // Base state corresponds to the physical state of the device
- private int mBaseState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
@Override
- public void onStateChanged(int deviceState) {
- boolean isDeviceStateOverrideActive = deviceState != mBaseState;
+ public void onDeviceStateChanged(DeviceState deviceState) {
synchronized (mSyncRoot) {
// Notify WindowManager that we are about to handle new device state, this should
// be sent before any work related to the device state in DisplayManager, so
// WindowManager could do implement that depends on the device state and display
// changes (serializes device state update and display change events)
Message msg = mHandler.obtainMessage(MSG_RECEIVED_DEVICE_STATE);
- msg.arg1 = deviceState;
+ msg.arg1 = deviceState.getIdentifier();
mHandler.sendMessage(msg);
mLogicalDisplayMapper
- .setDeviceStateLocked(deviceState, isDeviceStateOverrideActive);
+ .setDeviceStateLocked(deviceState.getIdentifier());
}
}
-
- @Override
- public void onBaseStateChanged(int state) {
- mBaseState = state;
- }
};
private class BrightnessPair {
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 21f90d4..f727eac 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -441,7 +441,7 @@
mVirtualDeviceDisplayMapping.put(displayDevice.getUniqueId(), virtualDeviceUniqueId);
}
- void setDeviceStateLocked(int state, boolean isOverrideActive) {
+ void setDeviceStateLocked(int state) {
if (!mBootCompleted) {
// The boot animation might still be in progress, we do not want to switch states now
// as the boot animation would end up with an incorrect size.
@@ -465,7 +465,7 @@
final boolean wakeDevice = shouldDeviceBeWoken(mPendingDeviceState, mDeviceState,
mInteractive, mBootCompleted);
final boolean sleepDevice = shouldDeviceBePutToSleep(mPendingDeviceState, mDeviceState,
- isOverrideActive, mInteractive, mBootCompleted);
+ mInteractive, mBootCompleted);
// If all displays are off already, we can just transition here, unless we are trying to
// wake or sleep the device as part of this transition. In that case defer the final
@@ -513,8 +513,7 @@
mBootCompleted = true;
if (mDeviceStateToBeAppliedAfterBoot
!= DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER) {
- setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot,
- /* isOverrideActive= */ false);
+ setDeviceStateLocked(mDeviceStateToBeAppliedAfterBoot);
}
}
}
@@ -560,7 +559,6 @@
*
* @param pendingState device state we are moving to
* @param currentState device state we are currently in
- * @param isOverrideActive if a device state override is currently active or not
* @param isInteractive if the device is in an interactive state
* @param isBootCompleted is the device fully booted
*
@@ -568,13 +566,13 @@
* @see #setDeviceStateLocked
*/
@VisibleForTesting
- boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isOverrideActive,
- boolean isInteractive, boolean isBootCompleted) {
+ boolean shouldDeviceBePutToSleep(int pendingState, int currentState, boolean isInteractive,
+ boolean isBootCompleted) {
return currentState != DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER
&& mDeviceStatesOnWhichToSleep.get(pendingState)
&& !mDeviceStatesOnWhichToSleep.get(currentState)
- && !isOverrideActive
- && isInteractive && isBootCompleted
+ && isInteractive
+ && isBootCompleted
&& !mFoldSettingProvider.shouldStayAwakeOnFold();
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index 5f455db..e1a166e 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -92,9 +92,9 @@
Flags.FLAG_BRIGHTNESS_INT_RANGE_USER_PERCEPTION,
Flags::brightnessIntRangeUserPerception);
- private final FlagState mVsyncProximityVote = new FlagState(
- Flags.FLAG_ENABLE_EXTERNAL_VSYNC_PROXIMITY_VOTE,
- Flags::enableExternalVsyncProximityVote);
+ private final FlagState mRestrictDisplayModes = new FlagState(
+ Flags.FLAG_ENABLE_RESTRICT_DISPLAY_MODES,
+ Flags::enableRestrictDisplayModes);
private final FlagState mVsyncLowPowerVote = new FlagState(
Flags.FLAG_ENABLE_VSYNC_LOW_POWER_VOTE,
@@ -135,6 +135,11 @@
Flags::sensorBasedBrightnessThrottling
);
+ private final FlagState mIdleScreenRefreshRateTimeout = new FlagState(
+ Flags.FLAG_IDLE_SCREEN_REFRESH_RATE_TIMEOUT,
+ Flags::idleScreenRefreshRateTimeout
+ );
+
private final FlagState mRefactorDisplayPowerController = new FlagState(
Flags.FLAG_REFACTOR_DISPLAY_POWER_CONTROLLER,
@@ -237,8 +242,8 @@
return mBrightnessIntRangeUserPerceptionFlagState.isEnabled();
}
- public boolean isVsyncProximityVoteEnabled() {
- return mVsyncProximityVote.isEnabled();
+ public boolean isRestrictDisplayModesEnabled() {
+ return mRestrictDisplayModes.isEnabled();
}
public boolean isVsyncLowPowerVoteEnabled() {
@@ -280,6 +285,10 @@
return mSensorBasedBrightnessThrottling.isEnabled();
}
+ public boolean isIdleScreenRefreshRateTimeoutEnabled() {
+ return mIdleScreenRefreshRateTimeout.isEnabled();
+ }
+
public boolean isRefactorDisplayPowerControllerEnabled() {
return mRefactorDisplayPowerController.isEnabled();
}
@@ -302,7 +311,7 @@
pw.println(" " + mPowerThrottlingClamperFlagState);
pw.println(" " + mSmallAreaDetectionFlagState);
pw.println(" " + mBrightnessIntRangeUserPerceptionFlagState);
- pw.println(" " + mVsyncProximityVote);
+ pw.println(" " + mRestrictDisplayModes);
pw.println(" " + mBrightnessWearBedtimeModeClamperFlagState);
pw.println(" " + mAutoBrightnessModesFlagState);
pw.println(" " + mFastHdrTransitions);
@@ -310,6 +319,7 @@
pw.println(" " + mRefreshRateVotingTelemetry);
pw.println(" " + mPixelAnisotropyCorrectionEnabled);
pw.println(" " + mSensorBasedBrightnessThrottling);
+ pw.println(" " + mIdleScreenRefreshRateTimeout);
pw.println(" " + mRefactorDisplayPowerController);
}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index d2909b8..a5f241f 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -130,9 +130,9 @@
}
flag {
- name: "enable_external_vsync_proximity_vote"
+ name: "enable_restrict_display_modes"
namespace: "display_manager"
- description: "Feature flag for external vsync proximity vote"
+ description: "Feature flag for restriction display modes api"
bug: "284866750"
is_fixed_read_only: true
}
@@ -219,3 +219,11 @@
bug: "294444204"
is_fixed_read_only: true
}
+
+flag {
+ name: "idle_screen_refresh_rate_timeout"
+ namespace: "display_manager"
+ description: "Feature flag for reducing the refresh rate when the screen is idle after a timeout"
+ bug: "310026579"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
index c538231..6d750c0 100644
--- a/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
+++ b/services/core/java/com/android/server/display/mode/BaseModeRefreshRateVote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.Objects;
class BaseModeRefreshRateVote implements Vote {
@@ -31,7 +33,7 @@
}
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
if (summary.appRequestBaseModeRefreshRate == 0f
&& mAppRequestBaseModeRefreshRate > 0f) {
summary.appRequestBaseModeRefreshRate = mAppRequestBaseModeRefreshRate;
diff --git a/services/core/java/com/android/server/display/mode/CombinedVote.java b/services/core/java/com/android/server/display/mode/CombinedVote.java
index 4b68791..3cd16bf 100644
--- a/services/core/java/com/android/server/display/mode/CombinedVote.java
+++ b/services/core/java/com/android/server/display/mode/CombinedVote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -28,7 +30,7 @@
}
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
mVotes.forEach(vote -> vote.updateSummary(summary));
}
diff --git a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
index 7f57406..7abb518 100644
--- a/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
+++ b/services/core/java/com/android/server/display/mode/DisableRefreshRateSwitchingVote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.Objects;
class DisableRefreshRateSwitchingVote implements Vote {
@@ -31,7 +33,7 @@
}
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
summary.disableRefreshRateSwitching =
summary.disableRefreshRateSwitching || mDisableRefreshRateSwitching;
}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 64cbd54..495ae87 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -41,6 +41,7 @@
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.net.Uri;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IThermalEventListener;
import android.os.IThermalService;
import android.os.Looper;
@@ -80,7 +81,6 @@
import com.android.server.display.utils.DeviceConfigParsingUtils;
import com.android.server.display.utils.SensorUtils;
import com.android.server.sensors.SensorManagerInternal;
-import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -128,9 +128,12 @@
private final SettingsObserver mSettingsObserver;
private final DisplayObserver mDisplayObserver;
private final UdfpsObserver mUdfpsObserver;
- private final SensorObserver mSensorObserver;
+ private final ProximitySensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
+
+ @Nullable
+ private final SystemRequestObserver mSystemRequestObserver;
private final DeviceConfigParameterProvider mConfigParameterProvider;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@@ -203,6 +206,7 @@
.isDisplaysRefreshRatesSynchronizationEnabled();
mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags
.isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled();
+
mContext = context;
mHandler = new DisplayModeDirectorHandler(handler.getLooper());
mInjector = injector;
@@ -222,10 +226,15 @@
mVotesStorage = new VotesStorage(this::notifyDesiredDisplayModeSpecsChangedLocked,
mVotesStatsReporter);
mDisplayObserver = new DisplayObserver(context, handler, mVotesStorage);
- mSensorObserver = new SensorObserver(context, mVotesStorage, injector);
+ mSensorObserver = new ProximitySensorObserver(mVotesStorage, injector);
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, mVotesStorage);
mHbmObserver = new HbmObserver(injector, mVotesStorage, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
+ if (mDvrrSupported && displayManagerFlags.isRestrictDisplayModesEnabled()) {
+ mSystemRequestObserver = new SystemRequestObserver(mVotesStorage);
+ } else {
+ mSystemRequestObserver = null;
+ }
mAlwaysRespectAppRequest = false;
mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
}
@@ -520,6 +529,15 @@
}
/**
+ * Delegates requestDisplayModes call to SystemRequestObserver
+ */
+ public void requestDisplayModes(IBinder token, int displayId, int[] modeIds) {
+ if (mSystemRequestObserver != null) {
+ mSystemRequestObserver.requestDisplayModes(token, displayId, modeIds);
+ }
+ }
+
+ /**
* Print the object's state and debug information into the given stream.
*
* @param pw The stream to dump information to.
@@ -970,10 +988,10 @@
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
if (inLowPowerMode && mVsynLowPowerVoteEnabled) {
- vote = Vote.forSupportedModes(List.of(
- new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f,
+ vote = Vote.forSupportedRefreshRates(List.of(
+ new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
/* vsyncRate= */ 240f),
- new SupportedModesVote.SupportedMode(/* peakRefreshRate= */ 60f,
+ new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
/* vsyncRate= */ 60f)
));
} else if (inLowPowerMode) {
@@ -2158,11 +2176,11 @@
}
if (mVsyncLowLightBlockingVoteEnabled) {
- refreshRateSwitchingVote = Vote.forSupportedModesAndDisableRefreshRateSwitching(
+ refreshRateSwitchingVote = Vote.forSupportedRefreshRatesAndDisableSwitching(
List.of(
- new SupportedModesVote.SupportedMode(
+ new SupportedRefreshRatesVote.RefreshRates(
/* peakRefreshRate= */ 60f, /* vsyncRate= */ 60f),
- new SupportedModesVote.SupportedMode(
+ new SupportedRefreshRatesVote.RefreshRates(
/* peakRefreshRate= */120f, /* vsyncRate= */ 120f)));
} else {
refreshRateSwitchingVote = Vote.forDisableRefreshRateSwitching();
@@ -2498,116 +2516,6 @@
}
}
- protected static final class SensorObserver implements ProximityActiveListener,
- DisplayManager.DisplayListener {
- private final String mProximitySensorName = null;
- private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
-
- private final VotesStorage mVotesStorage;
- private final Context mContext;
- private final Injector mInjector;
- @GuardedBy("mSensorObserverLock")
- private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
- private final Object mSensorObserverLock = new Object();
-
- private DisplayManager mDisplayManager;
- private DisplayManagerInternal mDisplayManagerInternal;
- @GuardedBy("mSensorObserverLock")
- private boolean mIsProxActive = false;
-
- SensorObserver(Context context, VotesStorage votesStorage, Injector injector) {
- mContext = context;
- mVotesStorage = votesStorage;
- mInjector = injector;
- }
-
- @Override
- public void onProximityActive(boolean isActive) {
- synchronized (mSensorObserverLock) {
- if (mIsProxActive != isActive) {
- mIsProxActive = isActive;
- recalculateVotesLocked();
- }
- }
- }
-
- public void observe() {
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
-
- final SensorManagerInternal sensorManager = mInjector.getSensorManagerInternal();
- sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
-
- synchronized (mSensorObserverLock) {
- for (Display d : mInjector.getDisplays()) {
- mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
- }
- }
- mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
- DisplayManager.EVENT_FLAG_DISPLAY_ADDED
- | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
- | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
- }
-
- private void recalculateVotesLocked() {
- final Display[] displays = mInjector.getDisplays();
- for (Display d : displays) {
- int displayId = d.getDisplayId();
- Vote vote = null;
- if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
- final RefreshRateRange rate =
- mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
- displayId, mProximitySensorName, mProximitySensorType);
- if (rate != null) {
- vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
- }
- }
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
- }
- }
-
- void dump(PrintWriter pw) {
- pw.println(" SensorObserver");
- synchronized (mSensorObserverLock) {
- pw.println(" mIsProxActive=" + mIsProxActive);
- pw.println(" mDozeStateByDisplay:");
- for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
- final int id = mDozeStateByDisplay.keyAt(i);
- final boolean dozed = mDozeStateByDisplay.valueAt(i);
- pw.println(" " + id + " -> " + dozed);
- }
- }
- }
-
- @Override
- public void onDisplayAdded(int displayId) {
- boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
- synchronized (mSensorObserverLock) {
- mDozeStateByDisplay.put(displayId, isDozeState);
- recalculateVotesLocked();
- }
- }
-
- @Override
- public void onDisplayChanged(int displayId) {
- boolean wasDozeState = mDozeStateByDisplay.get(displayId);
- synchronized (mSensorObserverLock) {
- mDozeStateByDisplay.put(displayId,
- mInjector.isDozeState(mInjector.getDisplay(displayId)));
- if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
- recalculateVotesLocked();
- }
- }
- }
-
- @Override
- public void onDisplayRemoved(int displayId) {
- synchronized (mSensorObserverLock) {
- mDozeStateByDisplay.delete(displayId);
- recalculateVotesLocked();
- }
- }
- }
/**
* Listens to DisplayManager for HBM status and applies any refresh-rate restrictions for
diff --git a/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java
new file mode 100644
index 0000000..11418c1
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/ProximitySensorObserver.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.hardware.Sensor;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.util.SparseBooleanArray;
+import android.view.Display;
+import android.view.SurfaceControl;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.sensors.SensorManagerInternal;
+
+import java.io.PrintWriter;
+
+class ProximitySensorObserver implements
+ SensorManagerInternal.ProximityActiveListener,
+ DisplayManager.DisplayListener {
+ private final String mProximitySensorName = null;
+ private final String mProximitySensorType = Sensor.STRING_TYPE_PROXIMITY;
+
+ private final VotesStorage mVotesStorage;
+ private final DisplayModeDirector.Injector mInjector;
+ @GuardedBy("mSensorObserverLock")
+ private final SparseBooleanArray mDozeStateByDisplay = new SparseBooleanArray();
+ private final Object mSensorObserverLock = new Object();
+ private DisplayManagerInternal mDisplayManagerInternal;
+ @GuardedBy("mSensorObserverLock")
+ private boolean mIsProxActive = false;
+
+ ProximitySensorObserver(VotesStorage votesStorage, DisplayModeDirector.Injector injector) {
+ mVotesStorage = votesStorage;
+ mInjector = injector;
+ }
+
+ @Override
+ public void onProximityActive(boolean isActive) {
+ synchronized (mSensorObserverLock) {
+ if (mIsProxActive != isActive) {
+ mIsProxActive = isActive;
+ recalculateVotesLocked();
+ }
+ }
+ }
+
+ void observe() {
+ mDisplayManagerInternal = mInjector.getDisplayManagerInternal();
+
+ final SensorManagerInternal sensorManager = mInjector.getSensorManagerInternal();
+ sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this);
+
+ synchronized (mSensorObserverLock) {
+ for (Display d : mInjector.getDisplays()) {
+ mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d));
+ }
+ }
+ mInjector.registerDisplayListener(this, BackgroundThread.getHandler(),
+ DisplayManager.EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
+ }
+
+ @GuardedBy("mSensorObserverLock")
+ private void recalculateVotesLocked() {
+ final Display[] displays = mInjector.getDisplays();
+ for (Display d : displays) {
+ int displayId = d.getDisplayId();
+ Vote vote = null;
+ if (mIsProxActive && !mDozeStateByDisplay.get(displayId)) {
+ final SurfaceControl.RefreshRateRange rate =
+ mDisplayManagerInternal.getRefreshRateForDisplayAndSensor(
+ displayId, mProximitySensorName, mProximitySensorType);
+ if (rate != null) {
+ vote = Vote.forPhysicalRefreshRates(rate.min, rate.max);
+ }
+ }
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_PROXIMITY, vote);
+ }
+ }
+
+ void dump(PrintWriter pw) {
+ pw.println(" SensorObserver");
+ synchronized (mSensorObserverLock) {
+ pw.println(" mIsProxActive=" + mIsProxActive);
+ pw.println(" mDozeStateByDisplay:");
+ for (int i = 0; i < mDozeStateByDisplay.size(); i++) {
+ final int id = mDozeStateByDisplay.keyAt(i);
+ final boolean dozed = mDozeStateByDisplay.valueAt(i);
+ pw.println(" " + id + " -> " + dozed);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId));
+ synchronized (mSensorObserverLock) {
+ mDozeStateByDisplay.put(displayId, isDozeState);
+ recalculateVotesLocked();
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ synchronized (mSensorObserverLock) {
+ boolean wasDozeState = mDozeStateByDisplay.get(displayId);
+ mDozeStateByDisplay.put(displayId,
+ mInjector.isDozeState(mInjector.getDisplay(displayId)));
+ if (wasDozeState != mDozeStateByDisplay.get(displayId)) {
+ recalculateVotesLocked();
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ synchronized (mSensorObserverLock) {
+ mDozeStateByDisplay.delete(displayId);
+ recalculateVotesLocked();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/RefreshRateVote.java b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
index 670b8a1..b96ab3b 100644
--- a/services/core/java/com/android/server/display/mode/RefreshRateVote.java
+++ b/services/core/java/com/android/server/display/mode/RefreshRateVote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.Objects;
@@ -64,7 +66,7 @@
* Vote: min(ignored) min(applied) min(applied+physical) max(applied) max(ignored)
*/
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
summary.minRenderFrameRate = Math.max(summary.minRenderFrameRate, mMinRefreshRate);
summary.maxRenderFrameRate = Math.min(summary.maxRenderFrameRate, mMaxRefreshRate);
// Physical refresh rate cannot be lower than the minimal render frame rate.
@@ -97,7 +99,7 @@
* Vote: min(ignored) min(applied) max(applied+render) max(applied) max(ignored)
*/
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
summary.minPhysicalRefreshRate = Math.max(summary.minPhysicalRefreshRate,
mMinRefreshRate);
summary.maxPhysicalRefreshRate = Math.min(summary.maxPhysicalRefreshRate,
diff --git a/services/core/java/com/android/server/display/mode/SizeVote.java b/services/core/java/com/android/server/display/mode/SizeVote.java
index f2f8dc4..f5a5abe 100644
--- a/services/core/java/com/android/server/display/mode/SizeVote.java
+++ b/services/core/java/com/android/server/display/mode/SizeVote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.Objects;
class SizeVote implements Vote {
@@ -48,7 +50,7 @@
}
@Override
- public void updateSummary(VoteSummary summary) {
+ public void updateSummary(@NonNull VoteSummary summary) {
if (mHeight > 0 && mWidth > 0) {
// For display size, disable refresh rate switching and base mode refresh rate use
// only the first vote we come across (i.e. the highest priority vote that includes
diff --git a/services/core/java/com/android/server/display/mode/SupportedModesVote.java b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
index 7eebcc0..0cf8311 100644
--- a/services/core/java/com/android/server/display/mode/SupportedModesVote.java
+++ b/services/core/java/com/android/server/display/mode/SupportedModesVote.java
@@ -16,77 +16,42 @@
package com.android.server.display.mode;
-import java.util.ArrayList;
+import android.annotation.NonNull;
+
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-class SupportedModesVote implements Vote {
+public class SupportedModesVote implements Vote {
- final List<SupportedMode> mSupportedModes;
+ final List<Integer> mModeIds;
- SupportedModesVote(List<SupportedMode> supportedModes) {
- mSupportedModes = Collections.unmodifiableList(supportedModes);
+ SupportedModesVote(List<Integer> modeIds) {
+ mModeIds = Collections.unmodifiableList(modeIds);
+ }
+ @Override
+ public void updateSummary(@NonNull VoteSummary summary) {
+ if (summary.supportedModeIds == null) {
+ summary.supportedModeIds = mModeIds;
+ } else {
+ summary.supportedModeIds.retainAll(mModeIds);
+ }
}
- /**
- * Summary should have subset of supported modes.
- * If Vote1.supportedModes=(A,B), Vote2.supportedModes=(B,C) then summary.supportedModes=(B)
- * If summary.supportedModes==null then there is no restriction on supportedModes
- */
@Override
- public void updateSummary(VoteSummary summary) {
- if (summary.supportedModes == null) {
- summary.supportedModes = new ArrayList<>(mSupportedModes);
- } else {
- summary.supportedModes.retainAll(mSupportedModes);
- }
+ public String toString() {
+ return "SupportedModesVote{ mModeIds=" + mModeIds + " }";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SupportedModesVote that)) return false;
- return mSupportedModes.equals(that.mSupportedModes);
+ return mModeIds.equals(that.mModeIds);
}
@Override
public int hashCode() {
- return Objects.hash(mSupportedModes);
- }
-
- @Override
- public String toString() {
- return "SupportedModesVote{ mSupportedModes=" + mSupportedModes + " }";
- }
-
- static class SupportedMode {
- final float mPeakRefreshRate;
- final float mVsyncRate;
-
-
- SupportedMode(float peakRefreshRate, float vsyncRate) {
- mPeakRefreshRate = peakRefreshRate;
- mVsyncRate = vsyncRate;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof SupportedMode that)) return false;
- return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
- && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPeakRefreshRate, mVsyncRate);
- }
-
- @Override
- public String toString() {
- return "SupportedMode{ mPeakRefreshRate=" + mPeakRefreshRate
- + ", mVsyncRate=" + mVsyncRate + " }";
- }
+ return Objects.hash(mModeIds);
}
}
diff --git a/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java b/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java
new file mode 100644
index 0000000..5305487
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SupportedRefreshRatesVote.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+class SupportedRefreshRatesVote implements Vote {
+
+ final List<RefreshRates> mRefreshRates;
+
+ SupportedRefreshRatesVote(List<RefreshRates> refreshRates) {
+ mRefreshRates = Collections.unmodifiableList(refreshRates);
+ }
+
+ /**
+ * Summary should have subset of supported modes.
+ * If Vote1.refreshRates=(A,B), Vote2.refreshRates=(B,C)
+ * then summary.supportedRefreshRates=(B)
+ * If summary.supportedRefreshRates==null then there is no restriction on supportedRefreshRates
+ */
+ @Override
+ public void updateSummary(@NonNull VoteSummary summary) {
+ if (summary.supportedRefreshRates == null) {
+ summary.supportedRefreshRates = new ArrayList<>(mRefreshRates);
+ } else {
+ summary.supportedRefreshRates.retainAll(mRefreshRates);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SupportedRefreshRatesVote that)) return false;
+ return mRefreshRates.equals(that.mRefreshRates);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRefreshRates);
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedRefreshRatesVote{ mSupportedModes=" + mRefreshRates + " }";
+ }
+
+ static class RefreshRates {
+ final float mPeakRefreshRate;
+ final float mVsyncRate;
+
+ RefreshRates(float peakRefreshRate, float vsyncRate) {
+ mPeakRefreshRate = peakRefreshRate;
+ mVsyncRate = vsyncRate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RefreshRates that)) return false;
+ return Float.compare(that.mPeakRefreshRate, mPeakRefreshRate) == 0
+ && Float.compare(that.mVsyncRate, mVsyncRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPeakRefreshRate, mVsyncRate);
+ }
+
+ @Override
+ public String toString() {
+ return "RefreshRates{ mPeakRefreshRate=" + mPeakRefreshRate
+ + ", mVsyncRate=" + mVsyncRate + " }";
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/SystemRequestObserver.java b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
new file mode 100644
index 0000000..15f19cc
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/SystemRequestObserver.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SystemRequestObserver responsible for handling system requests to filter allowable display
+ * modes
+ */
+class SystemRequestObserver {
+ private final VotesStorage mVotesStorage;
+
+ private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ // noop, binderDied(@NonNull IBinder who) is overridden
+ }
+ @Override
+ public void binderDied(@NonNull IBinder who) {
+ removeSystemRequestedVotes(who);
+ who.unlinkToDeath(mDeathRecipient, 0);
+ }
+ };
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final Map<IBinder, SparseArray<List<Integer>>> mDisplaysRestrictions = new HashMap<>();
+
+ SystemRequestObserver(VotesStorage storage) {
+ mVotesStorage = storage;
+ }
+
+ void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) {
+ if (modeIds == null) {
+ removeSystemRequestedVote(token, displayId);
+ } else {
+ addSystemRequestedVote(token, displayId, modeIds);
+ }
+ }
+
+ private void addSystemRequestedVote(IBinder token, int displayId, @NonNull int[] modeIds) {
+ try {
+ boolean needLinkToDeath = false;
+ List<Integer> modeIdsList = new ArrayList<>();
+ for (int mode: modeIds) {
+ modeIdsList.add(mode);
+ }
+ synchronized (mLock) {
+ SparseArray<List<Integer>> modesByDisplay = mDisplaysRestrictions.get(token);
+ if (modesByDisplay == null) {
+ needLinkToDeath = true;
+ modesByDisplay = new SparseArray<>();
+ mDisplaysRestrictions.put(token, modesByDisplay);
+ }
+
+ modesByDisplay.put(displayId, modeIdsList);
+ updateStorageLocked(displayId);
+ }
+ if (needLinkToDeath) {
+ token.linkToDeath(mDeathRecipient, 0);
+ }
+ } catch (RemoteException re) {
+ removeSystemRequestedVotes(token);
+ }
+ }
+
+ private void removeSystemRequestedVote(IBinder token, int displayId) {
+ boolean needToUnlink = false;
+ synchronized (mLock) {
+ SparseArray<List<Integer>> modesByDisplay = mDisplaysRestrictions.get(token);
+ if (modesByDisplay != null) {
+ modesByDisplay.remove(displayId);
+ needToUnlink = modesByDisplay.size() == 0;
+ updateStorageLocked(displayId);
+ }
+ }
+ if (needToUnlink) {
+ token.unlinkToDeath(mDeathRecipient, 0);
+ }
+ }
+
+ private void removeSystemRequestedVotes(IBinder token) {
+ synchronized (mLock) {
+ SparseArray<List<Integer>> removed = mDisplaysRestrictions.remove(token);
+ if (removed != null) {
+ for (int i = 0; i < removed.size(); i++) {
+ updateStorageLocked(removed.keyAt(i));
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateStorageLocked(int displayId) {
+ List<Integer> modeIds = new ArrayList<>();
+ boolean[] modesFound = new boolean[1];
+
+ mDisplaysRestrictions.forEach((key, value) -> {
+ List<Integer> modesForDisplay = value.get(displayId);
+ if (modesForDisplay != null) {
+ if (!modesFound[0]) {
+ modeIds.addAll(modesForDisplay);
+ modesFound[0] = true;
+ } else {
+ modeIds.retainAll(modesForDisplay);
+ }
+ }
+ });
+
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_SYSTEM_REQUESTED_MODES,
+ modesFound[0] ? Vote.forSupportedModes(modeIds) : null);
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index e8d5a19..5b987f4 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -16,6 +16,8 @@
package com.android.server.display.mode;
+import android.annotation.NonNull;
+
import java.util.List;
interface Vote {
@@ -91,26 +93,29 @@
// For concurrent displays we want to limit refresh rate on all displays
int PRIORITY_LAYOUT_LIMITED_FRAME_RATE = 12;
+ // For internal application to limit display modes to specific ids
+ int PRIORITY_SYSTEM_REQUESTED_MODES = 13;
+
// LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE = 13;
+ int PRIORITY_LOW_POWER_MODE = 14;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 14;
+ int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 15;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- int PRIORITY_SKIN_TEMPERATURE = 15;
+ int PRIORITY_SKIN_TEMPERATURE = 16;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
- int PRIORITY_PROXIMITY = 16;
+ int PRIORITY_PROXIMITY = 17;
// The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order
// to function, so this needs to be the highest priority of all votes.
- int PRIORITY_UDFPS = 17;
+ int PRIORITY_UDFPS = 18;
// Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and
// APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString.
@@ -128,7 +133,7 @@
*/
int INVALID_SIZE = -1;
- void updateSummary(VoteSummary summary);
+ void updateSummary(@NonNull VoteSummary summary);
static Vote forPhysicalRefreshRates(float minRefreshRate, float maxRefreshRate) {
return new CombinedVote(
@@ -166,15 +171,22 @@
return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
- static Vote forSupportedModes(List<SupportedModesVote.SupportedMode> supportedModes) {
- return new SupportedModesVote(supportedModes);
+ static Vote forSupportedRefreshRates(
+ List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
+ return new SupportedRefreshRatesVote(refreshRates);
+ }
+
+ static Vote forSupportedModes(List<Integer> modeIds) {
+ return new SupportedModesVote(modeIds);
}
- static Vote forSupportedModesAndDisableRefreshRateSwitching(
- List<SupportedModesVote.SupportedMode> supportedModes) {
+
+ static Vote forSupportedRefreshRatesAndDisableSwitching(
+ List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
return new CombinedVote(
- List.of(forDisableRefreshRateSwitching(), forSupportedModes(supportedModes)));
+ List.of(forDisableRefreshRateSwitching(),
+ forSupportedRefreshRates(supportedRefreshRates)));
}
static String priorityToString(int priority) {
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index 5fc36b5..d4ce892 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -16,6 +16,7 @@
package com.android.server.display.mode;
+import android.annotation.Nullable;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -39,7 +40,11 @@
public boolean disableRefreshRateSwitching;
public float appRequestBaseModeRefreshRate;
- public List<SupportedModesVote.SupportedMode> supportedModes;
+ @Nullable
+ public List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates;
+
+ @Nullable
+ public List<Integer> supportedModeIds;
final boolean mIsDisplayResolutionRangeVotingEnabled;
@@ -112,6 +117,9 @@
boolean missingBaseModeRefreshRate = appRequestBaseModeRefreshRate > 0f;
for (Display.Mode mode : modes) {
+ if (!validateRefreshRatesSupported(mode)) {
+ continue;
+ }
if (!validateModeSupported(mode)) {
continue;
}
@@ -253,21 +261,37 @@
}
private boolean validateModeSupported(Display.Mode mode) {
- if (supportedModes == null || !mSupportedModesVoteEnabled) {
+ if (supportedModeIds == null || !mSupportedModesVoteEnabled) {
return true;
}
- for (SupportedModesVote.SupportedMode supportedMode : supportedModes) {
- if (equalsWithinFloatTolerance(mode.getRefreshRate(), supportedMode.mPeakRefreshRate)
- && equalsWithinFloatTolerance(mode.getVsyncRate(), supportedMode.mVsyncRate)) {
+ if (supportedModeIds.contains(mode.getModeId())) {
+ return true;
+ }
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ + ", supportedMode not found"
+ + ": mode.modeId=" + mode.getModeId()
+ + ", supportedModeIds=" + supportedModeIds);
+ }
+ return false;
+ }
+
+ private boolean validateRefreshRatesSupported(Display.Mode mode) {
+ if (supportedRefreshRates == null || !mSupportedModesVoteEnabled) {
+ return true;
+ }
+ for (SupportedRefreshRatesVote.RefreshRates refreshRates : this.supportedRefreshRates) {
+ if (equalsWithinFloatTolerance(mode.getRefreshRate(), refreshRates.mPeakRefreshRate)
+ && equalsWithinFloatTolerance(mode.getVsyncRate(), refreshRates.mVsyncRate)) {
return true;
}
}
if (mLoggingEnabled) {
Slog.w(TAG, "Discarding mode " + mode.getModeId()
- + ", supportedMode not found"
+ + ", supportedRefreshRates not found"
+ ": mode.refreshRate=" + mode.getRefreshRate()
+ ", mode.vsyncRate=" + mode.getVsyncRate()
- + ", supportedModes=" + supportedModes);
+ + ", supportedRefreshRates=" + supportedRefreshRates);
}
return false;
}
@@ -298,7 +322,8 @@
return false;
}
- if (supportedModes != null && mSupportedModesVoteEnabled && supportedModes.isEmpty()) {
+ if (supportedRefreshRates != null && mSupportedModesVoteEnabled
+ && supportedRefreshRates.isEmpty()) {
if (mLoggingEnabled) {
Slog.w(TAG, "Vote summary resulted in empty set (empty supportedModes)");
}
@@ -345,7 +370,8 @@
minHeight = 0;
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
- supportedModes = null;
+ supportedRefreshRates = null;
+ supportedModeIds = null;
if (mLoggingEnabled) {
Slog.i(TAG, "Summary reset: " + this);
}
@@ -367,7 +393,8 @@
+ ", minHeight=" + minHeight
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate
- + ", supportedModes=" + supportedModes
+ + ", supportedRefreshRates=" + supportedRefreshRates
+ + ", supportedModeIds=" + supportedModeIds
+ ", mIsDisplayResolutionRangeVotingEnabled="
+ mIsDisplayResolutionRangeVotingEnabled
+ ", mSupportedModesVoteEnabled=" + mSupportedModesVoteEnabled
diff --git a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
index e80b9451..7562a52 100644
--- a/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
+++ b/services/core/java/com/android/server/display/mode/VotesStatsReporter.java
@@ -117,11 +117,11 @@
maxRefreshRate = (int) physicalVote.mMaxRefreshRate;
} else if (!ignoreRenderRate && (vote instanceof RefreshRateVote.RenderVote renderVote)) {
maxRefreshRate = (int) renderVote.mMaxRefreshRate;
- } else if (vote instanceof SupportedModesVote supportedModesVote) {
- // SupportedModesVote limits mode by specific refreshRates, so highest rr is allowed
+ } else if (vote instanceof SupportedRefreshRatesVote refreshRatesVote) {
+ // SupportedRefreshRatesVote limits mode by refreshRates, so highest rr is allowed
maxRefreshRate = 0;
- for (SupportedModesVote.SupportedMode mode : supportedModesVote.mSupportedModes) {
- maxRefreshRate = Math.max(maxRefreshRate, (int) mode.mPeakRefreshRate);
+ for (SupportedRefreshRatesVote.RefreshRates rr : refreshRatesVote.mRefreshRates) {
+ maxRefreshRate = Math.max(maxRefreshRate, (int) rr.mPeakRefreshRate);
}
} else if (vote instanceof CombinedVote combinedVote) {
for (Vote subVote: combinedVote.mVotes) {
diff --git a/services/core/java/com/android/server/display/mode/VotesStorage.java b/services/core/java/com/android/server/display/mode/VotesStorage.java
index 56c7c18..6becf1c 100644
--- a/services/core/java/com/android/server/display/mode/VotesStorage.java
+++ b/services/core/java/com/android/server/display/mode/VotesStorage.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -124,6 +125,44 @@
}
}
+ /** removes all votes with certain priority from vote storage */
+ void removeAllVotesForPriority(int priority) {
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "removeAllVotesForPriority(priority="
+ + Vote.priorityToString(priority) + ")");
+ }
+ if (priority < Vote.MIN_PRIORITY || priority > Vote.MAX_PRIORITY) {
+ Slog.w(TAG, "Received an invalid priority, ignoring:"
+ + " priority=" + Vote.priorityToString(priority));
+ return;
+ }
+ IntArray removedVotesDisplayIds = new IntArray();
+ synchronized (mStorageLock) {
+ int size = mVotesByDisplay.size();
+ for (int i = 0; i < size; i++) {
+ SparseArray<Vote> votes = mVotesByDisplay.valueAt(i);
+ if (votes.get(priority) != null) {
+ votes.remove(priority);
+ removedVotesDisplayIds.add(mVotesByDisplay.keyAt(i));
+ }
+ }
+ }
+ if (mLoggingEnabled) {
+ Slog.i(TAG, "Removed votes with priority=" + priority
+ + " for displays=" + removedVotesDisplayIds);
+ }
+ int removedVotesSize = removedVotesDisplayIds.size();
+ if (removedVotesSize > 0) {
+ if (mVotesStatsReporter != null) {
+ for (int i = 0; i < removedVotesSize; i++) {
+ mVotesStatsReporter.reportVoteChanged(
+ removedVotesDisplayIds.get(i), priority, null);
+ }
+ }
+ mListener.onChanged();
+ }
+ }
+
/** dump class values, for debugging */
void dump(@NonNull PrintWriter pw) {
SparseArray<SparseArray<Vote>> votesByDisplayLocal = new SparseArray<>();
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index b8ae737..f21fd41 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -146,6 +146,10 @@
SPLIT_SCREEN_NAVIGATION(
FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION,
"SPLIT_SCREEN_NAVIGATION"),
+
+ CHANGE_SPLITSCREEN_FOCUS(
+ FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS,
+ "CHANGE_SPLITSCREEN_FOCUS"),
TRIGGER_BUG_REPORT(
FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT,
"TRIGGER_BUG_REPORT"),
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index a100fe0..29cccdf 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -74,7 +74,6 @@
@GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
@GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
- @GuardedBy("ImfLock.class") private int mCurSeq;
@GuardedBy("ImfLock.class") private boolean mVisibleBound;
@GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
@GuardedBy("ImfLock.class") private boolean mSupportsConnectionlessStylusHw;
@@ -195,27 +194,6 @@
}
/**
- * The current binding sequence number, incremented every time there is
- * a new bind performed.
- */
- @GuardedBy("ImfLock.class")
- int getSequenceNumber() {
- return mCurSeq;
- }
-
- /**
- * Increase the current binding sequence number by one.
- * Reset to 1 on overflow.
- */
- @GuardedBy("ImfLock.class")
- void advanceSequenceNumber() {
- mCurSeq += 1;
- if (mCurSeq <= 0) {
- mCurSeq = 1;
- }
- }
-
- /**
* If non-null, this is the input method service we are currently connected
* to.
*/
@@ -435,9 +413,11 @@
mLastBindTime = SystemClock.uptimeMillis();
addFreshWindowToken();
+ final UserData monitor = UserData.getOrCreate(
+ mService.getCurrentImeUserIdLocked());
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, null, mCurId, mCurSeq, false);
+ null, null, null, mCurId, monitor.mSequence.getSequenceNumber(), false);
}
Slog.w(InputMethodManagerService.TAG,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 50d2c63..d0a83a6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -479,7 +479,8 @@
*/
@GuardedBy("ImfLock.class")
private int getSequenceNumberLocked() {
- return mBindingController.getSequenceNumber();
+ final UserData monitor = UserData.getOrCreate(mCurrentUserId);
+ return monitor.mSequence.getSequenceNumber();
}
/**
@@ -488,7 +489,8 @@
*/
@GuardedBy("ImfLock.class")
private void advanceSequenceNumberLocked() {
- mBindingController.advanceSequenceNumber();
+ final UserData monitor = UserData.getOrCreate(mCurrentUserId);
+ monitor.mSequence.advanceSequenceNumber();
}
@GuardedBy("ImfLock.class")
@@ -558,10 +560,13 @@
private InputMethodSubtype mCurrentSubtype;
/**
- * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
+ * Map of window perceptible states indexed by their associated window tokens.
+ *
+ * The value {@code true} indicates that IME has not been mostly hidden via
+ * {@link android.view.InsetsController} for the given window.
*/
- @MultiUserUnawareField
- private boolean mCurPerceptible;
+ @GuardedBy("ImfLock.class")
+ private final WeakHashMap<IBinder, Boolean> mFocusedWindowPerceptible = new WeakHashMap<>();
/**
* Set to true if our ServiceConnection is currently actively bound to
@@ -1366,6 +1371,7 @@
// InputMethodSettingsRepository should be initialized before buildInputMethodListLocked
InputMethodSettingsRepository.initialize(mHandler, mContext);
AdditionalSubtypeMapRepository.initialize(mHandler, mContext);
+ UserData.initialize(mHandler);
mCurrentUserId = mActivityManagerInternal.getCurrentUserId();
@@ -2841,12 +2847,16 @@
+ " inv: " + (vis & InputMethodService.IME_INVISIBLE)
+ " displayId: " + mCurTokenDisplayId);
}
+ final IBinder focusedWindowToken = mImeBindingState != null
+ ? mImeBindingState.mFocusedWindow : null;
+ final Boolean windowPerceptible = focusedWindowToken != null
+ ? mFocusedWindowPerceptible.get(focusedWindowToken) : null;
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
// all updateSystemUi happens on system privilege.
final long ident = Binder.clearCallingIdentity();
try {
- if (!mCurPerceptible) {
+ if (windowPerceptible != null && !windowPerceptible) {
if ((vis & InputMethodService.IME_VISIBLE) != 0) {
vis &= ~InputMethodService.IME_VISIBLE;
vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
@@ -3313,11 +3323,12 @@
Binder.withCleanCallingIdentity(() -> {
Objects.requireNonNull(windowToken, "windowToken must not be null");
synchronized (ImfLock.class) {
+ Boolean windowPerceptible = mFocusedWindowPerceptible.get(windowToken);
if (mImeBindingState.mFocusedWindow != windowToken
- || mCurPerceptible == perceptible) {
+ || (windowPerceptible != null && windowPerceptible == perceptible)) {
return;
}
- mCurPerceptible = perceptible;
+ mFocusedWindowPerceptible.put(windowToken, windowPerceptible);
updateSystemUiLocked();
}
});
@@ -3695,7 +3706,7 @@
}
mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
- mCurPerceptible = true;
+ mFocusedWindowPerceptible.put(windowToken, true);
// We want to start input before showing the IME, but after closing
// it. We want to do this after closing it to help the IME disappear
@@ -5546,9 +5557,11 @@
public void reportImeControl(@Nullable IBinder windowToken) {
synchronized (ImfLock.class) {
if (mImeBindingState.mFocusedWindow != windowToken) {
- // mCurPerceptible was set by the focused window, but it is no longer in
- // control, so we reset mCurPerceptible.
- mCurPerceptible = true;
+ // A perceptible value was set for the focused window, but it is no longer in
+ // control, so we reset the perceptible for the window passed as argument.
+ // TODO(b/314149476): Investigate whether this logic is still relevant, if not
+ // then consider removing using concurrent_input_methods feature flag.
+ mFocusedWindowPerceptible.put(windowToken, true);
}
}
}
@@ -5849,11 +5862,11 @@
p.println(" curSession=" + c.mCurSession);
};
mClientController.forAllClients(clientControllerDump);
-
+ p.println(" mCurrentUserId=" + mCurrentUserId);
p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
- p.println(" mCurPerceptible=" + mCurPerceptible);
+ p.println(" mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
mImeBindingState.dump(" ", p);
p.println(" mCurId=" + getCurIdLocked() + " mHaveConnection=" + hasConnectionLocked()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
@@ -5875,8 +5888,6 @@
? Arrays.toString(mStylusIds.toArray()) : ""));
p.println(" mSwitchingController:");
mSwitchingController.dump(p);
- p.println(" mSettings:");
- settings.dump(p, " ");
p.println(" mStartInputHistory:");
mStartInputHistory.dump(pw, " ");
diff --git a/services/core/java/com/android/server/inputmethod/Sequence.java b/services/core/java/com/android/server/inputmethod/Sequence.java
new file mode 100644
index 0000000..05e31ce
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/Sequence.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import com.android.internal.annotations.GuardedBy;
+
+/**
+ * A sequence number utility class that only generate positive numbers.
+ */
+final class Sequence {
+
+ private final Object mLock = new Object();
+
+ private int mSequence;
+
+ int getSequenceNumber() {
+ synchronized (mLock) {
+ return mSequence;
+ }
+ }
+
+ @GuardedBy("ImfLock.class")
+ void advanceSequenceNumber() {
+ synchronized (mLock) {
+ mSequence++;
+ if (mSequence <= 0) {
+ mSequence = 1;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/UserData.java b/services/core/java/com/android/server/inputmethod/UserData.java
new file mode 100644
index 0000000..fc2a422
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/UserData.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.LocalServices;
+import com.android.server.pm.UserManagerInternal;
+
+final class UserData {
+
+ @NonNull
+ private static final SparseArray<UserData> sPerUserMonitor = new SparseArray<>();
+
+ @UserIdInt
+ final int mUserId;
+
+ @GuardedBy("ImfLock.class")
+ final Sequence mSequence = new Sequence();
+
+ /**
+ * Not intended to be instantiated.
+ */
+ private UserData(int userId) {
+ mUserId = userId;
+ }
+
+ @GuardedBy("ImfLock.class")
+ static UserData getOrCreate(@UserIdInt int userId) {
+ UserData monitor = sPerUserMonitor.get(userId);
+ if (monitor == null) {
+ monitor = new UserData(userId);
+ sPerUserMonitor.put(userId, monitor);
+ }
+ return monitor;
+ }
+
+ static void initialize(Handler handler) {
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+ userManagerInternal.addUserLifecycleListener(
+ new UserManagerInternal.UserLifecycleListener() {
+ @Override
+ public void onUserRemoved(UserInfo user) {
+ final int userId = user.id;
+ handler.post(() -> {
+ synchronized (ImfLock.class) {
+ sPerUserMonitor.remove(userId);
+ }
+ });
+ }
+
+ @Override
+ public void onUserCreated(UserInfo user, Object unusedToken) {
+ final int userId = user.id;
+ handler.post(() -> {
+ synchronized (ImfLock.class) {
+ getOrCreate(userId);
+ }
+ });
+ }
+ });
+ synchronized (ImfLock.class) {
+ for (int userId : userManagerInternal.getUserIds()) {
+ getOrCreate(userId);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 97ce77c..18b495b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -340,8 +340,6 @@
static final String TAG = NetworkPolicyLogger.TAG;
private static final boolean LOGD = NetworkPolicyLogger.LOGD;
private static final boolean LOGV = NetworkPolicyLogger.LOGV;
- // TODO: b/304347838 - Remove once the feature is in staging.
- private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
/**
* No opportunistic quota could be calculated from user data plan or data settings.
@@ -1070,8 +1068,7 @@
}
// The flag is boot-stable.
- mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK
- && Flags.networkBlockedForTopSleepingAndAbove();
+ mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
if (mBackgroundNetworkRestricted) {
// Firewall rules and UidBlockedState will get updated in
// updateRulesForGlobalChangeAL below.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9e4ea6a..3eeeae7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -148,6 +148,7 @@
import android.os.storage.StorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.system.ErrnoException;
import android.system.Int64Ref;
@@ -2364,8 +2365,21 @@
assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
assertNoWriteFileTransfersOpenLocked();
- final boolean isSecureFrpEnabled =
- Global.getInt(mContext.getContentResolver(), Global.SECURE_FRP_MODE, 0) == 1;
+ boolean isSecureFrpEnabled;
+ if (android.security.Flags.frpEnforcement()) {
+ PersistentDataBlockManager pdbManager =
+ mContext.getSystemService(PersistentDataBlockManager.class);
+ if (pdbManager == null) {
+ // Some devices may not support FRP. In that case, we can't block the install
+ // accordingly.
+ isSecureFrpEnabled = false;
+ } else {
+ isSecureFrpEnabled = pdbManager.isFactoryResetProtectionActive();
+ }
+ } else {
+ isSecureFrpEnabled = Global.getInt(mContext.getContentResolver(),
+ Global.SECURE_FRP_MODE, 0) == 1;
+ }
if (isSecureFrpEnabled
&& !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c9fd261..1919137 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -217,8 +217,7 @@
private static final Set<String> IMMUTABLE_BY_OWNERS = Sets.newArraySet(
UserManager.DISALLOW_RECORD_AUDIO,
UserManager.DISALLOW_WALLPAPER,
- UserManager.DISALLOW_OEM_UNLOCK,
- UserManager.DISALLOW_ADD_PRIVATE_PROFILE
+ UserManager.DISALLOW_OEM_UNLOCK
);
/**
@@ -293,7 +292,8 @@
UserManager.DISALLOW_USB_FILE_TRANSFER,
UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
UserManager.DISALLOW_UNMUTE_MICROPHONE,
- UserManager.DISALLOW_CONFIG_DEFAULT_APPS
+ UserManager.DISALLOW_CONFIG_DEFAULT_APPS,
+ UserManager.DISALLOW_ADD_PRIVATE_PROFILE
);
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 266418f..9e31748 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1506,9 +1506,7 @@
}
private void stemPrimaryPress(int count) {
- if (DEBUG_INPUT) {
- Slog.d(TAG, "stemPrimaryPress: " + count);
- }
+ Slog.d(TAG, "stemPrimaryPress: " + count);
if (count == 3) {
stemPrimaryTriplePressAction(mTriplePressOnStemPrimaryBehavior);
} else if (count == 2) {
@@ -1519,22 +1517,18 @@
}
private void stemPrimarySinglePressAction(int behavior) {
- if (DEBUG_INPUT) {
- Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
- }
+ Slog.d(TAG, "stemPrimarySinglePressAction: behavior=" + behavior);
if (behavior == SHORT_PRESS_PRIMARY_NOTHING) return;
final boolean keyguardActive = mKeyguardDelegate != null && mKeyguardDelegate.isShowing();
if (keyguardActive) {
// If keyguarded then notify the keyguard.
mKeyguardDelegate.onSystemKeyPressed(KeyEvent.KEYCODE_STEM_PRIMARY);
+ Slog.d(TAG, "stemPrimarySinglePressAction: skip due to keyguard");
return;
}
switch (behavior) {
case SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS:
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing stem primary short press action behavior.");
- }
Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
allAppsIntent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
@@ -1542,12 +1536,6 @@
startActivityAsUser(allAppsIntent, UserHandle.CURRENT_OR_SELF);
break;
case SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY:
- if (DEBUG_INPUT) {
- Slog.d(
- TAG,
- "Executing stem primary short press action behavior for launching "
- + "target activity.");
- }
if (mPrimaryShortPressTargetActivity != null) {
Intent targetActivityIntent = new Intent();
targetActivityIntent.setComponent(mPrimaryShortPressTargetActivity);
@@ -1578,13 +1566,11 @@
}
private void stemPrimaryDoublePressAction(int behavior) {
+ Slog.d(TAG, "stemPrimaryDoublePressAction: " + behavior);
switch (behavior) {
case DOUBLE_PRESS_PRIMARY_NOTHING:
break;
case DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP:
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing stem primary double press action behavior.");
- }
final boolean keyguardActive = mKeyguardDelegate == null
? false
: mKeyguardDelegate.isShowing();
@@ -1596,13 +1582,11 @@
}
private void stemPrimaryTriplePressAction(int behavior) {
+ Slog.d(TAG, "stemPrimaryTriplePressAction: " + behavior);
switch (behavior) {
case TRIPLE_PRESS_PRIMARY_NOTHING:
break;
case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing stem primary triple press action behavior.");
- }
mTalkbackShortcutController.toggleTalkback(mCurrentUserId);
if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */
@@ -1614,9 +1598,7 @@
}
private void stemPrimaryLongPress(long eventTime) {
- if (DEBUG_INPUT) {
- Slog.d(TAG, "Executing stem primary long press action behavior.");
- }
+ Slog.d(TAG, "stemPrimaryLongPress: " + mLongPressOnStemPrimaryBehavior);
switch (mLongPressOnStemPrimaryBehavior) {
case LONG_PRESS_PRIMARY_NOTHING:
@@ -1696,10 +1678,19 @@
return mLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING;
}
+ /** Determine whether the device has any stem primary behaviors. */
private boolean hasStemPrimaryBehavior() {
+ // Read the default stem behaviors from the XML config to determine whether stem primary
+ // behaviors are supported in this build. If they are supported, then the behaviors may be
+ // overridden at runtime through their respective Settings overrides. If they are not
+ // supported, the Settings overrides will not apply.
+ final int defaultShortPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior);
+ final int defaultLongPressOnStemPrimaryBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior);
return getMaxMultiPressStemPrimaryCount() > 1
- || hasLongPressOnStemPrimaryBehavior()
- || mShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
+ || defaultLongPressOnStemPrimaryBehavior != LONG_PRESS_PRIMARY_NOTHING
+ || defaultShortPressOnStemPrimaryBehavior != SHORT_PRESS_PRIMARY_NOTHING;
}
private void interceptScreenshotChord(int source, long pressDelay) {
@@ -2787,6 +2778,7 @@
KeyEvent.KEYCODE_STEM_PRIMARY,
eventTime,
() -> {
+ Slog.d(TAG, "StemPrimaryKeyRule: executing deferred onKeyUp");
// Save the info of the focused task on screen. This may be used
// later to bring the current focused task back to top. For
// example, stem primary triple press enables the A11y interface
@@ -3526,6 +3518,9 @@
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
true /* leftOrTop */);
logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+ } else if (event.isAltPressed()) {
+ setSplitscreenFocus(true /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.CHANGE_SPLITSCREEN_FOCUS);
} else {
logKeyboardSystemsEvent(event, KeyboardLogEvent.BACK);
injectBackGesture(event.getDownTime());
@@ -3534,11 +3529,17 @@
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
- moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
- false /* leftOrTop */);
- logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
- return true;
+ if (firstDown && event.isMetaPressed()) {
+ if (event.isCtrlPressed()) {
+ moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
+ false /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
+ return true;
+ } else if (event.isAltPressed()) {
+ setSplitscreenFocus(false /* leftOrTop */);
+ logKeyboardSystemsEvent(event, KeyboardLogEvent.CHANGE_SPLITSCREEN_FOCUS);
+ return true;
+ }
}
break;
case KeyEvent.KEYCODE_SLASH:
@@ -4398,6 +4399,13 @@
}
}
+ private void setSplitscreenFocus(boolean leftOrTop) {
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.setSplitscreenFocus(leftOrTop);
+ }
+ }
+
void launchHomeFromHotKey(int displayId) {
launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b50e2bf..6ff8cf3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,6 +56,7 @@
import android.database.ContentObserver;
import android.hardware.SensorManager;
import android.hardware.SystemSensorManager;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.display.DisplayManagerInternal;
@@ -7152,9 +7153,10 @@
private int mDeviceState = DeviceStateManager.INVALID_DEVICE_STATE_IDENTIFIER;
@Override
- public void onStateChanged(int deviceState) {
- if (mDeviceState != deviceState) {
- mDeviceState = deviceState;
+ public void onDeviceStateChanged(@NonNull DeviceState deviceState) {
+ int stateIdentifier = deviceState.getIdentifier();
+ if (mDeviceState != stateIdentifier) {
+ mDeviceState = stateIdentifier;
// Device-state interactions are applied to the default display so that they
// are reflected only with the default power group.
userActivityInternal(Display.DEFAULT_DISPLAY, mClock.uptimeMillis(),
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index c73f89c..f7c236a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -238,6 +238,14 @@
void moveFocusedTaskToStageSplit(int displayId, boolean leftOrTop);
/**
+ * Change the split screen focus to the left / top app or the right / bottom app based on
+ * {@param leftOrTop}.
+ *
+ * @see com.android.internal.statusbar.IStatusBar#setSplitscreenFocus
+ */
+ void setSplitscreenFocus(boolean leftOrTop);
+
+ /**
* Shows the media output switcher dialog.
*
* @param packageName of the session for which the output switcher is shown.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 214dbe0..7b3e237 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -830,6 +830,15 @@
}
@Override
+ public void setSplitscreenFocus(boolean leftOrTop) {
+ IStatusBar bar = mBar;
+ if (bar != null) {
+ try {
+ bar.setSplitscreenFocus(leftOrTop);
+ } catch (RemoteException ex) { }
+ }
+ }
+ @Override
public void enterDesktop(int displayId) {
IStatusBar bar = mBar;
if (bar != null) {
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 7206c03..ecd140e 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -89,7 +89,7 @@
@NonNull private final TelephonyManager mTelephonyManager;
@NonNull private final SubscriptionManager mSubscriptionManager;
- @NonNull private final CarrierConfigManager mCarrierConfigManager;
+ @Nullable private final CarrierConfigManager mCarrierConfigManager;
@NonNull private final ActiveDataSubscriptionIdListener mActiveDataSubIdListener;
@@ -158,8 +158,10 @@
mSubscriptionManager.addOnSubscriptionsChangedListener(
executor, mSubscriptionChangedListener);
mTelephonyManager.registerTelephonyCallback(executor, mActiveDataSubIdListener);
- mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
- mCarrierConfigChangeListener);
+ if (mCarrierConfigManager != null) {
+ mCarrierConfigManager.registerCarrierConfigChangeListener(executor,
+ mCarrierConfigChangeListener);
+ }
registerCarrierPrivilegesCallbacks();
}
@@ -200,7 +202,10 @@
mContext.unregisterReceiver(this);
mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionChangedListener);
mTelephonyManager.unregisterTelephonyCallback(mActiveDataSubIdListener);
- mCarrierConfigManager.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener);
+ if (mCarrierConfigManager != null) {
+ mCarrierConfigManager.unregisterCarrierConfigChangeListener(
+ mCarrierConfigChangeListener);
+ }
unregisterCarrierPrivilegesCallbacks();
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 1a63f14..5184e49 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -228,11 +228,8 @@
private int mAppTransitionState = APP_STATE_IDLE;
private final ArrayList<AppTransitionListener> mListeners = new ArrayList<>();
- private KeyguardExitAnimationStartListener mKeyguardExitAnimationStartListener;
private final ExecutorService mDefaultExecutor = Executors.newSingleThreadExecutor();
- private final boolean mGridLayoutRecentsEnabled;
-
private final int mDefaultWindowAnimationStyleResId;
private boolean mOverrideTaskTransition;
@@ -249,8 +246,6 @@
mTransitionAnimation = new TransitionAnimation(
context, ProtoLog.isEnabled(WM_DEBUG_ANIM), TAG);
- mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
-
final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
com.android.internal.R.styleable.Window);
mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
@@ -493,11 +488,6 @@
mListeners.remove(listener);
}
- void registerKeygaurdExitAnimationStartListener(
- KeyguardExitAnimationStartListener listener) {
- mKeyguardExitAnimationStartListener = listener;
- }
-
public void notifyAppTransitionFinishedLocked(IBinder token) {
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAppTransitionFinishedLocked(token);
@@ -1595,14 +1585,6 @@
return mNextAppTransitionRequests.contains(transit);
}
- /**
- * @return whether the transition should show the thumbnail being scaled down.
- */
- private boolean shouldScaleDownThumbnailTransition(int uiMode, int orientation) {
- return mGridLayoutRecentsEnabled
- || orientation == Configuration.ORIENTATION_PORTRAIT;
- }
-
private void handleAppTransitionTimeout() {
synchronized (mService.mGlobalLock) {
final DisplayContent dc = mDisplayContent;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 56e6107..1dcfde4 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3521,6 +3521,9 @@
}
void enableHighPerfTransition(boolean enable) {
+ if (!mWmService.mSupportsHighPerfTransitions) {
+ return;
+ }
if (!explicitRefreshRateHints()) {
if (enable) {
getPendingTransaction().setEarlyWakeupStart();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e789fec..16f7373 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -117,6 +117,7 @@
import android.view.Surface;
import android.view.View;
import android.view.ViewDebug;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowLayout;
@@ -969,19 +970,30 @@
break;
case TYPE_BASE_APPLICATION:
-
- // A non-translucent main app window isn't allowed to fit insets or display cutouts,
- // as it would create a hole on the display!
if (attrs.isFullscreen() && win.mActivityRecord != null
&& win.mActivityRecord.fillsParent()
- && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
- && (attrs.getFitInsetsTypes() != 0
- || (attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
- && attrs.layoutInDisplayCutoutMode
- != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS)) {
- throw new IllegalArgumentException("Illegal attributes: Main activity window"
- + " that isn't translucent trying to fit insets or display cutouts."
- + " attrs=" + attrs);
+ && (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0) {
+ if (attrs.getFitInsetsTypes() != 0) {
+ // A non-translucent main app window isn't allowed to fit insets,
+ // as it would create a hole on the display!
+ throw new IllegalArgumentException("Illegal attributes: Main window of "
+ + win.mActivityRecord.getName() + " that isn't translucent trying"
+ + " to fit insets. fitInsetsTypes=" + WindowInsets.Type.toString(
+ attrs.getFitInsetsTypes()));
+ }
+ if ((attrs.privateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0
+ && attrs.layoutInDisplayCutoutMode
+ != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
+ // A non-translucent main window of the app enforced to go edge-to-edge
+ // isn't allowed to fit display cutout, or it will cause software bezels.
+ throw new IllegalArgumentException("Illegal attributes: Main window of "
+ + win.mActivityRecord.getName() + " that isn't translucent and"
+ + " targets SDK level " + win.mActivityRecord.mTargetSdk
+ + " (>= 35) trying to specify layoutInDisplayCutoutMode as '"
+ + WindowManager.LayoutParams.layoutInDisplayCutoutModeToString(
+ attrs.layoutInDisplayCutoutMode)
+ + "' instead of 'always'");
+ }
}
break;
}
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index 34ea4ac..8116f68 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.View.DRAG_FLAG_GLOBAL;
import static android.view.View.DRAG_FLAG_GLOBAL_SAME_APPLICATION;
import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;
@@ -36,6 +37,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.Display;
import android.view.DragEvent;
@@ -52,6 +54,7 @@
import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
import java.util.Objects;
+import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -386,6 +389,9 @@
+ "(listener=" + mGlobalDragListener + ", flags=" + mDragState.mFlags + ")");
return false;
}
+ final int traceCookie = new Random().nextInt();
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#notifyUnhandledDrop",
+ traceCookie);
if (DEBUG_DRAG) Slog.d(TAG_WM, "Sending DROP to unhandled listener (" + reason + ")");
try {
// Schedule timeout for the unhandled drag listener to call back
@@ -396,6 +402,8 @@
if (DEBUG_DRAG) Slog.d(TAG_WM, "Unhandled listener finished handling DROP");
synchronized (mService.mGlobalLock) {
onUnhandledDropCallback(consumedByListener);
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
+ "DragDropController#notifyUnhandledDrop", traceCookie);
}
}
});
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index b266caa..5ed343a 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -20,6 +20,7 @@
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -49,6 +50,7 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
@@ -258,6 +260,7 @@
}
mNotifiedWindows.clear();
mDragInProgress = false;
+ Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_ENDED");
}
// Take the cursor back if it has been changed.
@@ -343,6 +346,18 @@
if (mAnimator != null) {
return false;
}
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DROP");
+ return reportDropWindowLockInner(token, x, y);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ }
+
+ private boolean reportDropWindowLockInner(IBinder token, float x, float y) {
+ if (mAnimator != null) {
+ return false;
+ }
final WindowState touchedWin = mService.mInputToWindowMap.get(token);
final DragEvent unhandledDropEvent = createDropEvent(x, y, null /* touchedWin */,
@@ -355,10 +370,12 @@
return true;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#noWindow");
// "drop" outside a valid window -- no recipient to apply a timeout to, and we can send
// the drag-ended message immediately.
endDragLocked(false /* consumed */, false /* relinquishDragSurfaceToDropTarget */);
if (DEBUG_DRAG) Slog.d(TAG_WM, "Drop outside a valid window " + touchedWin);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return false;
}
@@ -367,6 +384,7 @@
final IBinder clientToken = touchedWin.mClient.asBinder();
final DragEvent event = createDropEvent(x, y, touchedWin, false /* includePrivateInfo */);
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "DragDropController#dispatchDrop");
touchedWin.mClient.dispatchDragEvent(event);
// 5 second timeout for this window to respond to the drop
@@ -380,6 +398,7 @@
if (MY_PID != touchedWin.mSession.mPid) {
event.recycle();
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
mToken = clientToken;
mUnhandledDropEvent = unhandledDropEvent;
@@ -471,6 +490,7 @@
/* call out to each visible window/session informing it about the drag
*/
void broadcastDragStartedLocked(final float touchX, final float touchY) {
+ Trace.instant(TRACE_TAG_WINDOW_MANAGER, "DragDropController#DRAG_STARTED");
mOriginalX = mCurrentX = touchX;
mOriginalY = mCurrentY = touchY;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index a24c02a..5d613cf 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -22,7 +22,6 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
@@ -132,7 +131,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -196,8 +194,7 @@
private final boolean mIsOverrideCameraCompatDisableRefreshEnabled;
// Corresponds to OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE
private final boolean mIsOverrideCameraCompatEnableRefreshViaPauseEnabled;
- // Corresponds to OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT
- private final boolean mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled;
+
// Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION
private final boolean mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled;
// Corresponds to OVERRIDE_ENABLE_COMPAT_IGNORE_ORIENTATION_REQUEST_WHEN_LOOP_DETECTED
@@ -324,15 +321,15 @@
PROPERTY_COMPAT_ENABLE_FAKE_FOCUS);
mBooleanPropertyCameraCompatAllowForceRotation =
readComponentProperty(packageManager, mActivityRecord.packageName,
- mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION);
mBooleanPropertyCameraCompatAllowRefresh =
readComponentProperty(packageManager, mActivityRecord.packageName,
- mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH);
mBooleanPropertyCameraCompatEnableRefreshViaPause =
readComponentProperty(packageManager, mActivityRecord.packageName,
- mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(),
PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
mBooleanPropertyAllowOrientationOverride =
@@ -354,11 +351,11 @@
mBooleanPropertyAllowUserAspectRatioOverride =
readComponentProperty(packageManager, mActivityRecord.packageName,
- mLetterboxConfiguration::isUserAppAspectRatioSettingsEnabled,
+ () -> mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled(),
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE);
mBooleanPropertyAllowUserAspectRatioFullscreenOverride =
readComponentProperty(packageManager, mActivityRecord.packageName,
- mLetterboxConfiguration::isUserAppAspectRatioFullscreenEnabled,
+ () -> mLetterboxConfiguration.isUserAppAspectRatioFullscreenEnabled(),
PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE);
mIsOverrideAnyOrientationEnabled = isCompatChangeEnabled(OVERRIDE_ANY_ORIENTATION);
@@ -383,8 +380,6 @@
isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH);
mIsOverrideCameraCompatEnableRefreshViaPauseEnabled =
isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE);
- mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled =
- isCompatChangeEnabled(OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT);
mIsOverrideEnableCompatIgnoreRequestedOrientationEnabled =
isCompatChangeEnabled(OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION);
@@ -764,7 +759,8 @@
*/
boolean shouldRefreshActivityForCameraCompat() {
return shouldEnableWithOptOutOverrideAndProperty(
- /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ /* gatingCondition */ () -> mLetterboxConfiguration
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatDisableRefreshEnabled,
mBooleanPropertyCameraCompatAllowRefresh);
}
@@ -785,7 +781,8 @@
*/
boolean shouldRefreshActivityViaPauseForCameraCompat() {
return shouldEnableWithOverrideAndProperty(
- /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ /* gatingCondition */ () -> mLetterboxConfiguration
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatEnableRefreshViaPauseEnabled,
mBooleanPropertyCameraCompatEnableRefreshViaPause);
}
@@ -803,34 +800,12 @@
*/
boolean shouldForceRotateForCameraCompat() {
return shouldEnableWithOptOutOverrideAndProperty(
- /* gatingCondition */ mLetterboxConfiguration::isCameraCompatTreatmentEnabled,
+ /* gatingCondition */ () -> mLetterboxConfiguration
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatDisableForceRotationEnabled,
mBooleanPropertyCameraCompatAllowForceRotation);
}
- /**
- * Whether activity is eligible for camera compatibility free-form treatment.
- *
- * <p>The treatment is applied to a fixed-orientation camera activity in free-form windowing
- * mode. The treatment letterboxes or pillarboxes the activity to the expected orientation and
- * provides changes to the camera and display orientation signals to match those expected on a
- * portrait device in that orientation (for example, on a standard phone).
- *
- * <p>The treatment is enabled when the following conditions are met:
- * <ul>
- * <li>Property gating the camera compatibility free-form treatment is enabled.
- * <li>Activity isn't opted out by the device manufacturer with override or by the app
- * developers with the component property.
- * </ul>
- */
- boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return shouldEnableWithOptOutOverrideAndProperty(
- /* gatingCondition */ ()-> Flags.cameraCompatForFreeform(),
- mIsOverrideCameraCompatDisableFreeformWindowingTreatmentEnabled,
- // TODO(b/328616176): add a manifest override for developers.
- null);
- }
-
private boolean isCameraCompatTreatmentActive() {
DisplayContent displayContent = mActivityRecord.mDisplayContent;
if (displayContent == null) {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index dd14642..33588a0 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -364,11 +364,6 @@
com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
mMaxNumVisibleTasks = res.getInteger(
com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
- } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
- mMinNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
- mMaxNumVisibleTasks = res.getInteger(
- com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
} else {
mMinNumVisibleTasks = res.getInteger(
com.android.internal.R.integer.config_minNumVisibleRecentTasks);
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 77319cc..acc6330 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -620,14 +620,6 @@
public abstract void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener);
/**
- * Registers a listener to be notified to start the keyguard exit animation.
- *
- * @param listener The listener to register.
- */
- public abstract void registerKeyguardExitAnimationStartListener(
- KeyguardExitAnimationStartListener listener);
-
- /**
* Reports that the password for the given user has changed.
*/
public abstract void reportPasswordChanged(int userId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 40b1b20..207b1bb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -538,7 +538,7 @@
final boolean mHasPermanentDpad;
final long mDrawLockTimeoutMillis;
final boolean mAllowAnimationsInLowPowerMode;
-
+ final boolean mSupportsHighPerfTransitions;
final boolean mAllowBootMessages;
// Indicates whether the Assistant should show on top of the Dream (respectively, above
@@ -1181,6 +1181,8 @@
com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);
mMaxUiWidth = context.getResources().getInteger(
com.android.internal.R.integer.config_maxUiWidth);
+ mSupportsHighPerfTransitions = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_deviceSupportsHighPerfTransitions);
mDisableTransitionAnimation = context.getResources().getBoolean(
com.android.internal.R.bool.config_disableTransitionAnimation);
mPerDisplayFocusEnabled = context.getResources().getBoolean(
@@ -1192,6 +1194,7 @@
final boolean isScreenSizeDecoupledFromStatusBarAndCutout = context.getResources()
.getBoolean(R.bool.config_decoupleStatusBarAndDisplayCutoutFromScreenSize)
&& mFlags.mAllowsScreenSizeDecoupledFromStatusBarAndCutout;
+
if (mFlags.mInsetsDecoupledConfiguration) {
mDecorTypes = 0;
mConfigTypes = 0;
@@ -8094,15 +8097,6 @@
}
@Override
- public void registerKeyguardExitAnimationStartListener(
- KeyguardExitAnimationStartListener listener) {
- synchronized (mGlobalLock) {
- getDefaultDisplayContentLocked().mAppTransition
- .registerKeygaurdExitAnimationStartListener(listener);
- }
- }
-
- @Override
public void reportPasswordChanged(int userId) {
mKeyguardDisableHandler.updateKeyguardEnabled(userId);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 46bac16..2b337ae 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5700,7 +5700,8 @@
// window becomes visible while the sync group is still active.
return true;
}
- if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mLastConfigReportedToClient && isDrawn()) {
+ if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW && mLastConfigReportedToClient && isDrawn()
+ && mPrepareSyncSeqId <= 0) {
// Complete the sync state immediately for a drawn window that doesn't need to redraw.
onSyncFinishedDrawing();
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index b38a2f9..d0df2b2 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -168,6 +168,10 @@
<xs:element type="nonNegativeDecimal" name="screenBrightnessCapForWearBedtimeMode">
<xs:annotation name="final"/>
</xs:element>
+ <!-- Timeout after which we reduce the refresh rate if the screen has been idle, in order to save power. -->
+ <xs:element type="idleScreenRefreshRateTimeout" name="idleScreenRefreshRateTimeout" minOccurs="0">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -772,6 +776,30 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="idleScreenRefreshRateTimeout">
+ <xs:element name="luxThresholds" type="idleScreenRefreshRateTimeoutLuxThresholds" minOccurs="0">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+
+ <!-- Lux based timeout after which we reduce the refresh rate if the screen has been idle, in order to save power. -->
+ <xs:complexType name="idleScreenRefreshRateTimeoutLuxThresholds">
+ <xs:sequence>
+ <xs:element name="point" type="idleScreenRefreshRateTimeoutLuxThresholdPoint" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+
+ <!-- Represents a tuple of lux and timeout(in ms), which represents the timeout value for the lux in
+ the [luxValue, nextLuxValue (INF if missing)) -->
+ <xs:complexType name="idleScreenRefreshRateTimeoutLuxThresholdPoint">
+ <xs:element name="lux" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element name="timeout" type="xs:nonNegativeInteger">
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:complexType>
+
<!-- Predefined type names as defined by
AutomaticBrightnessController.AutomaticBrightnessMode -->
<xs:simpleType name="AutoBrightnessModeName">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index b329db4..00dc908 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -111,6 +111,7 @@
method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
method @Nullable public final com.android.server.display.config.HdrBrightnessConfig getHdrBrightnessConfig();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
+ method public final com.android.server.display.config.IdleScreenRefreshRateTimeout getIdleScreenRefreshRateTimeout();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public com.android.server.display.config.LuxThrottling getLuxThrottling();
method @Nullable public final String getName();
@@ -146,6 +147,7 @@
method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public final void setHdrBrightnessConfig(@Nullable com.android.server.display.config.HdrBrightnessConfig);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
+ method public final void setIdleScreenRefreshRateTimeout(com.android.server.display.config.IdleScreenRefreshRateTimeout);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public void setLuxThrottling(com.android.server.display.config.LuxThrottling);
method public final void setName(@Nullable String);
@@ -222,6 +224,25 @@
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
+ public class IdleScreenRefreshRateTimeout {
+ ctor public IdleScreenRefreshRateTimeout();
+ method public final com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds getLuxThresholds();
+ method public final void setLuxThresholds(com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholds);
+ }
+
+ public class IdleScreenRefreshRateTimeoutLuxThresholdPoint {
+ ctor public IdleScreenRefreshRateTimeoutLuxThresholdPoint();
+ method public final java.math.BigInteger getLux();
+ method public final java.math.BigInteger getTimeout();
+ method public final void setLux(java.math.BigInteger);
+ method public final void setTimeout(java.math.BigInteger);
+ }
+
+ public class IdleScreenRefreshRateTimeoutLuxThresholds {
+ ctor public IdleScreenRefreshRateTimeoutLuxThresholds();
+ method public java.util.List<com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint> getPoint();
+ }
+
public class IntegerArray {
ctor public IntegerArray();
method public java.util.List<java.math.BigInteger> getItem();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 38ab765..cb87f7e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -79,7 +79,6 @@
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES;
-import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THEFT_DETECTION;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_TIME;
import static android.Manifest.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING;
@@ -93,6 +92,7 @@
import static android.Manifest.permission.MASTER_CLEAR;
import static android.Manifest.permission.NOTIFY_PENDING_SYSTEM_UPDATE;
import static android.Manifest.permission.QUERY_ADMIN_POLICY;
+import static android.Manifest.permission.QUERY_DEVICE_STOLEN_STATE;
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
@@ -18460,7 +18460,7 @@
return;
}
new CalculateHasIncompatibleAccountsTask().executeOnExecutor(
- calculateHasIncompatibleAccountsExecutor, null);
+ calculateHasIncompatibleAccountsExecutor);
}
@Nullable
@@ -22128,12 +22128,12 @@
}
@Override
- public boolean isTheftDetectionTriggered(String callerPackageName) {
+ public boolean isDevicePotentiallyStolen(String callerPackageName) {
final CallerIdentity caller = getCallerIdentity(callerPackageName);
if (!android.app.admin.flags.Flags.deviceTheftImplEnabled()) {
return false;
}
- enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),
+ enforcePermission(QUERY_DEVICE_STOLEN_STATE, caller.getPackageName(),
caller.getUserId());
return mInjector.binderWithCleanCallingIdentity(() ->
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c6189ed..3b2a3dd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -408,6 +408,8 @@
"com.android.server.searchui.SearchUiManagerService";
private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
"com.android.server.smartspace.SmartspaceManagerService";
+ private static final String CONTEXTUAL_SEARCH_MANAGER_SERVICE_CLASS =
+ "com.android.server.contextualsearch.ContextualSearchManagerService";
private static final String DEVICE_IDLE_CONTROLLER_CLASS =
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -2016,6 +2018,16 @@
Slog.d(TAG, "SmartspaceManagerService not defined by OEM or disabled by flag");
}
+ // Contextual search manager service
+ if (deviceHasConfigString(context,
+ R.string.config_defaultContextualSearchPackageName)) {
+ t.traceBegin("StartContextualSearchService");
+ mSystemServiceManager.startService(CONTEXTUAL_SEARCH_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+ } else {
+ Slog.d(TAG, "ContextualSearchManagerService not defined or disabled by flag");
+ }
+
t.traceBegin("InitConnectivityModuleConnector");
try {
ConnectivityModuleConnector.getInstance().init(context);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 2867041..35b69f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -54,6 +54,7 @@
import com.android.internal.R;
import com.android.server.display.config.HdrBrightnessData;
+import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.feature.DisplayManagerFlags;
@@ -108,6 +109,7 @@
when(mContext.getResources()).thenReturn(mResources);
when(mFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
when(mFlags.isSensorBasedBrightnessThrottlingEnabled()).thenReturn(true);
+ when(mFlags.isIdleScreenRefreshRateTimeoutEnabled()).thenReturn(true);
mockDeviceConfigs();
}
@@ -146,6 +148,8 @@
assertNull(mDisplayDeviceConfig.getProximitySensor().type);
assertNull(mDisplayDeviceConfig.getProximitySensor().name);
assertEquals(TEMPERATURE_TYPE_SKIN, mDisplayDeviceConfig.getTempSensor().type);
+ assertEquals(List.of(), mDisplayDeviceConfig
+ .getIdleScreenRefreshRateTimeoutLuxThresholdPoint());
assertNull(mDisplayDeviceConfig.getTempSensor().name);
assertTrue(mDisplayDeviceConfig.isAutoBrightnessAvailable());
}
@@ -226,6 +230,19 @@
assertNotNull(mDisplayDeviceConfig.getHostUsiVersion());
assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMajorVersion(), 2);
assertEquals(mDisplayDeviceConfig.getHostUsiVersion().getMinorVersion(), 0);
+
+ List<IdleScreenRefreshRateTimeoutLuxThresholdPoint>
+ idleScreenRefreshRateTimeoutLuxThresholdPoints =
+ mDisplayDeviceConfig.getIdleScreenRefreshRateTimeoutLuxThresholdPoint();
+ assertEquals(2, idleScreenRefreshRateTimeoutLuxThresholdPoints.size());
+ assertEquals(6, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(0).getLux()
+ .intValue());
+ assertEquals(1000, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(0)
+ .getTimeout().intValue());
+ assertEquals(10, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(1)
+ .getLux().intValue());
+ assertEquals(800, idleScreenRefreshRateTimeoutLuxThresholdPoints.get(1)
+ .getTimeout().intValue());
}
@Test
@@ -734,6 +751,8 @@
assertEquals(brightnessIntToFloat(35),
mDisplayDeviceConfig.getBrightnessCapForWearBedtimeMode(), ZERO_DELTA);
+ assertEquals(List.of(), mDisplayDeviceConfig
+ .getIdleScreenRefreshRateTimeoutLuxThresholdPoint());
}
@Test
@@ -1587,6 +1606,18 @@
+ "<screenBrightnessCapForWearBedtimeMode>"
+ "0.1"
+ "</screenBrightnessCapForWearBedtimeMode>"
+ + "<idleScreenRefreshRateTimeout>"
+ + "<luxThresholds>"
+ + "<point>"
+ + "<lux>6</lux>"
+ + "<timeout>1000</timeout>"
+ + "</point>"
+ + "<point>"
+ + "<lux>10</lux>"
+ + "<timeout>800</timeout>"
+ + "</point>"
+ + "</luxThresholds>"
+ + "</idleScreenRefreshRateTimeout>"
+ "</displayConfiguration>\n";
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 48fc407..869cec8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -78,6 +78,7 @@
import android.graphics.Rect;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.BrightnessInfo;
import android.hardware.display.Curve;
@@ -721,7 +722,9 @@
IDisplayManagerCallback displayChangesCallback = registerDisplayChangeCallback(
displayManager);
- listener.onStateChanged(123);
+ listener.onDeviceStateChanged(new DeviceState(
+ new DeviceState.Configuration.Builder(123 /* identifier */,
+ "TEST" /* name */).build()));
waitForIdleHandler(handler);
InOrder inOrder = inOrder(mMockWindowManagerInternal, displayChangesCallback);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 2939192..d0c7077 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -650,7 +650,6 @@
public void testDeviceShouldBePutToSleep() {
assertTrue(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
DEVICE_STATE_OPEN,
- /* isOverrideActive= */false,
/* isInteractive= */true,
/* isBootCompleted= */true));
}
@@ -661,7 +660,6 @@
assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
DEVICE_STATE_OPEN,
- /* isOverrideActive= */false,
/* isInteractive= */true,
/* isBootCompleted= */true));
}
@@ -670,21 +668,10 @@
public void testDeviceShouldNotBePutToSleep() {
assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_OPEN,
DEVICE_STATE_CLOSED,
- /* isOverrideActive= */false,
/* isInteractive= */true,
/* isBootCompleted= */true));
assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
INVALID_DEVICE_STATE_IDENTIFIER,
- /* isOverrideActive= */false,
- /* isInteractive= */true,
- /* isBootCompleted= */true));
- }
-
- @Test
- public void testDeviceShouldNotBePutToSleepDifferentBaseState() {
- assertFalse(mLogicalDisplayMapper.shouldDeviceBePutToSleep(DEVICE_STATE_CLOSED,
- DEVICE_STATE_OPEN,
- /* isOverrideActive= */true,
/* isInteractive= */true,
/* isBootCompleted= */true));
}
@@ -750,7 +737,7 @@
// We can only have one default display
assertEquals(DEFAULT_DISPLAY, id(display1));
- mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(0);
advanceTime(1000);
// The new state is not applied until the boot is completed
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
@@ -771,7 +758,7 @@
assertEquals("concurrent", mLogicalDisplayMapper.getDisplayLocked(device2)
.getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
- mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(1);
advanceTime(1000);
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -784,7 +771,7 @@
mLogicalDisplayMapper.getDisplayLocked(device2)
.getDisplayInfoLocked().thermalBrightnessThrottlingDataId);
- mLogicalDisplayMapper.setDeviceStateLocked(2, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(2);
advanceTime(1000);
assertFalse(mLogicalDisplayMapper.getDisplayLocked(device1).isEnabledLocked());
assertTrue(mLogicalDisplayMapper.getDisplayLocked(device2).isEnabledLocked());
@@ -861,7 +848,7 @@
// 3) Send DISPLAY_DEVICE_EVENT_CHANGE to inform the mapper of the new display state
// 4) Dispatch handler events.
mLogicalDisplayMapper.onBootCompleted();
- mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(0);
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
advanceTime(1000);
final int[] allDisplayIds = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -891,7 +878,7 @@
/* includeDisabled= */ false));
// Now do it again to go back to state 1
- mLogicalDisplayMapper.setDeviceStateLocked(1, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(1);
mDisplayDeviceRepo.onDisplayDeviceEvent(device3, DISPLAY_DEVICE_EVENT_CHANGED);
advanceTime(1000);
final int[] threeDisplaysEnabled = mLogicalDisplayMapper.getDisplayIdsLocked(
@@ -945,7 +932,7 @@
// We can only have one default display
assertEquals(DEFAULT_DISPLAY, id(display1));
- mLogicalDisplayMapper.setDeviceStateLocked(0, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(0);
advanceTime(1000);
mLogicalDisplayMapper.onBootCompleted();
advanceTime(1000);
@@ -964,11 +951,11 @@
/////////////////
private void finishBootAndFoldDevice() {
- mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_OPEN);
advanceTime(1000);
mLogicalDisplayMapper.onBootCompleted();
advanceTime(1000);
- mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED, false);
+ mLogicalDisplayMapper.setDeviceStateLocked(DEVICE_STATE_CLOSED);
advanceTime(1000);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
index 638924e..b182cce 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/BrightnessObserverTest.kt
@@ -95,9 +95,9 @@
) {
ALL_ENABLED(true, true, CombinedVote(
listOf(DisableRefreshRateSwitchingVote(true),
- SupportedModesVote(
- listOf(SupportedModesVote.SupportedMode(60f, 60f),
- SupportedModesVote.SupportedMode(120f, 120f)))))),
+ SupportedRefreshRatesVote(
+ listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f),
+ SupportedRefreshRatesVote.RefreshRates(120f, 120f)))))),
VRR_NOT_SUPPORTED(false, true, DisableRefreshRateSwitchingVote(true)),
VSYNC_VOTE_DISABLED(true, false, DisableRefreshRateSwitchingVote(true))
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java
new file mode 100644
index 0000000..e93e5bc
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/ProximitySensorObserverTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.DisplayManagerInternal;
+import android.util.SparseArray;
+import android.view.Display;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.sensors.SensorManagerInternal;
+
+import junitparams.JUnitParamsRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class ProximitySensorObserverTest {
+
+ private static final float FLOAT_TOLERANCE = 0.01f;
+ private static final int DISPLAY_ID = 1;
+ private static final SurfaceControl.RefreshRateRange REFRESH_RATE_RANGE =
+ new SurfaceControl.RefreshRateRange(60, 90);
+
+ private final VotesStorage mStorage = new VotesStorage(() -> { }, null);
+ private final FakesInjector mInjector = new FakesInjector();
+ private ProximitySensorObserver mSensorObserver;
+
+ @Mock
+ DisplayManagerInternal mMockDisplayManagerInternal;
+ @Mock
+ SensorManagerInternal mMockSensorManagerInternal;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockDisplayManagerInternal.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID),
+ any(), any())).thenReturn(REFRESH_RATE_RANGE);
+ mSensorObserver = new ProximitySensorObserver(mStorage, mInjector);
+ mSensorObserver.observe();
+ }
+
+ @Test
+ public void testAddsProximityVoteIfSensorManagerProximityActive() {
+ mSensorObserver.onProximityActive(true);
+
+ SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+ assertThat(displayVotes.size()).isEqualTo(1);
+ Vote vote = displayVotes.get(Vote.PRIORITY_PROXIMITY);
+ assertThat(vote).isNotNull();
+ assertThat(vote).isInstanceOf(CombinedVote.class);
+ CombinedVote combinedVote = (CombinedVote) vote;
+ RefreshRateVote.PhysicalVote physicalVote =
+ (RefreshRateVote.PhysicalVote) combinedVote.mVotes.get(0);
+ assertThat(physicalVote.mMinRefreshRate).isWithin(FLOAT_TOLERANCE).of(60);
+ assertThat(physicalVote.mMaxRefreshRate).isWithin(FLOAT_TOLERANCE).of(90);
+ }
+
+ @Test
+ public void testDoesNotAddProximityVoteIfSensorManagerProximityNotActive() {
+ mSensorObserver.onProximityActive(false);
+
+ SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+ assertThat(displayVotes.size()).isEqualTo(0);
+ }
+
+ @Test
+ public void testDoesNotAddProximityVoteIfDoze() {
+ mInjector.mDozeState = true;
+ mSensorObserver.onDisplayChanged(DISPLAY_ID);
+ mSensorObserver.onProximityActive(true);
+
+ SparseArray<Vote> displayVotes = mStorage.getVotes(DISPLAY_ID);
+ assertThat(displayVotes.size()).isEqualTo(0);
+ }
+
+ private class FakesInjector extends DisplayModeDirectorTest.FakesInjector {
+
+ private boolean mDozeState = false;
+
+ @Override
+ public Display[] getDisplays() {
+ return new Display[] { createDisplay(DISPLAY_ID) };
+ }
+
+ @Override
+ public DisplayManagerInternal getDisplayManagerInternal() {
+ return mMockDisplayManagerInternal;
+ }
+
+ @Override
+ public SensorManagerInternal getSensorManagerInternal() {
+ return mMockSensorManagerInternal;
+ }
+
+ @Override
+ public boolean isDozeState(Display d) {
+ return mDozeState;
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index ebb4f18..230317b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -85,9 +85,9 @@
internal val expectedVote: Vote?
) {
ALL_ENABLED(true, true, true,
- SupportedModesVote(listOf(
- SupportedModesVote.SupportedMode(60f, 240f),
- SupportedModesVote.SupportedMode(60f, 60f)
+ SupportedRefreshRatesVote(listOf(
+ SupportedRefreshRatesVote.RefreshRates(60f, 240f),
+ SupportedRefreshRatesVote.RefreshRates(60f, 60f)
))),
LOW_POWER_OFF(true, true, false, null),
DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
index 04e6265..6ce49b8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedModesVoteTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,12 +27,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SupportedModesVoteTest {
- private val supportedModes = listOf(
- SupportedModesVote.SupportedMode(60f, 90f ),
- SupportedModesVote.SupportedMode(120f, 240f )
- )
+ private val supportedModes = listOf(1, 2, 4)
- private val otherMode = SupportedModesVote.SupportedMode(120f, 120f )
+ private val otherMode = 5
private lateinit var supportedModesVote: SupportedModesVote
@@ -42,31 +39,31 @@
}
@Test
- fun `adds supported modes if supportedModes in summary is null`() {
+ fun `adds supported mode ids if supportedModeIds in summary is null`() {
val summary = createVotesSummary()
supportedModesVote.updateSummary(summary)
- assertThat(summary.supportedModes).containsExactlyElementsIn(supportedModes)
+ assertThat(summary.supportedModeIds).containsExactlyElementsIn(supportedModes)
}
@Test
- fun `does not add supported modes if summary has empty list of modes`() {
+ fun `does not add supported mode ids if summary has empty list of modeIds`() {
val summary = createVotesSummary()
- summary.supportedModes = ArrayList()
+ summary.supportedModeIds = ArrayList()
supportedModesVote.updateSummary(summary)
- assertThat(summary.supportedModes).isEmpty()
+ assertThat(summary.supportedModeIds).isEmpty()
}
@Test
fun `filters out modes that does not match vote`() {
val summary = createVotesSummary()
- summary.supportedModes = ArrayList(listOf(otherMode, supportedModes[0]))
+ summary.supportedModeIds = ArrayList(listOf(otherMode, supportedModes[0]))
supportedModesVote.updateSummary(summary)
- assertThat(summary.supportedModes).containsExactly(supportedModes[0])
+ assertThat(summary.supportedModeIds).containsExactly(supportedModes[0])
}
}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
new file mode 100644
index 0000000..d0c112b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SupportedRefreshRatesVoteTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SupportedRefreshRatesVoteTest {
+ private val refreshRates = listOf(
+ SupportedRefreshRatesVote.RefreshRates(60f, 90f),
+ SupportedRefreshRatesVote.RefreshRates(120f, 240f)
+ )
+
+ private val otherMode = SupportedRefreshRatesVote.RefreshRates(120f, 120f)
+
+ private lateinit var supportedRefreshRatesVote: SupportedRefreshRatesVote
+
+ @Before
+ fun setUp() {
+ supportedRefreshRatesVote = SupportedRefreshRatesVote(refreshRates)
+ }
+
+ @Test
+ fun `adds supported refresh rates if supportedModes in summary is null`() {
+ val summary = createVotesSummary()
+
+ supportedRefreshRatesVote.updateSummary(summary)
+
+ assertThat(summary.supportedRefreshRates).containsExactlyElementsIn(refreshRates)
+ }
+
+ @Test
+ fun `does not add supported refresh rates if summary has empty list of refresh rates`() {
+ val summary = createVotesSummary()
+ summary.supportedRefreshRates = ArrayList()
+
+ supportedRefreshRatesVote.updateSummary(summary)
+
+ assertThat(summary.supportedRefreshRates).isEmpty()
+ }
+
+ @Test
+ fun `filters out supported refresh rates that does not match vote`() {
+ val summary = createVotesSummary()
+ summary.supportedRefreshRates = ArrayList(listOf(otherMode, refreshRates[0]))
+
+ supportedRefreshRatesVote.updateSummary(summary)
+
+ assertThat(summary.supportedRefreshRates).containsExactly(refreshRates[0])
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
new file mode 100644
index 0000000..c49205b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SystemRequestObserverTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import android.os.IBinder
+import android.os.RemoteException
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.junit.testparameterinjector.TestParameterInjector
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+private const val DISPLAY_ID = 1
+private const val DISPLAY_ID_OTHER = 2
+
+@SmallTest
+@RunWith(TestParameterInjector::class)
+class SystemRequestObserverTest {
+
+
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ private val mockToken = mock<IBinder>()
+ private val mockOtherToken = mock<IBinder>()
+
+ private val storage = VotesStorage({}, null)
+
+ @Test
+ fun `requestDisplayModes adds vote to storage`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(1)
+ val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+ assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+ val supportedModesVote = vote as SupportedModesVote
+ assertThat(supportedModesVote.mModeIds.size).isEqualTo(requestedModes.size)
+ for (mode in requestedModes) {
+ assertThat(supportedModesVote.mModeIds).contains(mode)
+ }
+ }
+
+ @Test
+ fun `requestDisplayModes overrides votes in storage`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, intArrayOf(1, 2, 3))
+
+ val overrideModes = intArrayOf(10, 20, 30)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, overrideModes)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(1)
+ val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+ assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+ val supportedModesVote = vote as SupportedModesVote
+ assertThat(supportedModesVote.mModeIds.size).isEqualTo(overrideModes.size)
+ for (mode in overrideModes) {
+ assertThat(supportedModesVote.mModeIds).contains(mode)
+ }
+ }
+
+ @Test
+ fun `requestDisplayModes removes vote to storage`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun `requestDisplayModes calls linkToDeath to token`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+ verify(mockToken).linkToDeath(any(), eq(0))
+ }
+
+ @Test
+ fun `does not add votes to storage if binder died when requestDisplayModes called`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ doThrow(RemoteException()).whenever(mockOtherToken).linkToDeath(any(), eq(0))
+ systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedModes)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun `removes all votes from storage when binder dies`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+ val deathRecipientCaptor = argumentCaptor<IBinder.DeathRecipient>()
+ verify(mockToken).linkToDeath(deathRecipientCaptor.capture(), eq(0))
+
+ deathRecipientCaptor.lastValue.binderDied(mockToken)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun `calls unlinkToDeath on token when no votes remaining`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+ verify(mockToken).unlinkToDeath(any(), eq(0))
+ }
+
+ @Test
+ fun `does not call unlinkToDeath on token when votes for other display in storage`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID_OTHER, requestedModes)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, null)
+
+ verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+ }
+
+ @Test
+ fun `requestDisplayModes subset modes from different tokens`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+ val requestedOtherModes = intArrayOf(2, 3, 4)
+ systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedOtherModes)
+
+ verify(mockToken).linkToDeath(any(), eq(0))
+ verify(mockOtherToken).linkToDeath(any(), eq(0))
+ verify(mockToken, never()).unlinkToDeath(any(), eq(0))
+ verify(mockOtherToken, never()).unlinkToDeath(any(), eq(0))
+
+ val expectedModes = intArrayOf(2, 3)
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(1)
+ val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+ assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+ val supportedModesVote = vote as SupportedModesVote
+ assertThat(supportedModesVote.mModeIds.size).isEqualTo(expectedModes.size)
+ for (mode in expectedModes) {
+ assertThat(supportedModesVote.mModeIds).contains(mode)
+ }
+ }
+
+ @Test
+ fun `recalculates vote if one binder dies`() {
+ val systemRequestObserver = SystemRequestObserver(storage)
+ val requestedModes = intArrayOf(1, 2, 3)
+ systemRequestObserver.requestDisplayModes(mockToken, DISPLAY_ID, requestedModes)
+
+ val requestedOtherModes = intArrayOf(2, 3, 4)
+ systemRequestObserver.requestDisplayModes(mockOtherToken, DISPLAY_ID, requestedOtherModes)
+
+ val deathRecipientCaptor = argumentCaptor<IBinder.DeathRecipient>()
+ verify(mockOtherToken).linkToDeath(deathRecipientCaptor.capture(), eq(0))
+ deathRecipientCaptor.lastValue.binderDied(mockOtherToken)
+
+ val votes = storage.getVotes(DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(1)
+ val vote = votes.get(Vote.PRIORITY_SYSTEM_REQUESTED_MODES)
+ assertThat(vote).isInstanceOf(SupportedModesVote::class.java)
+ val supportedModesVote = vote as SupportedModesVote
+ assertThat(supportedModesVote.mModeIds.size).isEqualTo(requestedModes.size)
+ for (mode in requestedModes) {
+ assertThat(supportedModesVote.mModeIds).contains(mode)
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 910e03c..6b90bde 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -18,10 +18,10 @@
internal fun createVotesSummary(
isDisplayResolutionRangeVotingEnabled: Boolean = true,
- vsyncProximityVoteEnabled: Boolean = true,
+ supportedModesVoteEnabled: Boolean = true,
loggingEnabled: Boolean = true,
supportsFrameRateOverride: Boolean = true
): VoteSummary {
- return VoteSummary(isDisplayResolutionRangeVotingEnabled, vsyncProximityVoteEnabled,
+ return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled,
loggingEnabled, supportsFrameRateOverride)
}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index d6c8469..04b35f1 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -28,29 +28,29 @@
@RunWith(TestParameterInjector::class)
class VoteSummaryTest {
- enum class SupportedModesVoteTestCase(
- val vsyncProximityVoteEnabled: Boolean,
- internal val summarySupportedModes: List<SupportedModesVote.SupportedMode>?,
+ enum class SupportedRefreshRatesTestCase(
+ val supportedModesVoteEnabled: Boolean,
+ internal val summaryRefreshRates: List<SupportedRefreshRatesVote.RefreshRates>?,
val modesToFilter: Array<Display.Mode>,
val expectedModeIds: List<Int>
) {
HAS_NO_MATCHING_VOTE(true,
- listOf(SupportedModesVote.SupportedMode(60f, 60f)),
+ listOf(SupportedRefreshRatesVote.RefreshRates(60f, 60f)),
arrayOf(createMode(1, 90f, 90f),
createMode(2, 90f, 60f),
createMode(3, 60f, 90f)),
listOf()
),
HAS_SINGLE_MATCHING_VOTE(true,
- listOf(SupportedModesVote.SupportedMode(60f, 90f)),
+ listOf(SupportedRefreshRatesVote.RefreshRates(60f, 90f)),
arrayOf(createMode(1, 90f, 90f),
createMode(2, 90f, 60f),
createMode(3, 60f, 90f)),
listOf(3)
),
HAS_MULTIPLE_MATCHING_VOTES(true,
- listOf(SupportedModesVote.SupportedMode(60f, 90f),
- SupportedModesVote.SupportedMode(90f, 90f)),
+ listOf(SupportedRefreshRatesVote.RefreshRates(60f, 90f),
+ SupportedRefreshRatesVote.RefreshRates(90f, 90f)),
arrayOf(createMode(1, 90f, 90f),
createMode(2, 90f, 60f),
createMode(3, 60f, 90f)),
@@ -70,7 +70,69 @@
createMode(3, 60f, 90f)),
listOf(1, 2, 3)
),
- HAS_VSYNC_PROXIMITY_DISABLED(false,
+ HAS_SUPPORTED_MODES_VOTE_DISABLED(false,
+ listOf(),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf(1, 2, 3)
+ ),
+ }
+
+ @Test
+ fun `filters modes for summary supportedRefreshRates`(
+ @TestParameter testCase: SupportedRefreshRatesTestCase
+ ) {
+ val summary = createSummary(testCase.supportedModesVoteEnabled)
+ summary.supportedRefreshRates = testCase.summaryRefreshRates
+
+ val result = summary.filterModes(testCase.modesToFilter)
+
+ assertThat(result.map { it.modeId }).containsExactlyElementsIn(testCase.expectedModeIds)
+ }
+
+ enum class SupportedModesTestCase(
+ val supportedModesVoteEnabled: Boolean,
+ internal val summarySupportedModes: List<Int>?,
+ val modesToFilter: Array<Display.Mode>,
+ val expectedModeIds: List<Int>
+ ) {
+ HAS_NO_MATCHING_VOTE(true,
+ listOf(4, 5),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf()
+ ),
+ HAS_SINGLE_MATCHING_VOTE(true,
+ listOf(3),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf(3)
+ ),
+ HAS_MULTIPLE_MATCHING_VOTES(true,
+ listOf(1, 3),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf(1, 3)
+ ),
+ HAS_NO_SUPPORTED_MODES(true,
+ listOf(),
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf()
+ ),
+ HAS_NULL_SUPPORTED_MODES(true,
+ null,
+ arrayOf(createMode(1, 90f, 90f),
+ createMode(2, 90f, 60f),
+ createMode(3, 60f, 90f)),
+ listOf(1, 2, 3)
+ ),
+ HAS_SUPPORTED_MODES_VOTE_DISABLED(false,
listOf(),
arrayOf(createMode(1, 90f, 90f),
createMode(2, 90f, 60f),
@@ -81,10 +143,10 @@
@Test
fun `filters modes for summary supportedModes`(
- @TestParameter testCase: SupportedModesVoteTestCase
+ @TestParameter testCase: SupportedModesTestCase
) {
- val summary = createSummary(testCase.vsyncProximityVoteEnabled)
- summary.supportedModes = testCase.summarySupportedModes
+ val summary = createSummary(testCase.supportedModesVoteEnabled)
+ summary.supportedModeIds = testCase.summarySupportedModes
val result = summary.filterModes(testCase.modesToFilter)
@@ -96,8 +158,8 @@
FloatArray(0), IntArray(0))
}
-private fun createSummary(vsyncVoteEnabled: Boolean): VoteSummary {
- val summary = createVotesSummary(vsyncProximityVoteEnabled = vsyncVoteEnabled)
+private fun createSummary(supportedModesVoteEnabled: Boolean): VoteSummary {
+ val summary = createVotesSummary(supportedModesVoteEnabled = supportedModesVoteEnabled)
summary.width = 600
summary.height = 800
summary.maxPhysicalRefreshRate = Float.POSITIVE_INFINITY
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
index 1f6f1a4..a248d6de 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -153,4 +154,51 @@
assertThat(mVotesStorage.getVotes(DISPLAY_ID).size()).isEqualTo(0);
verify(mVotesListener, never()).onChanged();
}
+
+
+ @Test
+ public void removesAllVotesForPriority() {
+ // GIVEN vote storage with votes
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+ mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY_OTHER, VOTE_OTHER);
+ // WHEN removeAllVotesForPriority is called
+ mVotesStorage.removeAllVotesForPriority(PRIORITY);
+ // THEN votes with priority are removed from the storage
+ SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
+ assertThat(votes.size()).isEqualTo(1);
+ assertThat(votes.get(PRIORITY)).isNull();
+ votes = mVotesStorage.getVotes(DISPLAY_ID_OTHER);
+ assertThat(votes.size()).isEqualTo(1);
+ assertThat(votes.get(PRIORITY)).isNull();
+ }
+
+ @Test
+ public void removesAllVotesForPriority_notifiesListenerOnce() {
+ // GIVEN vote storage with votes
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY_OTHER, VOTE_OTHER);
+ mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY, VOTE);
+ mVotesStorage.updateVote(DISPLAY_ID_OTHER, PRIORITY_OTHER, VOTE_OTHER);
+ clearInvocations(mVotesListener);
+ // WHEN removeAllVotesForPriority is called
+ mVotesStorage.removeAllVotesForPriority(PRIORITY);
+ // THEN listener notified once
+ verify(mVotesListener).onChanged();
+ }
+
+ @Test
+ public void removesAllVotesForPriority_noChangesIfNothingRemoved() {
+ // GIVEN vote storage with votes
+ mVotesStorage.updateVote(DISPLAY_ID, PRIORITY, VOTE);
+ clearInvocations(mVotesListener);
+ // WHEN removeAllVotesForPriority is called for missing priority
+ mVotesStorage.removeAllVotesForPriority(PRIORITY_OTHER);
+ // THEN no changes to votes storage
+ SparseArray<Vote> votes = mVotesStorage.getVotes(DISPLAY_ID);
+ assertThat(votes.size()).isEqualTo(1);
+ assertThat(votes.get(PRIORITY)).isEqualTo(VOTE);
+ verify(mVotesListener, never()).onChanged();
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index fcb3caa..dc5b00a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -28,10 +28,15 @@
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.util.DebugUtils.valueToString;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.am.ActivityManagerInternalTest.CustomThread;
import static com.android.server.am.ActivityManagerService.Injector;
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
@@ -52,28 +57,40 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
+import android.app.ForegroundServiceDelegationOptions;
import android.app.IApplicationThread;
import android.app.IUidObserver;
+import android.app.Notification;
+import android.app.NotificationChannel;
import android.app.SyncNotedAppOp;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
+import android.content.pm.ServiceInfo;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -84,6 +101,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.permission.IPermissionManager;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
@@ -105,18 +123,20 @@
import com.android.server.am.ProcessList.IsolatedUidRangeAllocator;
import com.android.server.am.UidObserverController.ChangeRecord;
import com.android.server.appop.AppOpsService;
+import com.android.server.notification.NotificationManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerService;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.mockito.verification.VerificationMode;
import java.io.File;
import java.util.ArrayList;
@@ -127,13 +147,15 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.Function;
/**
* Test class for {@link ActivityManagerService}.
*
* Build/Install/Run:
- * atest FrameworksServicesTests:ActivityManagerServiceTest
+ * atest FrameworksMockingServicesTests:ActivityManagerServiceTest
*/
@Presubmit
@SmallTest
@@ -148,6 +170,9 @@
private static final String TEST_EXTRA_KEY1 = "com.android.server.am.TEST_EXTRA_KEY1";
private static final String TEST_EXTRA_VALUE1 = "com.android.server.am.TEST_EXTRA_VALUE1";
+
+ private static final String TEST_PACKAGE_NAME = "com.android.server.am.testpackage";
+
private static final String PROPERTY_APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS =
"apply_sdk_sandbox_audit_restrictions";
private static final String PROPERTY_APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS =
@@ -155,6 +180,7 @@
private static final String APPLY_SDK_SANDBOX_AUDIT_RESTRICTIONS = ":isSdkSandboxAudit";
private static final String APPLY_SDK_SANDBOX_NEXT_RESTRICTIONS = ":isSdkSandboxNext";
private static final int TEST_UID = 11111;
+ private static final int TEST_PID = 22222;
private static final int USER_ID = 666;
private static final long TEST_PROC_STATE_SEQ1 = 555;
@@ -169,22 +195,8 @@
UidRecord.CHANGE_CAPABILITY,
};
- private static PackageManagerInternal sPackageManagerInternal;
private static ProcessList.ProcessListSettingsListener sProcessListSettingsListener;
- @BeforeClass
- public static void setUpOnce() {
- sPackageManagerInternal = mock(PackageManagerInternal.class);
- doReturn(new ComponentName("", "")).when(sPackageManagerInternal)
- .getSystemUiServiceComponent();
- LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal);
- }
-
- @AfterClass
- public static void tearDownOnce() {
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- }
-
@Rule
public final ApplicationExitInfoTest.ServiceThreadRule
mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
@@ -196,15 +208,41 @@
@Mock private AppOpsService mAppOpsService;
@Mock private UserController mUserController;
+ @Mock private IPackageManager mPackageManager;
+ @Mock private IPermissionManager mPermissionManager;
+ @Mock private BatteryStatsService mBatteryStatsService;
+ @Mock private PackageManagerInternal mPackageManagerInternal;
+ @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ @Mock private NotificationManagerInternal mNotificationManagerInternal;
+
private TestInjector mInjector;
private ActivityManagerService mAms;
+ private ActiveServices mActiveServices;
private HandlerThread mHandlerThread;
private TestHandler mHandler;
+ private MockitoSession mMockingSession;
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
+ mMockingSession = mockitoSession()
+ .initMocks(this)
+ .mockStatic(AppGlobals.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
MockitoAnnotations.initMocks(this);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
+ LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal);
+
+ doReturn(new ComponentName("", "")).when(mPackageManagerInternal)
+ .getSystemUiServiceComponent();
+
+ doReturn(mPackageManager).when(AppGlobals::getPackageManager);
+ doReturn(mPermissionManager).when(AppGlobals::getPermissionManager);
+ doReturn(new String[]{""}).when(mPackageManager).getPackagesForUid(eq(Process.myUid()));
+
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new TestHandler(mHandlerThread.getLooper());
@@ -220,6 +258,7 @@
mAms.mConstants.mNetworkAccessTimeoutMs = 2000;
mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
mAms.mActivityTaskManager.initialize(null, null, mHandler.getLooper());
+ mAms.mAtmInternal = mActivityTaskManagerInternal;
mHandler.setRunnablesToIgnore(
List.of(mAms.mUidObserverController.getDispatchRunnableForTest()));
@@ -250,6 +289,15 @@
if (sProcessListSettingsListener != null) {
sProcessListSettingsListener.unregisterObserver();
}
+ clearInvocations(mNotificationManagerInternal);
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+
+ if (mMockingSession != null) {
+ mMockingSession.finishMocking();
+ }
}
@SuppressWarnings("GuardedBy")
@@ -445,6 +493,7 @@
}
}
+ @SuppressWarnings("GuardedBy")
private UidRecord addUidRecord(int uid) {
final UidRecord uidRec = new UidRecord(uid, mAms);
uidRec.procStateSeqWaitingForNetwork = 1;
@@ -453,10 +502,13 @@
ApplicationInfo info = new ApplicationInfo();
info.packageName = "";
+ info.uid = uid;
final ProcessRecord appRec = new ProcessRecord(mAms, info, TAG, uid);
- final ProcessStatsService tracker = new ProcessStatsService(mAms, mContext.getCacheDir());
- appRec.makeActive(mock(IApplicationThread.class), tracker);
+ final ProcessStatsService tracker = mAms.mProcessStats;
+ final IApplicationThread appThread = mock(IApplicationThread.class);
+ doReturn(mock(IBinder.class)).when(appThread).asBinder();
+ appRec.makeActive(appThread, tracker);
mAms.mProcessList.getLruProcessesLSP().add(appRec);
return uidRec;
@@ -1209,6 +1261,108 @@
mAms.mUidObserverController.getPendingUidChangesForTest().clear();
}
+ @Test
+ public void testStartForegroundServiceDelegateWithNotification() throws Exception {
+ testStartForegroundServiceDelegate(true);
+ }
+
+ @Test
+ public void testStartForegroundServiceDelegateWithoutNotification() throws Exception {
+ testStartForegroundServiceDelegate(false);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ private void testStartForegroundServiceDelegate(boolean withNotification) throws Exception {
+ mockNoteOperation();
+
+ final int notificationId = 42;
+ final Notification notification = mock(Notification.class);
+
+ addUidRecord(TEST_UID);
+ final ProcessRecord app = mAms.mProcessList.getLruProcessesLSP().get(0);
+ app.mPid = TEST_PID;
+ app.info.packageName = TEST_PACKAGE_NAME;
+ app.info.processName = TEST_PACKAGE_NAME;
+
+ doReturn(app.info).when(mPackageManager).getApplicationInfo(
+ eq(app.info.packageName), anyLong(), anyInt());
+
+ doReturn(true).when(mActiveServices)
+ .canStartForegroundServiceLocked(anyInt(), anyInt(), anyString());
+ doReturn(REASON_DENIED).when(mActiveServices)
+ .shouldAllowFgsWhileInUsePermissionLocked(anyString(), anyInt(), anyInt(),
+ any(ProcessRecord.class), any(BackgroundStartPrivileges.class));
+
+ doReturn(true).when(mNotificationManagerInternal).areNotificationsEnabledForPackage(
+ anyString(), anyInt());
+ doReturn(mock(Icon.class)).when(notification).getSmallIcon();
+ doReturn("").when(notification).getChannelId();
+ doReturn(mock(NotificationChannel.class)).when(mNotificationManagerInternal)
+ .getNotificationChannel(anyString(), anyInt(), anyString());
+
+ mAms.mAppProfiler.mCachedAppsWatermarkData.mCachedAppHighWatermark = Integer.MAX_VALUE;
+
+ final ForegroundServiceDelegationOptions.Builder optionsBuilder =
+ new ForegroundServiceDelegationOptions.Builder()
+ .setClientPid(app.mPid)
+ .setClientUid(app.uid)
+ .setClientPackageName(app.info.packageName)
+ .setClientAppThread(app.getThread())
+ .setSticky(false)
+ .setClientInstanceName(
+ "SystemExemptedFgsDelegate_"
+ + Process.myUid()
+ + "_"
+ + app.uid
+ + "_"
+ + app.info.packageName)
+ .setForegroundServiceTypes(ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED)
+ .setDelegationService(
+ ForegroundServiceDelegationOptions.DELEGATION_SERVICE_SYSTEM_EXEMPTED);
+ if (withNotification) {
+ optionsBuilder.setClientNotification(notificationId, notification);
+ }
+ final ForegroundServiceDelegationOptions options = optionsBuilder.build();
+
+ final CountDownLatch[] latchHolder = new CountDownLatch[1];
+ final ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ latchHolder[0].countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ latchHolder[0].countDown();
+ }
+ };
+
+ latchHolder[0] = new CountDownLatch(1);
+ mAms.mInternal.startForegroundServiceDelegate(options, conn);
+
+ assertThat(latchHolder[0].await(5, TimeUnit.SECONDS)).isTrue();
+ assertEquals(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ app.mState.getCurProcState());
+ final long timeoutMs = 5000L;
+ final VerificationMode mode = withNotification
+ ? timeout(timeoutMs) : after(timeoutMs).atMost(0);
+ verify(mNotificationManagerInternal, mode)
+ .enqueueNotification(eq(app.info.packageName), eq(app.info.packageName),
+ eq(app.info.uid), eq(app.mPid), eq(null),
+ eq(notificationId), eq(notification), anyInt(), eq(true));
+
+ latchHolder[0] = new CountDownLatch(1);
+ mAms.mInternal.stopForegroundServiceDelegate(options);
+
+ assertThat(latchHolder[0].await(5, TimeUnit.SECONDS)).isTrue();
+ assertEquals(ActivityManager.PROCESS_STATE_CACHED_EMPTY,
+ app.mState.getCurProcState());
+ verify(mNotificationManagerInternal, mode)
+ .cancelNotification(eq(app.info.packageName), eq(app.info.packageName),
+ eq(app.info.uid), eq(app.mPid), eq(null),
+ eq(notificationId), anyInt());
+ }
+
private static class TestHandler extends Handler {
private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec
private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec
@@ -1291,6 +1445,19 @@
usersStartedOnSecondaryDisplays.add(new Pair<>(userId, displayId));
return returnValueForstartUserOnSecondaryDisplay;
}
+
+ @Override
+ public ActiveServices getActiveServices(ActivityManagerService service) {
+ if (mActiveServices == null) {
+ mActiveServices = spy(new ActiveServices(service));
+ }
+ return mActiveServices;
+ }
+
+ @Override
+ public BatteryStatsService getBatteryStatsService() {
+ return mBatteryStatsService;
+ }
}
// TODO: [b/302724778] Remove manual JNI load
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index aec896f..f86ff14 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -64,6 +64,7 @@
import android.content.PermissionChecker;
import android.content.res.Resources;
import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -154,6 +155,8 @@
private static final float BRIGHTNESS_FACTOR = 0.7f;
private static final boolean BATTERY_SAVER_ENABLED = true;
+ private static final DeviceState DEVICE_STATE_1 = new DeviceState(
+ new DeviceState.Configuration.Builder(1 /* identifier */, "" /* name */).build());
@Mock private BatterySaverController mBatterySaverControllerMock;
@Mock private BatterySaverPolicy mBatterySaverPolicyMock;
@@ -2839,7 +2842,7 @@
// Send a display state change event and advance the clock 10.
final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
- deviceStateCallback.onStateChanged(1);
+ deviceStateCallback.onDeviceStateChanged(DEVICE_STATE_1);
final long timeToAdvance = 10;
advanceTime(timeToAdvance);
@@ -2849,7 +2852,7 @@
assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
// Send the same state and ensure that does not trigger an update.
- deviceStateCallback.onStateChanged(1);
+ deviceStateCallback.onDeviceStateChanged(DEVICE_STATE_1);
advanceTime(timeToAdvance);
final long newTime = timeToAdvance * 2;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index eb89503..b2ecea1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -813,7 +813,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_DISABLE_CONTINUOUS_SHORTCUT_ON_FORCE_STOP)
public void testPackagesForceStopped_fromContinuousService_removesButtonTarget() {
final AccessibilityServiceInfo info_a = new AccessibilityServiceInfo();
info_a.setComponentName(COMPONENT_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index f86cb7b..cda8b01 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -208,7 +208,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
public void onServiceConnected_addsWindowTokens() {
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
index b1964e2..b269beb9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -51,7 +51,6 @@
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.graphics.Color;
-import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
@@ -220,18 +219,6 @@
}
@Test
- // addServiceLocked only calls addWindowTokensForAllDisplays when
- // FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK is off, so skip the test if it is on.
- @RequiresFlagsDisabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
- public void addService_flagDisabled_addsWindowTokens() {
- when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
-
- mUserState.addServiceLocked(mMockConnection);
-
- verify(mMockConnection).addWindowTokensForAllDisplays();
- }
-
- @Test
public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
// When soft kb show mode is hidden in settings and is auto in state.
putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index 3ee5f61..95a1f5a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -39,7 +39,6 @@
import android.content.pm.ServiceInfo;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.WindowManager;
@@ -209,7 +208,6 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ADD_WINDOW_TOKEN_WITHOUT_LOCK)
public void registerUiAutomationService_callsAddWindowTokenUsingHandler() {
register(0);
// registerUiTestAutomationServiceLocked() should not directly call addWindowToken.
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 67b131f..1249707 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -206,7 +206,6 @@
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.MethodRule;
@@ -2151,14 +2150,12 @@
assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainEnabled() throws Exception {
verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true);
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnProcStateChange() throws Exception {
@@ -2188,7 +2185,6 @@
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnAllowlistChange() throws Exception {
@@ -2227,7 +2223,6 @@
assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnTempAllowlistChange() throws Exception {
@@ -2266,7 +2261,6 @@
&& uidState.procState == procState && uidState.capability == capability;
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersProcStateChanges() throws Exception {
@@ -2329,7 +2323,6 @@
waitForUidEventHandlerIdle();
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersStaleChanges() throws Exception {
@@ -2350,7 +2343,6 @@
waitForUidEventHandlerIdle();
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersCapabilityChanges() throws Exception {
@@ -2430,7 +2422,6 @@
assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
}
- @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testObsoleteHandleUidChanged() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index e249cd7..55c48e0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -90,8 +90,6 @@
public void testCanDeviceOwnerChange() {
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_RECORD_AUDIO));
assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_WALLPAPER));
- assertFalse(UserRestrictionsUtils.canDeviceOwnerChange(
- UserManager.DISALLOW_ADD_PRIVATE_PROFILE));
assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_ADD_USER));
assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
}
@@ -110,10 +108,6 @@
UserManager.DISALLOW_USER_SWITCH,
true,
false));
- assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
- UserManager.DISALLOW_ADD_PRIVATE_PROFILE,
- true,
- false));
assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
UserManager.DISALLOW_ADD_USER,
true,
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index fee6582..dff4984 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -43,9 +43,12 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER;
+import static org.mockito.ArgumentMatchers.eq;
+
import static java.util.Collections.unmodifiableMap;
import android.content.Context;
+import android.content.res.Resources;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -57,6 +60,7 @@
import com.android.internal.util.test.FakeSettingsProviderRule;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.RuleChain;
@@ -70,9 +74,10 @@
@Rule
public RuleChain rules = RuleChain.outerRule(mSettingsProviderRule).around(mSetFlagsRule);
+ private Resources mResources;
TestPhoneWindowManager mPhoneWindowManager;
DispatchedKeyHandler mDispatchedKeyHandler = event -> false;
- final Context mContext = spy(getInstrumentation().getTargetContext());
+ Context mContext;
/** Modifier key to meta state */
protected static final Map<Integer, Integer> MODIFIER;
@@ -90,6 +95,16 @@
MODIFIER = unmodifiableMap(map);
}
+ @Before
+ public void setup() {
+ mContext = spy(getInstrumentation().getTargetContext());
+ mResources = spy(mContext.getResources());
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mSettingsProviderRule.mockContentResolver(mContext))
+ .when(mContext).getContentResolver();
+ }
+
+
/** Same as {@link setUpPhoneWindowManager(boolean)}, without supporting settings update. */
protected final void setUpPhoneWindowManager() {
setUpPhoneWindowManager(/* supportSettingsUpdate= */ false);
@@ -101,12 +116,14 @@
* <p>Subclasses must call this at the start of the test if they intend to interact with phone
* window manager.
*
- * @param supportSettingsUpdate {@code true} if this test should read and listen to provider
- * settings values.
+ * @param supportSettingsUpdate {@code true} to have PWM respond to any Settings changes upon
+ * instantiation. Although this is supposed to also allow a test to listen to any Settings
+ * changes after instantiation, MockContentResolver in this class's setup stubs out
+ * notifyChange(), which prevents SettingsObserver from getting notified of events. So
+ * we're effectively always instantiating TestPhoneWindowManager with
+ * supportSettingsUpdate=false.
*/
protected final void setUpPhoneWindowManager(boolean supportSettingsUpdate) {
- doReturn(mSettingsProviderRule.mockContentResolver(mContext))
- .when(mContext).getContentResolver();
mPhoneWindowManager = new TestPhoneWindowManager(mContext, supportSettingsUpdate);
}
@@ -187,6 +204,23 @@
sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
}
+ /**
+ * Since we use SettingsProviderRule to mock the ContentResolver in these
+ * tests, the settings observer registered by PhoneWindowManager will not
+ * be triggered automatically by the mock. Use this method to force the
+ * settings observer change after modifying any settings.
+ */
+ void triggerSettingsObserverChange() {
+ mPhoneWindowManager.getSettingsObserver().onChange(
+ // This boolean doesn't matter. This observer does the same thing regardless.
+ /*selfChange=*/true);
+ }
+
+ /** Override a resource's return value. */
+ void overrideResource(int resId, int expectedBehavior) {
+ doReturn(expectedBehavior).when(mResources).getInteger(eq(resId));
+ }
+
private void interceptKey(KeyEvent keyEvent) {
int actions = mPhoneWindowManager.interceptKeyBeforeQueueing(keyEvent);
if ((actions & ACTION_PASS_TO_USER) != 0) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
index 77e7a0a..2e85025 100644
--- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java
@@ -291,6 +291,101 @@
mPhoneWindowManager.assertSwitchToTask(referenceId);
}
+ /**
+ * Ensure the stem rule is added even when button behaviors are set to nothing.
+ *
+ * This makes sure that if stem key behaviors are overridden to NOTHING, then we check the
+ * XML config as the source of truth upon reboot to see whether a device should have a stem
+ * key rule. This test walks us through a scenario where a device powers off during Wear's
+ * Touch Lock mode.
+ */
+ @Test
+ public void stemKeyRuleIsAddedEvenWhenBehaviorsRemoved() {
+ // deactivate stem button presses
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
+ PhoneWindowManager.DOUBLE_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
+ PhoneWindowManager.TRIPLE_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_LONG_PRESS,
+ PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+ // pretend like we have stem keys enabled in the xmls
+ overrideResource(
+ com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
+ SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+
+ // start the PhoneWindowManager, just like would happen with a reboot
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ // Set the stem behavior back to something normal after boot
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ // manually trigger the SettingsObserver's onChange() method because subclasses of
+ // ShortcutKeyTestBase cannot automatically pick up Settings changes.
+ triggerSettingsObserverChange();
+
+ // These calls are required to make the All Apps view show up
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ // Because the rule was loaded and we changed the behavior back to non-zero, PWM should
+ // actually perform this action. It would not perform the action if the rule was missing.
+ mPhoneWindowManager.assertOpenAllAppView();
+ }
+
+ /**
+ * Ensure the stem rule is not added when stem behavior is not defined in the xml.
+ *
+ * This is the opposite of the test above.
+ */
+ @Test
+ public void stemKeyRuleIsNotAddedWhenXmlDoesntDefineIt() {
+ // deactivate stem button presses
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS,
+ PhoneWindowManager.DOUBLE_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_TRIPLE_PRESS,
+ PhoneWindowManager.TRIPLE_PRESS_PRIMARY_NOTHING);
+ overrideBehavior(STEM_PRIMARY_BUTTON_LONG_PRESS,
+ PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+ // pretend like we do not have stem keys enabled in the xmls
+ overrideResource(
+ com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior,
+ PhoneWindowManager.SHORT_PRESS_PRIMARY_NOTHING);
+ overrideResource(
+ com.android.internal.R.integer.config_longPressOnStemPrimaryBehavior,
+ PhoneWindowManager.LONG_PRESS_PRIMARY_NOTHING);
+
+ // start the PhoneWindowManager, just like would happen with a reboot
+ setUpPhoneWindowManager(/* supportSettingsUpdate= */ true);
+ // Set the stem behavior back to something normal after boot
+ // (Despite this fact, a stem press shouldn't have any behavior because there's no rule.)
+ overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS);
+ // manually trigger the SettingsObserver's onChange() method because subclasses of
+ // ShortcutKeyTestBase cannot automatically pick up Settings changes.
+ triggerSettingsObserverChange();
+
+ // These calls are required to make the All Apps view show up
+ mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false);
+ mPhoneWindowManager.overrideStartActivity();
+ mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false);
+ mPhoneWindowManager.overrideIsUserSetupComplete(true);
+
+ sendKey(KEYCODE_STEM_PRIMARY);
+
+ // Because the rule was not loaded, PWM should not actually perform this action, even
+ // though the Settings override is set to non-null.
+ mPhoneWindowManager.assertNotOpenAllAppView();
+ }
+
private void overrideBehavior(String key, int expectedBehavior) {
Settings.Global.putLong(mContext.getContentResolver(), key, expectedBehavior);
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 0776c51..52df010 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -66,6 +66,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
@@ -246,6 +247,13 @@
}
}
+ /**
+ * {@link TestPhoneWindowManager}'s constructor.
+ *
+ * @param context The {@Context} to be used in any Context-related actions.
+ * @param supportSettingsUpdate {@code true} if this object should read and listen to provider
+ * settings values.
+ */
TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {
MockitoAnnotations.initMocks(this);
mHandler = new Handler(mTestLooper.getLooper());
@@ -416,6 +424,13 @@
mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
}
+ /**
+ * Provide access to the SettingsObserver so that tests can manually trigger Settings changes.
+ */
+ ContentObserver getSettingsObserver() {
+ return mPhoneWindowManager.mSettingsObserver;
+ }
+
long getCurrentTime() {
return mClock.now();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index 0290f0f..4e4bbfe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -21,7 +21,6 @@
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION;
import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION;
-import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS;
@@ -64,7 +63,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.LetterboxUiController.MIN_COUNT_TO_IGNORE_REQUEST_IN_LOOP;
import static com.android.server.wm.LetterboxUiController.SET_ORIENTATION_REQUEST_COUNTER_TIMEOUT_MS;
-import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -83,7 +81,6 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.RoundedCorner;
@@ -104,11 +101,11 @@
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
-/**
+ /**
* Test class for {@link LetterboxUiControllerTest}.
*
* Build/Install/Run:
- * atest WmTests:LetterboxUiControllerTest
+ * atest WmTests:LetterboxUiControllerTest
*/
@SmallTest
@Presubmit
@@ -125,8 +122,6 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ActivityRecord mActivity;
private Task mTask;
@@ -471,48 +466,10 @@
assertTrue(mController.shouldForceRotateForCameraCompat());
}
- // shouldApplyFreeformTreatmentForCameraCompat
-
- @Test
- public void testShouldApplyCameraCompatFreeformTreatment_flagIsDisabled_returnsFalse() {
- mSetFlagsRule.disableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
- assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
- public void testShouldApplyCameraCompatFreeformTreatment_overrideEnabled_returnsFalse() {
- mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
- assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
- }
-
- @Test
- @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT})
- public void testShouldApplyCameraCompatFreeformTreatment_disabledByOverride_returnsFalse()
- throws Exception {
- mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertFalse(mController.shouldApplyFreeformTreatmentForCameraCompat());
- }
-
- @Test
- public void testShouldApplyCameraCompatFreeformTreatment_notDisabledByOverride_returnsTrue()
- throws Exception {
- mSetFlagsRule.enableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM);
-
- mController = new LetterboxUiController(mWm, mActivity);
-
- assertTrue(mController.shouldApplyFreeformTreatmentForCameraCompat());
- }
-
@Test
public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() {
final InsetsSource taskbar = new InsetsSource(/*id=*/ 0,
- WindowInsets.Type.navigationBars());
+ WindowInsets.Type.navigationBars());
taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER);
final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar);
final Rect opaqueBounds = new Rect(0, 0, 500, 300);
@@ -769,7 +726,7 @@
throws Exception {
mDisplayContent.setIgnoreOrientationRequest(false);
assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
}
@Test
@@ -779,7 +736,7 @@
prepareActivityThatShouldApplyUserMinAspectRatioOverride();
assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
- /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
}
@Test
@@ -902,7 +859,6 @@
assertEquals(SCREEN_ORIENTATION_USER, mController.overrideOrientationIfNeeded(
/* candidate */ SCREEN_ORIENTATION_UNSPECIFIED));
}
-
@Test
public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
spyOn(mController);
@@ -1424,7 +1380,7 @@
private void mockThatProperty(String propertyName, boolean value) throws Exception {
Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
- /* className */ "");
+ /* className */ "");
PackageManager pm = mWm.mContext.getPackageManager();
spyOn(pm);
doReturn(property).when(pm).getProperty(eq(propertyName), anyString());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 03b695d..43b424f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -1376,8 +1376,9 @@
assertTrue(w1.syncNextBuffer());
assertTrue(w2.syncNextBuffer());
- // A drawn window can complete the sync state automatically.
+ // A drawn window in non-explicit sync can complete the sync state automatically.
w1.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ w1.mPrepareSyncSeqId = 0;
makeLastConfigReportedToClient(w1, true /* visible */);
mWm.mSyncEngine.onSurfacePlacement();
verify(mockCallback).onTransactionReady(anyInt(), any());
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 2ff21ad..40537c8 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -978,8 +978,14 @@
shouldRestrictOverlayActivities = filteredAppProcessInfos.stream().anyMatch(pkg -> {
try {
- return mPackageManager.getProperty(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES, pkg)
- .getBoolean();
+ boolean restrictUsbOverlayActivitiesForPackage = mPackageManager
+ .getProperty(PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES, pkg).getBoolean();
+
+ if (restrictUsbOverlayActivitiesForPackage) {
+ Slog.d(TAG, "restricting usb overlay activities as package " + pkg
+ + " is in foreground");
+ }
+ return restrictUsbOverlayActivitiesForPackage;
} catch (NameNotFoundException e) {
if (DEBUG) {
Slog.d(TAG, "property PROPERTY_RESTRICT_USB_OVERLAY_ACTIVITIES "
@@ -989,8 +995,8 @@
}
});
- if (shouldRestrictOverlayActivities) {
- Slog.d(TAG, "restricting starting of usb overlay activities");
+ if (!shouldRestrictOverlayActivities) {
+ Slog.d(TAG, "starting of usb overlay activities");
}
return shouldRestrictOverlayActivities;
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 5ba5ee883..8b9a93b 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,6 +16,8 @@
package android.telecom;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +30,11 @@
import android.telephony.ims.ImsReasonInfo;
import android.text.TextUtils;
+import androidx.annotation.IntDef;
+
import com.android.server.telecom.flags.Flags;
+import java.lang.annotation.Retention;
import java.util.Objects;
/**
@@ -42,48 +47,69 @@
public final class DisconnectCause implements Parcelable {
/** Disconnected because of an unknown or unspecified reason. */
- public static final int UNKNOWN = TelecomProtoEnums.UNKNOWN; // = 0
+ public static final int UNKNOWN = 0;
/** Disconnected because there was an error, such as a problem with the network. */
- public static final int ERROR = TelecomProtoEnums.ERROR; // = 1
+ public static final int ERROR = 1;
/** Disconnected because of a local user-initiated action, such as hanging up. */
- public static final int LOCAL = TelecomProtoEnums.LOCAL; // = 2
+ public static final int LOCAL = 2;
/**
* Disconnected because the remote party hung up an ongoing call, or because an outgoing call
* was not answered by the remote party.
*/
- public static final int REMOTE = TelecomProtoEnums.REMOTE; // = 3
+ public static final int REMOTE = 3;
/** Disconnected because it has been canceled. */
- public static final int CANCELED = TelecomProtoEnums.CANCELED; // = 4
+ public static final int CANCELED = 4;
/** Disconnected because there was no response to an incoming call. */
- public static final int MISSED = TelecomProtoEnums.MISSED; // = 5
+ public static final int MISSED = 5;
/** Disconnected because the user rejected an incoming call. */
- public static final int REJECTED = TelecomProtoEnums.REJECTED; // = 6
+ public static final int REJECTED = 6;
/** Disconnected because the other party was busy. */
- public static final int BUSY = TelecomProtoEnums.BUSY; // = 7
+ public static final int BUSY = 7;
/**
* Disconnected because of a restriction on placing the call, such as dialing in airplane
* mode.
*/
- public static final int RESTRICTED = TelecomProtoEnums.RESTRICTED; // = 8
+ public static final int RESTRICTED = 8;
/** Disconnected for reason not described by other disconnect codes. */
- public static final int OTHER = TelecomProtoEnums.OTHER; // = 9
+ public static final int OTHER = 9;
/**
* Disconnected because the connection manager did not support the call. The call will be tried
* again without a connection manager. See {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}.
*/
- public static final int CONNECTION_MANAGER_NOT_SUPPORTED =
- TelecomProtoEnums.CONNECTION_MANAGER_NOT_SUPPORTED; // = 10
+ public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10;
/**
* Disconnected because the user did not locally answer the incoming call, but it was answered
* on another device where the call was ringing.
*/
- public static final int ANSWERED_ELSEWHERE = TelecomProtoEnums.ANSWERED_ELSEWHERE; // = 11
+ public static final int ANSWERED_ELSEWHERE = 11;
/**
* Disconnected because the call was pulled from the current device to another device.
*/
- public static final int CALL_PULLED = TelecomProtoEnums.CALL_PULLED; // = 12
+ public static final int CALL_PULLED = 12;
+
+ /**
+ * @hide
+ */
+ @Retention(SOURCE)
+ @FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
+ @IntDef({
+ UNKNOWN,
+ ERROR,
+ LOCAL,
+ REMOTE,
+ CANCELED,
+ MISSED,
+ REJECTED,
+ BUSY,
+ RESTRICTED,
+ OTHER,
+ CONNECTION_MANAGER_NOT_SUPPORTED,
+ ANSWERED_ELSEWHERE,
+ CALL_PULLED
+ })
+ public @interface DisconnectCauseCode {}
/**
* Reason code (returned via {@link #getReason()}) which indicates that a call could not be
@@ -116,7 +142,7 @@
*/
public static final String REASON_EMERGENCY_CALL_PLACED = "REASON_EMERGENCY_CALL_PLACED";
- private int mDisconnectCode;
+ private @DisconnectCauseCode int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
private String mDisconnectReason;
@@ -130,7 +156,7 @@
*
* @param code The code for the disconnect cause.
*/
- public DisconnectCause(int code) {
+ public DisconnectCause(@DisconnectCauseCode int code) {
this(code, null, null, null, ToneGenerator.TONE_UNKNOWN);
}
@@ -140,7 +166,7 @@
* @param code The code for the disconnect cause.
* @param reason The reason for the disconnect.
*/
- public DisconnectCause(int code, String reason) {
+ public DisconnectCause(@DisconnectCauseCode int code, String reason) {
this(code, null, null, reason, ToneGenerator.TONE_UNKNOWN);
}
@@ -152,7 +178,8 @@
* @param description The localized description to show to the user to explain the disconnect.
* @param reason The reason for the disconnect.
*/
- public DisconnectCause(int code, CharSequence label, CharSequence description, String reason) {
+ public DisconnectCause(@DisconnectCauseCode int code, CharSequence label,
+ CharSequence description, String reason) {
this(code, label, description, reason, ToneGenerator.TONE_UNKNOWN);
}
@@ -165,8 +192,8 @@
* @param reason The reason for the disconnect.
* @param toneToPlay The tone to play on disconnect, as defined in {@link ToneGenerator}.
*/
- public DisconnectCause(int code, CharSequence label, CharSequence description, String reason,
- int toneToPlay) {
+ public DisconnectCause(@DisconnectCauseCode int code, CharSequence label,
+ CharSequence description, String reason, int toneToPlay) {
this(code, label, description, reason, toneToPlay,
android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
PreciseDisconnectCause.ERROR_UNSPECIFIED, null /* imsReasonInfo */);
@@ -186,8 +213,8 @@
* @param imsReasonInfo The relevant {@link ImsReasonInfo}, or {@code null} if not available.
* @hide
*/
- public DisconnectCause(int code, @NonNull CharSequence label,
- @NonNull CharSequence description, @NonNull String reason,
+ public DisconnectCause(@DisconnectCauseCode int code, @Nullable CharSequence label,
+ @Nullable CharSequence description, @Nullable String reason,
int toneToPlay, @Annotation.DisconnectCauses int telephonyDisconnectCause,
@Annotation.PreciseDisconnectCauses int telephonyPreciseDisconnectCause,
@Nullable ImsReasonInfo imsReasonInfo) {
@@ -206,7 +233,7 @@
*
* @return The disconnect code.
*/
- public int getCode() {
+ public @DisconnectCauseCode int getCode() {
return mDisconnectCode;
}
@@ -301,28 +328,24 @@
@SystemApi
@FlaggedApi(Flags.FLAG_TELECOM_RESOLVE_HIDDEN_DEPENDENCIES)
public static final class Builder {
- private int mDisconnectCode;
+ private @DisconnectCauseCode int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
private String mDisconnectReason;
- private int mToneToPlay;
+ private int mToneToPlay = ToneGenerator.TONE_UNKNOWN;
private int mTelephonyDisconnectCause;
private int mTelephonyPreciseDisconnectCause;
private ImsReasonInfo mImsReasonInfo;
- /**
- * Sets the code for the reason for this disconnect.
- * @param code The code denoting the type of disconnect.
- */
- public @NonNull DisconnectCause.Builder setCode(int code) {
+ public Builder(@DisconnectCauseCode int code) {
mDisconnectCode = code;
- return this;
}
/**
* Sets a label which explains the reason for the disconnect cause, used for display in the
* user interface.
* @param label The label to associate with the disconnect cause.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setLabel(@Nullable CharSequence label) {
mDisconnectLabel = label;
@@ -333,6 +356,7 @@
* Sets a description which provides the reason for the disconnect cause, used for display
* in the user interface.
* @param description The description to associate with the disconnect cause.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setDescription(
@Nullable CharSequence description) {
@@ -344,6 +368,7 @@
* Sets a reason providing explanation for the disconnect (intended for logging and not for
* displaying in the user interface).
* @param reason The reason for the disconnect.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setReason(@NonNull String reason) {
mDisconnectReason = reason;
@@ -353,6 +378,7 @@
/**
* Sets the tone to play when disconnected.
* @param toneToPlay The tone as defined in {@link ToneGenerator} to play when disconnected.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setTone(int toneToPlay) {
mToneToPlay = toneToPlay;
@@ -363,6 +389,7 @@
* Sets the telephony {@link android.telephony.DisconnectCause} for the call (used
* internally by Telecom for providing extra debug information from Telephony).
* @param telephonyDisconnectCause The disconnect cause as provided by Telephony.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setTelephonyDisconnectCause(
@Annotation.DisconnectCauses int telephonyDisconnectCause) {
@@ -375,6 +402,7 @@
* internally by Telecom for providing extra debug information from Telephony).
* @param telephonyPreciseDisconnectCause The precise disconnect cause as provided by
* Telephony.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setTelephonyPreciseDisconnectCause(
@@ -384,10 +412,11 @@
}
/**
- * Returns the telephony {@link ImsReasonInfo} associated with the call disconnection. This
+ * Sets the telephony {@link ImsReasonInfo} associated with the call disconnection. This
* is only used internally by Telecom for providing extra debug information from Telephony.
*
* @param imsReasonInfo The {@link ImsReasonInfo} or {@code null} if not known.
+ * @return The {@link DisconnectCause} builder instance.
*/
public @NonNull DisconnectCause.Builder setImsReasonInfo(
@Nullable ImsReasonInfo imsReasonInfo) {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index e3ce766..ebabbf9 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -512,6 +512,7 @@
EUICC_ACTIVATION_TYPE_BACKUP,
EUICC_ACTIVATION_TYPE_TRANSFER,
EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED,
+ EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD,
})
public @interface EuiccActivationType{}
@@ -555,6 +556,15 @@
public static final int EUICC_ACTIVATION_TYPE_ACCOUNT_REQUIRED = 4;
/**
+ * The activation flow of eSIM transfer to block the transfer process before B&R flow.
+ * This is needed to avoid connection overlapping between eSIM connection B&R connection.
+ *
+ * @hide
+ */
+ // TODO(b/329212614): add system api annotation during the allowed api timeline.
+ public static final int EUICC_ACTIVATION_TYPE_TRANSFER_FINAL_HOLD = 5;
+
+ /**
* Euicc OTA update status which can be got by {@link #getOtaStatus}
* @removed mistakenly exposed as system-api previously
*/
diff --git a/tools/aapt2/optimize/VersionCollapser.cpp b/tools/aapt2/optimize/VersionCollapser.cpp
index cd791bd..27fff9a 100644
--- a/tools/aapt2/optimize/VersionCollapser.cpp
+++ b/tools/aapt2/optimize/VersionCollapser.cpp
@@ -70,7 +70,7 @@
* exception is when there is no exact matching resource for the minSdk. The next smallest one will
* be kept.
*/
-static void CollapseVersions(int min_sdk, ResourceEntry* entry) {
+static void CollapseVersions(IAaptContext* context, int min_sdk, ResourceEntry* entry) {
// First look for all sdks less than minSdk.
for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
++iter) {
@@ -102,7 +102,14 @@
auto filter_iter =
make_filter_iterator(iter + 1, entry->values.rend(), pred);
while (filter_iter.HasNext()) {
- filter_iter.Next() = {};
+ auto& next = filter_iter.Next();
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "removing configuration " << next->config.to_string()
+ << " for entry: " << entry->name
+ << ", because its SDK version is smaller than minSdk");
+ }
+ next = {};
}
}
}
@@ -126,6 +133,12 @@
util::make_unique<ResourceConfigValue>(
config_value->config.CopyWithoutSdkVersion(),
config_value->product);
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "overriding resource: " << entry->name
+ << ", removing SDK version from configuration "
+ << config_value->config.to_string());
+ }
new_value->value = std::move(config_value->value);
config_value = std::move(new_value);
@@ -147,10 +160,14 @@
bool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
TRACE_NAME("VersionCollapser::Consume");
const int min_sdk = context->GetMinSdkVersion();
+ if (context->IsVerbose()) {
+ context->GetDiagnostics()->Note(android::DiagMessage()
+ << "Running VersionCollapser with minSdk = " << min_sdk);
+ }
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
- CollapseVersions(min_sdk, entry.get());
+ CollapseVersions(context, min_sdk, entry.get());
}
}
}