Merge "Add MR2ProviderService APIs to support system media routing" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 56831d7..4302d24 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -23,6 +23,7 @@
"aconfig_mediacodec_flags_java_lib",
"aconfig_settingslib_flags_java_lib",
"aconfig_trade_in_mode_flags_java_lib",
+ "adpf_flags_java_lib",
"android.adaptiveauth.flags-aconfig-java",
"android.app.appfunctions.flags-aconfig-java",
"android.app.assist.flags-aconfig-java",
@@ -328,6 +329,7 @@
name: "android_nfc_flags_aconfig_c_lib",
vendor_available: true,
aconfig_declarations: "android.nfc.flags-aconfig",
+ min_sdk_version: "34",
apex_available: [
"//apex_available:platform",
"com.android.nfcservices",
@@ -652,6 +654,8 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
+ "com.android.art",
+ "com.android.art.debug",
"com.android.permission",
],
}
@@ -871,6 +875,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// Adaptive Performance
+java_aconfig_library {
+ name: "adpf_flags_java_lib",
+ aconfig_declarations: "adpf_flags",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Graphics
java_aconfig_library {
name: "hwui_flags_java_lib",
diff --git a/Android.bp b/Android.bp
index 424a4a71..42028e0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,7 +83,6 @@
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
- ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -313,7 +312,6 @@
":framework-telecomm-sources",
":framework-telephony-common-sources",
":framework-telephony-sources",
- ":framework-vcn-util-sources",
":framework-wifi-annotations",
":framework-wifi-non-updatable-sources",
":PacProcessor-aidl-sources",
@@ -371,6 +369,7 @@
"view-inspector-annotation-processor",
"staledataclass-annotation-processor",
"error_prone_android_framework",
+ "systemfeatures-metadata-processor",
],
// Exports needed for staledataclass-annotation-processor, see b/139342589.
javacflags: [
@@ -597,7 +596,7 @@
srcs: [
"core/java/com/android/internal/util/HexDump.java",
"core/java/com/android/internal/util/WakeupMessage.java",
- "services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java",
+ "core/java/android/net/vcn/util/PersistableBundleUtils.java",
"telephony/java/android/telephony/Annotation.java",
],
}
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index d748a3b..d83109a 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -18,7 +18,7 @@
tests/
tools/
bpfmt = -d
-ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/core/api/current.txt b/core/api/current.txt
index bd5614f..1a34781 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8917,6 +8917,7 @@
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA";
+ field @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public static final String EXTRA_SESSION_TRANSFER_WEB_URI = "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
}
public class AssistStructure implements android.os.Parcelable {
@@ -9999,12 +10000,12 @@
method public int describeContents();
method @Nullable public android.companion.AssociatedDevice getAssociatedDevice();
method @FlaggedApi("android.companion.association_device_icon") @Nullable public android.graphics.drawable.Icon getDeviceIcon();
+ method @FlaggedApi("android.companion.association_tag") @Nullable public android.companion.DeviceId getDeviceId();
method @Nullable public android.net.MacAddress getDeviceMacAddress();
method @Nullable public String getDeviceProfile();
method @Nullable public CharSequence getDisplayName();
method public int getId();
method public int getSystemDataSyncFlags();
- method @FlaggedApi("android.companion.association_tag") @Nullable public String getTag();
method public boolean isSelfManaged();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationInfo> CREATOR;
@@ -10077,7 +10078,6 @@
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void attachSystemDataTransport(int, @NonNull java.io.InputStream, @NonNull java.io.OutputStream) throws android.companion.DeviceNotAssociatedException;
method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
- method @FlaggedApi("android.companion.association_tag") public void clearAssociationTag(int);
method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
method public void disableSystemDataSyncForTypes(int, int);
method @Deprecated public void disassociate(@NonNull String);
@@ -10089,7 +10089,7 @@
method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int);
method @FlaggedApi("android.companion.unpair_associated_device") @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond(int);
method public void requestNotificationAccess(android.content.ComponentName);
- method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
+ method @FlaggedApi("android.companion.association_tag") public void setDeviceId(int, @Nullable android.companion.DeviceId);
method @Deprecated @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
method @FlaggedApi("android.companion.device_presence") @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull android.companion.ObservingDevicePresenceRequest);
method public void startSystemDataTransfer(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.companion.CompanionException>) throws android.companion.DeviceNotAssociatedException;
@@ -10134,6 +10134,21 @@
public interface DeviceFilter<D extends android.os.Parcelable> extends android.os.Parcelable {
}
+ @FlaggedApi("android.companion.association_tag") public final class DeviceId implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getCustomId();
+ method @Nullable public android.net.MacAddress getMacAddress();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.companion.DeviceId> CREATOR;
+ }
+
+ public static final class DeviceId.Builder {
+ ctor public DeviceId.Builder();
+ method @NonNull public android.companion.DeviceId build();
+ method @NonNull public android.companion.DeviceId.Builder setCustomId(@Nullable String);
+ method @NonNull public android.companion.DeviceId.Builder setMacAddress(@Nullable android.net.MacAddress);
+ }
+
public class DeviceNotAssociatedException extends java.lang.RuntimeException {
}
@@ -20768,6 +20783,7 @@
public final class VirtualDisplayConfig implements android.os.Parcelable {
method public int describeContents();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDefaultBrightness();
method public int getDensityDpi();
method @NonNull public java.util.Set<java.lang.String> getDisplayCategories();
method public int getFlags();
@@ -20780,10 +20796,16 @@
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.VirtualDisplayConfig> CREATOR;
}
+ @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public static interface VirtualDisplayConfig.BrightnessListener {
+ method public void onBrightnessChanged(@FloatRange(from=0.0f, to=1.0f) float);
+ }
+
public static final class VirtualDisplayConfig.Builder {
ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String);
method @NonNull public android.hardware.display.VirtualDisplayConfig build();
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setBrightnessListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.display.VirtualDisplayConfig.BrightnessListener);
+ method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDefaultBrightness(@FloatRange(from=0.0f, to=1.0f) float);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.Set<java.lang.String>);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int);
method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float);
@@ -24853,6 +24875,7 @@
method @Nullable public android.net.Uri getIconUri();
method @NonNull public String getId();
method @NonNull public CharSequence getName();
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public java.util.Set<java.lang.String> getRequiredPermissions();
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") public int getSuitabilityStatus();
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") public int getSupportedRoutingTypes();
method public int getType();
@@ -24921,6 +24944,7 @@
method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+ method @FlaggedApi("com.android.media.flags.enable_route_visibility_control_api") @NonNull public android.media.MediaRoute2Info.Builder setRequiredPermissions(@NonNull java.util.Set<java.lang.String>);
method @FlaggedApi("com.android.media.flags.enable_built_in_speaker_route_suitability_statuses") @NonNull public android.media.MediaRoute2Info.Builder setSuitabilityStatus(int);
method @FlaggedApi("com.android.media.flags.enable_mirroring_in_media_router_2") @NonNull public android.media.MediaRoute2Info.Builder setSupportedRoutingTypes(int);
method @NonNull public android.media.MediaRoute2Info.Builder setType(int);
@@ -33617,14 +33641,12 @@
@FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
ctor public CpuHeadroomParams();
method public int getCalculationType();
- method @IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public long getCalculationWindowMillis();
+ method @IntRange(from=0x32, to=0x2710) public long getCalculationWindowMillis();
method public void setCalculationType(int);
- method public void setCalculationWindowMillis(@IntRange(from=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.CpuHeadroomParams.CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int);
+ method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int);
method public void setTids(@NonNull int...);
field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
- field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710
- field public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32
}
public final class CpuUsageInfo implements android.os.Parcelable {
@@ -33877,13 +33899,11 @@
@FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
ctor public GpuHeadroomParams();
method public int getCalculationType();
- method @IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) public int getCalculationWindowMillis();
+ method @IntRange(from=0x32, to=0x2710) public int getCalculationWindowMillis();
method public void setCalculationType(int);
- method public void setCalculationWindowMillis(@IntRange(from=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to=android.os.GpuHeadroomParams.GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int);
+ method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int);
field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
- field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710
- field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32
}
public class Handler {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 1a949d84..804210f 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -100,6 +100,7 @@
method @NonNull public android.content.Context createContextForSdkInSandbox(@NonNull android.content.pm.ApplicationInfo, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @NonNull public android.os.IBinder getProcessToken();
method @NonNull public android.os.UserHandle getUser();
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence_module") public static final int BIND_FOREGROUND_SERVICE = 67108864; // 0x4000000
field public static final String PAC_PROXY_SERVICE = "pac_proxy";
field public static final String TEST_NETWORK_SERVICE = "test_network";
field @FlaggedApi("android.os.mainline_vcn_platform_api") public static final String VCN_MANAGEMENT_SERVICE = "vcn_management";
@@ -129,6 +130,7 @@
public abstract class PackageManager {
method @NonNull public String getSdkSandboxPackageName();
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") @NonNull public static android.content.pm.SigningInfo getVerifiedSigningInfo(@NonNull String, int) throws android.content.pm.SigningInfoException;
method @RequiresPermission(android.Manifest.permission.MAKE_UID_VISIBLE) public void makeUidVisible(int, int);
field public static final String EXTRA_VERIFICATION_ROOT_HASH = "android.content.pm.extra.VERIFICATION_ROOT_HASH";
field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
@@ -139,6 +141,18 @@
method @NonNull public String getPackageName();
}
+ public final class SigningInfo implements android.os.Parcelable {
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") public boolean signersMatchExactly(@NonNull android.content.pm.SigningInfo);
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_JAR = 1; // 0x1
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V2 = 2; // 0x2
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V3 = 3; // 0x3
+ field @FlaggedApi("android.content.pm.cloud_compilation_pm") public static final int VERSION_SIGNING_BLOCK_V4 = 4; // 0x4
+ }
+
+ @FlaggedApi("android.content.pm.cloud_compilation_pm") public class SigningInfoException extends java.lang.Exception {
+ method @FlaggedApi("android.content.pm.cloud_compilation_pm") public int getCode();
+ }
+
}
package android.hardware.usb {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 83699ac..d7010ae5 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -516,6 +516,7 @@
field public static final int config_defaultCallScreening = 17039398; // 0x1040026
field public static final int config_defaultDialer = 17039395; // 0x1040023
field public static final int config_defaultNotes = 17039429; // 0x1040045
+ field @FlaggedApi("android.permission.flags.cross_user_role_platform_api_enabled") public static final int config_defaultReservedForTestingProfileGroupExclusivity;
field @FlaggedApi("android.permission.flags.retail_demo_role_enabled") public static final int config_defaultRetailDemo = 17039432; // 0x1040048
field public static final int config_defaultSms = 17039396; // 0x1040024
field @FlaggedApi("android.permission.flags.wallet_role_enabled") public static final int config_defaultWallet = 17039433; // 0x1040049
@@ -902,7 +903,7 @@
public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public String getAttributionTag();
- method @FlaggedApi("android.permission.flags.device_id_in_op_proxy_info_enabled") @Nullable public String getDeviceId();
+ method @FlaggedApi("android.permission.flags.device_id_in_op_proxy_info_enabled") @NonNull public String getDeviceId();
method @Nullable public String getPackageName();
method @IntRange(from=0) public int getUid();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -3543,6 +3544,7 @@
public static interface VirtualDeviceManager.ActivityListener {
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onActivityLaunchBlocked(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle, @Nullable android.content.IntentSender);
method public void onDisplayEmpty(int);
+ method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowHidden(int);
method @FlaggedApi("android.companion.virtualdevice.flags.activity_control_api") public default void onSecureWindowShown(int, @NonNull android.content.ComponentName, @NonNull android.os.UserHandle);
method @Deprecated public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
method public default void onTopActivityChanged(int, @NonNull android.content.ComponentName, int);
@@ -5252,9 +5254,9 @@
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSession implements java.lang.AutoCloseable {
- method public void close();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public void close();
method @Nullable public android.hardware.contexthub.HubServiceInfo getServiceInfo();
- method @NonNull public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public android.hardware.location.ContextHubTransaction<java.lang.Void> sendMessage(@NonNull android.hardware.contexthub.HubMessage);
}
@FlaggedApi("android.chre.flags.offload_api") public class HubEndpointSessionResult {
@@ -5455,19 +5457,13 @@
field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
}
- public abstract static class VirtualDisplay.Callback {
- method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") public void onRequestedBrightnessChanged(@FloatRange(from=0.0f, to=1.0f) float);
- }
-
public final class VirtualDisplayConfig implements android.os.Parcelable {
- method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @FloatRange(from=0.0f, to=1.0f) public float getDefaultBrightness();
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @Nullable public android.view.DisplayCutout getDisplayCutout();
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") public boolean isHomeSupported();
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") public boolean isIgnoreActivitySizeRestrictions();
}
public static final class VirtualDisplayConfig.Builder {
- method @FlaggedApi("android.companion.virtualdevice.flags.device_aware_display_power") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDefaultBrightness(@FloatRange(from=0.0f, to=1.0f) float);
method @FlaggedApi("android.companion.virtualdevice.flags.virtual_display_insets") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCutout(@Nullable android.view.DisplayCutout);
method @FlaggedApi("android.companion.virtual.flags.vdm_custom_home") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setHomeSupported(boolean);
method @FlaggedApi("com.android.window.flags.vdm_force_app_universal_resizable_api") @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setIgnoreActivitySizeRestrictions(boolean);
@@ -7196,8 +7192,8 @@
method public int getAudioCapabilities();
method @NonNull public byte[] getData();
method @NonNull public java.util.List<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra> getKeyphrases();
- method public boolean isAllowMultipleTriggers();
method public boolean isCaptureRequested();
+ method public boolean isMultipleTriggersAllowed();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.RecognitionConfig> CREATOR;
}
@@ -7205,11 +7201,11 @@
public static final class SoundTrigger.RecognitionConfig.Builder {
ctor public SoundTrigger.RecognitionConfig.Builder();
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig build();
- method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAllowMultipleTriggers(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setAudioCapabilities(int);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setCaptureRequested(boolean);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setData(@NonNull byte[]);
method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setKeyphrases(@NonNull java.util.Collection<android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra>);
+ method @NonNull public android.hardware.soundtrigger.SoundTrigger.RecognitionConfig.Builder setMultipleTriggersAllowed(boolean);
}
public static class SoundTrigger.RecognitionEvent {
@@ -7779,7 +7775,7 @@
}
public final class MediaCas implements java.lang.AutoCloseable {
- method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
+ method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceOwnershipRetention(boolean);
method @FlaggedApi("android.media.tv.flags.mediacas_update_client_profile_priority") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public boolean updateResourcePriority(int, int);
}
@@ -8692,8 +8688,8 @@
method public int setLnaEnabled(boolean);
method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
method public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
- method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceHolderRetain(boolean);
method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener);
+ method @FlaggedApi("android.media.tv.flags.set_resource_holder_retain") @RequiresPermission("android.permission.TUNER_RESOURCE_ACCESS") public void setResourceOwnershipRetention(boolean);
method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
method public int transferOwner(@NonNull android.media.tv.tuner.Tuner);
method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
@@ -16095,6 +16091,10 @@
field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CALL_FORWARDING_INDICATOR_CHANGED = 4; // 0x4
field public static final int EVENT_CALL_STATE_CHANGED = 6; // 0x6
field public static final int EVENT_CARRIER_NETWORK_CHANGED = 17; // 0x11
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44; // 0x2c
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43; // 0x2b
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42; // 0x2a
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45; // 0x2d
field @FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int EVENT_CELLULAR_IDENTIFIER_DISCLOSED_CHANGED = 47; // 0x2f
field @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public static final int EVENT_CELL_INFO_CHANGED = 11; // 0xb
field @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public static final int EVENT_CELL_LOCATION_CHANGED = 5; // 0x5
@@ -16139,6 +16139,13 @@
method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallStatesChanged(@NonNull java.util.List<android.telephony.CallState>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static interface TelephonyCallback.CarrierRoamingNtnModeListener {
+ method public default void onCarrierRoamingNtnAvailableServicesChanged(@NonNull int[]);
+ method public default void onCarrierRoamingNtnEligibleStateChanged(boolean);
+ method public void onCarrierRoamingNtnModeChanged(boolean);
+ method public default void onCarrierRoamingNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrength);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.cellular_identifier_disclosure_indications") public static interface TelephonyCallback.CellularIdentifierDisclosedListener {
method public void onCellularIdentifierDisclosedChanged(@NonNull android.telephony.CellularIdentifierDisclosure);
}
@@ -18741,6 +18748,14 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.AntennaPosition> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class EarfcnRange implements android.os.Parcelable {
+ method public int describeContents();
+ method @IntRange(from=0, to=65535) public int getEndEarfcn();
+ method @IntRange(from=0, to=65535) public int getStartEarfcn();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.EarfcnRange> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public class EnableRequestAttributes {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isDemoMode();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public boolean isEmergencyMode();
@@ -18779,6 +18794,14 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.PointingInfo> CREATOR;
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteAccessConfiguration implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.telephony.satellite.SatelliteInfo> getSatelliteInfos();
+ method @NonNull public java.util.List<java.lang.Integer> getTagIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteAccessConfiguration> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteCapabilities implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public java.util.Map<java.lang.Integer,android.telephony.satellite.AntennaPosition> getAntennaPositionMap();
@@ -18793,6 +18816,11 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteCommunicationAllowedStateCallback {
+ method public default void onSatelliteAccessConfigurationChanged(@Nullable android.telephony.satellite.SatelliteAccessConfiguration);
+ method public void onSatelliteCommunicationAllowedStateChanged(boolean);
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int describeContents();
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @NonNull public byte[] getSatelliteDatagram();
@@ -18804,18 +18832,32 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteDatagramReceived(long, @NonNull android.telephony.satellite.SatelliteDatagram, int, @NonNull java.util.function.Consumer<java.lang.Void>);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.Integer> getBands();
+ method @NonNull public java.util.List<android.telephony.satellite.EarfcnRange> getEarfcnRanges();
+ method @NonNull public java.util.UUID getSatelliteId();
+ method @NonNull public android.telephony.satellite.SatellitePosition getSatellitePosition();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteInfo> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCommunicationAllowedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForIncomingDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSupportedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -18828,16 +18870,21 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void requestIsSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestNtnSignalStrength(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.NtnSignalStrength,android.telephony.satellite.SatelliteManager.SatelliteException>);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteSubscriberProvisionStatus(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void sendDatagram(int, @NonNull android.telephony.satellite.SatelliteDatagram, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void setDeviceAlignedWithSatellite(boolean);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCommunicationAllowedStateChanged(@NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForIncomingDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForProvisionStateChanged(@NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForSupportedStateChanged(@NonNull android.telephony.satellite.SatelliteSupportedStateCallback);
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION = "android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED = "android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_CHECK_PENDING_INCOMING_SMS = 7; // 0x7
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_KEEP_ALIVE = 3; // 0x3
field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final int DATAGRAM_TYPE_LAST_SOS_MESSAGE_NO_HELP_NEEDED = 5; // 0x5
@@ -18856,6 +18903,7 @@
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int DISPLAY_MODE_UNKNOWN = 0; // 0x0
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_SOS = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int EMERGENCY_CALL_TO_SATELLITE_HANDOVER_TYPE_T911 = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT = "android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_EMTC_NTN = 3; // 0x3
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NB_IOT_NTN = 1; // 0x1
field @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
@@ -18922,18 +18970,100 @@
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public int getErrorCode();
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteModemEnableRequestAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriptionInfo getSatelliteSubscriptionInfo();
+ method public boolean isDemoMode();
+ method public boolean isEmergencyMode();
+ method public boolean isEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteModemEnableRequestAttributes> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteModemStateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteModemStateChanged(int);
}
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatellitePosition implements android.os.Parcelable {
+ method public int describeContents();
+ method public double getAltitudeKm();
+ method public double getLongitudeDegrees();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatellitePosition> CREATOR;
+ }
+
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteProvisionStateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteProvisionStateChanged(boolean);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSatelliteSubscriptionProvisionStateChanged(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberProvisionStatus>);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriberInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCarrierId();
+ method @NonNull public String getNiddApn();
+ method public int getSubId();
+ method @NonNull public String getSubscriberId();
+ method public int getSubscriberIdType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriberInfo> CREATOR;
+ field public static final int ICCID = 0; // 0x0
+ field public static final int IMSI_MSISDN = 1; // 0x1
+ }
+
+ public static final class SatelliteSubscriberInfo.Builder {
+ ctor public SatelliteSubscriberInfo.Builder();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo build();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setCarrierId(int);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setNiddApn(@NonNull String);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubId(int);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberId(@NonNull String);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberIdType(int);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriberProvisionStatus implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo getSatelliteSubscriberInfo();
+ method public boolean isProvisioned();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriberProvisionStatus> CREATOR;
+ }
+
+ public static final class SatelliteSubscriberProvisionStatus.Builder {
+ ctor public SatelliteSubscriberProvisionStatus.Builder();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus build();
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus.Builder setProvisioned(boolean);
+ method @NonNull public android.telephony.satellite.SatelliteSubscriberProvisionStatus.Builder setSatelliteSubscriberInfo(@NonNull android.telephony.satellite.SatelliteSubscriberInfo);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriptionInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public String getIccId();
+ method @NonNull public String getNiddApn();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriptionInfo> CREATOR;
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteSupportedStateCallback {
+ method public void onSatelliteSupportedStateChanged(boolean);
}
@FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public interface SatelliteTransmissionUpdateCallback {
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onReceiveDatagramStateChanged(int, int, int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatellitePositionChanged(@NonNull android.telephony.satellite.PointingInfo);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramRequested(int);
method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSendDatagramStateChanged(int, int, int);
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public default void onSendDatagramStateChanged(int, int, int, int);
+ }
+
+ @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SystemSelectionSpecifier implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public int[] getBands();
+ method @NonNull public int[] getEarfcns();
+ method @NonNull public String getMccMnc();
+ method @NonNull public java.util.List<android.telephony.satellite.SatelliteInfo> getSatelliteInfos();
+ method @NonNull public int[] getTagIds();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SystemSelectionSpecifier> CREATOR;
}
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 3a62ac9..603677e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -934,6 +934,7 @@
method @NonNull public android.companion.AssociationInfo build();
method @NonNull public android.companion.AssociationInfo.Builder setAssociatedDevice(@Nullable android.companion.AssociatedDevice);
method @FlaggedApi("android.companion.association_device_icon") @NonNull public android.companion.AssociationInfo.Builder setDeviceIcon(@Nullable android.graphics.drawable.Icon);
+ method @FlaggedApi("android.companion.association_tag") @NonNull public android.companion.AssociationInfo.Builder setDeviceId(@Nullable android.companion.DeviceId);
method @NonNull public android.companion.AssociationInfo.Builder setDeviceMacAddress(@Nullable android.net.MacAddress);
method @NonNull public android.companion.AssociationInfo.Builder setDeviceProfile(@Nullable String);
method @NonNull public android.companion.AssociationInfo.Builder setDisplayName(@Nullable CharSequence);
@@ -942,7 +943,6 @@
method @NonNull public android.companion.AssociationInfo.Builder setRevoked(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSelfManaged(boolean);
method @NonNull public android.companion.AssociationInfo.Builder setSystemDataSyncFlags(int);
- method @FlaggedApi("android.companion.association_tag") @NonNull public android.companion.AssociationInfo.Builder setTag(@Nullable String);
method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long);
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index d1eb8e8..4bf87f91 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -141,6 +141,14 @@
}
/**
+ * @see #sPostNotifyEndListenerEnabled
+ * @hide
+ */
+ public static boolean isPostNotifyEndListenerEnabled() {
+ return sPostNotifyEndListenerEnabled;
+ }
+
+ /**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
* running after that delay elapses. A non-delayed animation will have its initial
* value(s) set immediately, followed by calls to
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f8186d6..3c1cce9 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6995,21 +6995,44 @@
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
- try {
- switch (profileType) {
- default:
+ switch (profileType) {
+ case ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD:
+ if (!com.android.art.flags.Flags.alwaysEnableProfileCode()) {
+ Slog.w(TAG, "Low overhead tracing feature is not enabled");
+ break;
+ }
+ VMDebug.startLowOverheadTrace();
+ break;
+ default:
+ try {
mProfiler.setProfiler(profilerInfo);
mProfiler.startProfiling();
break;
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile
- + " -- can the process access this path?");
- } finally {
- profilerInfo.closeFd();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile
+ + " -- can the process access this path?");
+ } finally {
+ profilerInfo.closeFd();
+ }
}
} else {
switch (profileType) {
+ case ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD:
+ if (!com.android.art.flags.Flags.alwaysEnableProfileCode()) {
+ if (profilerInfo != null) {
+ profilerInfo.closeFd();
+ }
+ Slog.w(TAG, "Low overhead tracing feature is not enabled");
+ break;
+ }
+ if (profilerInfo != null) {
+ FileDescriptor fd = profilerInfo.profileFd.getFileDescriptor();
+ VMDebug.TraceDestination dst =
+ VMDebug.TraceDestination.fromFileDescriptor(fd);
+ VMDebug.dumpLowOverheadTrace(dst);
+ }
+ VMDebug.stopLowOverheadTrace();
+ break;
default:
mProfiler.stopProfiling();
break;
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 009cd72..61b5687 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
@@ -69,6 +70,14 @@
public int topActivityLetterboxAppWidth = PROPERTY_VALUE_UNSET;
/**
+ * Contains the top activity bounds when the activity is letterboxed.
+ * It's {@code null} if there's no top activity in the task or it's not letterboxed.
+ */
+ // TODO(b/379824541) Remove duplicate information.
+ @Nullable
+ public Rect topActivityLetterboxBounds;
+
+ /**
* Stores camera-related app compat information about a particular Task.
*/
public CameraCompatTaskInfo cameraCompatTaskInfo = CameraCompatTaskInfo.create();
@@ -378,6 +387,7 @@
topActivityLetterboxHeight = source.readInt();
topActivityLetterboxAppWidth = source.readInt();
topActivityLetterboxAppHeight = source.readInt();
+ topActivityLetterboxBounds = source.readTypedObject(Rect.CREATOR);
cameraCompatTaskInfo = source.readTypedObject(CameraCompatTaskInfo.CREATOR);
}
@@ -393,6 +403,7 @@
dest.writeInt(topActivityLetterboxHeight);
dest.writeInt(topActivityLetterboxAppWidth);
dest.writeInt(topActivityLetterboxAppHeight);
+ dest.writeTypedObject(topActivityLetterboxBounds, flags);
dest.writeTypedObject(cameraCompatTaskInfo, flags);
}
@@ -415,6 +426,7 @@
+ " isUserFullscreenOverrideEnabled=" + isUserFullscreenOverrideEnabled()
+ " isSystemFullscreenOverrideEnabled=" + isSystemFullscreenOverrideEnabled()
+ " hasMinAspectRatioOverride=" + hasMinAspectRatioOverride()
+ + " topActivityLetterboxBounds=" + topActivityLetterboxBounds
+ " cameraCompatTaskInfo=" + cameraCompatTaskInfo.toString()
+ "}";
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 3476578..1913812 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -3586,7 +3586,7 @@
/** Attribution tag of the proxy that noted the op */
private @Nullable String mAttributionTag;
/** Persistent device Id of the proxy that noted the op */
- private @Nullable String mDeviceId;
+ private @NonNull String mDeviceId;
/**
* Reinit existing object with new state.
@@ -3599,7 +3599,7 @@
* @hide
*/
public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
- @Nullable String attributionTag, @Nullable String deviceId) {
+ @Nullable String attributionTag, @NonNull String deviceId) {
mUid = Preconditions.checkArgumentNonnegative(uid);
mPackageName = packageName;
mAttributionTag = attributionTag;
@@ -3662,7 +3662,8 @@
"from", 0);
this.mPackageName = packageName;
this.mAttributionTag = attributionTag;
- this.mDeviceId = deviceId;
+ this.mDeviceId = deviceId == null ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ : deviceId;
}
/**
* Copy constructor
@@ -3705,7 +3706,7 @@
* Persistent device Id of the proxy that noted the op
*/
@FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED)
- public @Nullable String getDeviceId() { return mDeviceId; }
+ public @NonNull String getDeviceId() { return mDeviceId; }
@Override
@DataClass.Generated.Member
@@ -3716,12 +3717,12 @@
byte flg = 0;
if (mPackageName != null) flg |= 0x2;
if (mAttributionTag != null) flg |= 0x4;
- if (mDeviceId != null) flg |= 0x8;
+ flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mUid);
if (mPackageName != null) dest.writeString(mPackageName);
if (mAttributionTag != null) dest.writeString(mAttributionTag);
- if (mDeviceId != null) dest.writeString(mDeviceId);
+ dest.writeString(mDeviceId);
}
@Override
@@ -3739,7 +3740,8 @@
int uid = in.readInt();
String packageName = (flg & 0x2) == 0 ? null : in.readString();
String attributionTag = (flg & 0x4) == 0 ? null : in.readString();
- String deviceId = (flg & 0x8) == 0 ? null : in.readString();
+ String deviceId = (flg & 0x8) == 0 ? VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+ : in.readString();
this.mUid = uid;
com.android.internal.util.AnnotationValidations.validate(
IntRange.class, null, mUid,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 0668958..5048661 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -1028,4 +1028,14 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DEVICE_POWER)")
void noteAppRestrictionEnabled(in String packageName, int uid, int restrictionType,
boolean enabled, int reason, in String subReason, int source, long threshold);
+
+ /**
+ * Creates and returns a new IntentCreatorToken that keeps the creatorUid and refreshes key
+ * fields of the intent passed in.
+ *
+ * @param intent The intent with key fields out of sync of the IntentCreatorToken it contains.
+ * @hide
+ */
+ @EnforcePermission("INTERACT_ACROSS_USERS_FULL")
+ IBinder refreshIntentCreatorToken(in Intent intent);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index e2d20cb..a4d8a5c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -821,14 +821,14 @@
R.layout.notification_2025_template_collapsed_call,
R.layout.notification_2025_template_expanded_call,
R.layout.notification_2025_template_collapsed_messaging,
+ R.layout.notification_2025_template_expanded_messaging,
R.layout.notification_2025_template_collapsed_media,
- R.layout.notification_template_material_big_picture,
- R.layout.notification_template_material_big_text,
- R.layout.notification_template_material_inbox,
- R.layout.notification_template_material_big_messaging,
- R.layout.notification_template_material_big_media,
- R.layout.notification_template_header -> true;
- case R.layout.notification_template_material_progress -> Flags.apiRichOngoing();
+ R.layout.notification_2025_template_expanded_media,
+ R.layout.notification_2025_template_expanded_big_picture,
+ R.layout.notification_2025_template_expanded_big_text,
+ R.layout.notification_2025_template_expanded_inbox -> true;
+ case R.layout.notification_2025_template_expanded_progress
+ -> Flags.apiRichOngoing();
default -> false;
};
}
@@ -5964,7 +5964,7 @@
private static void setHeaderlessVerticalMargins(RemoteViews contentView,
StandardTemplateParams p, boolean hasSecondLine) {
- if (!p.mHeaderless) {
+ if (Flags.notificationsRedesignTemplates() || !p.mHeaderless) {
return;
}
int marginDimen = hasSecondLine
@@ -6445,10 +6445,13 @@
// Clear view padding to allow buttons to start on the left edge.
// This must be done before 'setEmphasizedMode' which sets top/bottom margins.
big.setViewPadding(R.id.actions, 0, 0, 0, 0);
- // Add an optional indent that will make buttons start at the correct column when
- // there is enough space to do so (and fall back to the left edge if not).
- big.setInt(R.id.actions, "setCollapsibleIndentDimen",
- R.dimen.call_notification_collapsible_indent);
+ if (!Flags.notificationsRedesignTemplates()) {
+ // Add an optional indent that will make buttons start at the correct column
+ // when there is enough space to do so (and fall back to the left edge if not).
+ // This is handled directly in NotificationActionListLayout in the new design.
+ big.setInt(R.id.actions, "setCollapsibleIndentDimen",
+ R.dimen.call_notification_collapsible_indent);
+ }
if (evenlyDividedCallStyleActionLayout()) {
if (CallStyle.DEBUG_NEW_ACTION_LAYOUT) {
Log.d(TAG, "setting evenly divided mode on action list");
@@ -6764,19 +6767,6 @@
}
/**
- * Construct a RemoteViews for the ambient version of the notification.
- *
- * @hide
- */
- public RemoteViews makeAmbientNotification() {
- RemoteViews headsUpContentView = createHeadsUpContentView(false /* increasedHeight */);
- if (headsUpContentView != null) {
- return headsUpContentView;
- }
- return createContentView();
- }
-
- /**
* Adapt the Notification header if this view is used as an expanded view.
*
* @hide
@@ -7561,15 +7551,27 @@
}
private int getBigPictureLayoutResource() {
- return R.layout.notification_template_material_big_picture;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_big_picture;
+ } else {
+ return R.layout.notification_template_material_big_picture;
+ }
}
private int getBigTextLayoutResource() {
- return R.layout.notification_template_material_big_text;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_big_text;
+ } else {
+ return R.layout.notification_template_material_big_text;
+ }
}
private int getInboxLayoutResource() {
- return R.layout.notification_template_material_inbox;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_inbox;
+ } else {
+ return R.layout.notification_template_material_inbox;
+ }
}
private int getCollapsedMessagingLayoutResource() {
@@ -7581,7 +7583,11 @@
}
private int getExpandedMessagingLayoutResource() {
- return R.layout.notification_template_material_big_messaging;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_messaging;
+ } else {
+ return R.layout.notification_template_material_big_messaging;
+ }
}
private int getCollapsedMediaLayoutResource() {
@@ -7592,6 +7598,14 @@
}
}
+ private int getExpandedMediaLayoutResource() {
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_media;
+ } else {
+ return R.layout.notification_template_material_big_media;
+ }
+ }
+
private int getConversationLayoutResource() {
if (Flags.notificationsRedesignTemplates()) {
return R.layout.notification_2025_template_conversation;
@@ -7617,7 +7631,11 @@
}
private int getProgressLayoutResource() {
- return R.layout.notification_template_material_progress;
+ if (Flags.notificationsRedesignTemplates()) {
+ return R.layout.notification_2025_template_expanded_progress;
+ } else {
+ return R.layout.notification_template_material_progress;
+ }
}
private int getActionLayoutResource() {
@@ -10541,7 +10559,7 @@
.fillTextsFrom(mBuilder);
TemplateBindResult result = new TemplateBindResult();
RemoteViews template = mBuilder.applyStandardTemplate(
- R.layout.notification_template_material_big_media, p , result);
+ mBuilder.getExpandedMediaLayoutResource(), p , result);
for (int i = 0; i < MAX_MEDIA_BUTTONS; i++) {
if (i < actionCount) {
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index bcae22a..0348b6d 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -32,6 +32,12 @@
* {@hide}
*/
public class ProfilerInfo implements Parcelable {
+ // Regular profiling which provides different modes of profiling at some performance cost.
+ public static final int PROFILE_TYPE_REGULAR = 0;
+
+ // Low overhead profiling that captures a simple sliding window of past events.
+ public static final int PROFILE_TYPE_LOW_OVERHEAD = 1;
+
// Version of the profiler output
public static final int OUTPUT_VERSION_DEFAULT = 1;
// CLOCK_TYPE_DEFAULT chooses the default used by ART. ART uses CLOCK_TYPE_DUAL by default (see
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 74d7298..8372078 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -12486,6 +12486,36 @@
}
/**
+ * Returns the {@link EnforcingAdmin} who have set this policy.
+ *
+ * <p>Important: this API is a temporary solution, hence should be kept hidden. That is because
+ * the string argument can't define policies with arguments.
+ *
+ * <p>Note that for {@link #POLICY_SUSPEND_PACKAGES} it returns the PO or DO to keep the
+ * behavior the same as before the bug fix for b/192245204.
+ *
+ * <p>This API is only callable by the system UID
+ *
+ * @param userId The user for whom to retrieve the information.
+ * @param identifier The policy enforced by admins. It could be any user restriction or
+ * policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA} and
+ * {@link DevicePolicyManager#POLICY_DISABLE_SCREEN_CAPTURE}. This also works
+ * for {@link DevicePolicyIdentifiers#MEMORY_TAGGING_POLICY}.
+ *
+ * @hide
+ */
+ public @Nullable EnforcingAdmin getEnforcingAdmin(int userId, String identifier) {
+ if (mService != null) {
+ try {
+ return mService.getEnforcingAdmin(userId, identifier);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return null;
+ }
+
+ /**
* Returns the list of {@link EnforcingAdmin}s who have set this restriction.
*
* <p>Note that for {@link #POLICY_SUSPEND_PACKAGES} it returns the PO or DO to keep the
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e7be822..03a9f99 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -279,8 +279,9 @@
boolean isNotificationListenerServicePermitted(in String packageName, int userId);
Intent createAdminSupportIntent(in String restriction);
- Bundle getEnforcingAdminAndUserDetails(int userId,String restriction);
- List<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId,String restriction);
+ Bundle getEnforcingAdminAndUserDetails(int userId, String restriction);
+ EnforcingAdmin getEnforcingAdmin(int userId, String identifier);
+ List<EnforcingAdmin> getEnforcingAdminsForRestriction(int userId, String restriction);
boolean setApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean hidden, boolean parent);
boolean isApplicationHidden(in ComponentName admin, in String callerPackage, in String packageName, boolean parent);
diff --git a/core/java/android/app/admin/UnknownAuthority.java b/core/java/android/app/admin/UnknownAuthority.java
index fdad898..82dcf7e 100644
--- a/core/java/android/app/admin/UnknownAuthority.java
+++ b/core/java/android/app/admin/UnknownAuthority.java
@@ -22,6 +22,8 @@
import android.annotation.TestApi;
import android.os.Parcel;
+import java.util.Objects;
+
/**
* Class used to identify a default value for the authority of the {@link EnforcingAdmin} setting
* a policy, meaning it is not one of the other known subclasses of {@link Authority}, this would be
@@ -31,6 +33,7 @@
*/
@SystemApi
public final class UnknownAuthority extends Authority {
+ private final String mName;
/**
* Object representing an unknown authority.
@@ -45,22 +48,40 @@
* Creates an authority that represents an admin that can set a policy but
* doesn't have a known authority (e.g. a system components).
*/
- public UnknownAuthority() {}
+ public UnknownAuthority() {
+ mName = null;
+ }
+
+ /** @hide */
+ public UnknownAuthority(String name) {
+ mName = name;
+ }
+
+ private UnknownAuthority(Parcel source) {
+ this(source.readString8());
+ }
+
+ /** @hide */
+ public String getName() {
+ return mName;
+ }
@Override
public String toString() {
- return "DefaultAuthority {}";
+ return "DefaultAuthority {" + mName + "}";
}
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
- return o != null && getClass() == o.getClass();
+ if (o != null && getClass() == o.getClass()) return false;
+ UnknownAuthority other = (UnknownAuthority) o;
+ return Objects.equals(mName, other.mName);
}
@Override
public int hashCode() {
- return 0;
+ return mName.hashCode();
}
@Override
@@ -69,14 +90,16 @@
}
@Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {}
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mName);
+ }
@NonNull
public static final Creator<UnknownAuthority> CREATOR =
new Creator<UnknownAuthority>() {
@Override
public UnknownAuthority createFromParcel(Parcel source) {
- return UNKNOWN_AUTHORITY;
+ return new UnknownAuthority(source);
}
@Override
diff --git a/core/java/android/app/appfunctions/AppFunctionException.java b/core/java/android/app/appfunctions/AppFunctionException.java
index cbd1d93..c8d80d3 100644
--- a/core/java/android/app/appfunctions/AppFunctionException.java
+++ b/core/java/android/app/appfunctions/AppFunctionException.java
@@ -29,7 +29,14 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
-/** Represents an app function related errors. */
+/**
+ * Represents an app function related error.
+ *
+ * <p>This exception may include an {@link AppFunctionException#getExtras() Bundle}
+ * containing additional error-specific metadata.
+ *
+ * <p>The AppFunction SDK can expose structured APIs by packing and unpacking this Bundle.
+ */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class AppFunctionException extends Exception implements Parcelable {
/**
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
index 1557815..a88198a 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionRequest.java
@@ -27,7 +27,16 @@
import java.util.Objects;
-/** A request to execute an app function. */
+/**
+ * A request to execute an app function.
+ *
+ * <p>The {@link ExecuteAppFunctionRequest#getParameters()} contains the parameters for the function
+ * to be executed in a GenericDocument. Structured classes defined in the AppFunction SDK can be
+ * converted into GenericDocuments.
+ *
+ * <p>The {@link ExecuteAppFunctionRequest#getExtras()} provides any extra metadata for the request.
+ * Structured APIs can be exposed in the SDK by packing and unpacking this Bundle.
+ */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionRequest implements Parcelable {
@NonNull
diff --git a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
index acad43b..a4952f4 100644
--- a/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
+++ b/core/java/android/app/appfunctions/ExecuteAppFunctionResponse.java
@@ -27,7 +27,16 @@
import java.util.Objects;
-/** The response to an app function execution. */
+/**
+ * The response to an app function execution.
+ *
+ * <p>The {@link ExecuteAppFunctionResponse#getResultDocument()} contains the function's return
+ * value as a GenericDocument. This can be converted back into a structured class using the
+ * AppFunction SDK.
+ *
+ * <p>The {@link ExecuteAppFunctionResponse#getExtras()} provides any extra metadata returned by the
+ * function. The AppFunction SDK can expose structured APIs by packing and unpacking this Bundle.
+ */
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
public final class ExecuteAppFunctionResponse implements Parcelable {
@NonNull
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index 43a46ba..3e3ca24 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -30,6 +30,31 @@
public static final String EXTRA_APP_FUNCTION_DATA =
"android.app.assist.extra.APP_FUNCTION_DATA";
+ /**
+ * This extra can be optionally supplied in the {@link #getExtras} bundle to provide a
+ * {@link Uri} which will be utilized when transitioning a user's session to another surface.
+ *
+ * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
+ * "Open in browser" feature will use this URI to transition the current session from one
+ * surface to the other. Apps may choose to encode session or user information into this
+ * URI in order to provide a better session transfer experience.
+ *
+ * <p>Unlike {@link #setWebUri}, this URI will not be used for features where the user might
+ * accidentally share it with another user. However, developers should not encode
+ * authentication credentials into this URI, because it will be surfaced in the browser URL
+ * bar and may be copied and shared from there.
+ *
+ * <p>When providing this extra, developers should still continue to provide
+ * {@link #setWebUri} for backwards compatibility with features such as
+ * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
+ * recents URL sharing</a> which do not benefit from a session-transfer web URI.
+ *
+ * @see android.app.Activity#requestOpenInBrowserEducation()
+ */
+ @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+ public static final String EXTRA_SESSION_TRANSFER_WEB_URI =
+ "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean mIsAppProvidedIntent = false;
private boolean mIsAppProvidedWebUri = false;
diff --git a/core/java/android/app/supervision/flags.aconfig b/core/java/android/app/supervision/flags.aconfig
index d5e696d..d4f82f6 100644
--- a/core/java/android/app/supervision/flags.aconfig
+++ b/core/java/android/app/supervision/flags.aconfig
@@ -16,3 +16,11 @@
description: "Flag to enable the SupervisionService on Wear devices"
bug: "373358935"
}
+
+flag {
+ name: "enable_sync_with_dpm"
+ is_exported: true
+ namespace: "supervision"
+ description: "Flag that enables supervision when the supervision app is the profile owner"
+ bug: "377261590"
+}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 1249734..2e108a1 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -54,8 +54,6 @@
@NonNull
private final String mPackageName;
@Nullable
- private final String mTag;
- @Nullable
private final MacAddress mDeviceMacAddress;
@Nullable
private final CharSequence mDisplayName;
@@ -85,6 +83,8 @@
*/
private final long mLastTimeConnectedMs;
private final int mSystemDataSyncFlags;
+ @Nullable
+ private final DeviceId mDeviceId;
/**
* A device icon displayed on a selfManaged association dialog.
@@ -97,11 +97,11 @@
* @hide
*/
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
- @Nullable String tag, @Nullable MacAddress macAddress,
- @Nullable CharSequence displayName, @Nullable String deviceProfile,
- @Nullable AssociatedDevice associatedDevice, boolean selfManaged,
- boolean notifyOnDeviceNearby, boolean revoked, boolean pending, long timeApprovedMs,
- long lastTimeConnectedMs, int systemDataSyncFlags, @Nullable Icon deviceIcon) {
+ @Nullable MacAddress macAddress, @Nullable CharSequence displayName,
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked, boolean pending,
+ long timeApprovedMs, long lastTimeConnectedMs, int systemDataSyncFlags,
+ @Nullable Icon deviceIcon, @Nullable DeviceId deviceId) {
if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
@@ -115,7 +115,6 @@
mPackageName = packageName;
mDeviceMacAddress = macAddress;
mDisplayName = displayName;
- mTag = tag;
mDeviceProfile = deviceProfile;
mAssociatedDevice = associatedDevice;
mSelfManaged = selfManaged;
@@ -126,6 +125,7 @@
mLastTimeConnectedMs = lastTimeConnectedMs;
mSystemDataSyncFlags = systemDataSyncFlags;
mDeviceIcon = deviceIcon;
+ mDeviceId = deviceId;
}
/**
@@ -155,13 +155,13 @@
}
/**
- * @return the tag of this association.
- * @see CompanionDeviceManager#setAssociationTag(int, String)
+ * @return the {@link DeviceId} of this association.
+ * @see CompanionDeviceManager#setDeviceId(int, DeviceId)
*/
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@Nullable
- public String getTag() {
- return mTag;
+ public DeviceId getDeviceId() {
+ return mDeviceId;
}
/**
@@ -355,7 +355,6 @@
+ "mId=" + mId
+ ", mUserId=" + mUserId
+ ", mPackageName='" + mPackageName + '\''
- + ", mTag='" + mTag + '\''
+ ", mDeviceMacAddress=" + mDeviceMacAddress
+ ", mDisplayName='" + mDisplayName + '\''
+ ", mDeviceProfile='" + mDeviceProfile + '\''
@@ -369,6 +368,7 @@
mLastTimeConnectedMs == Long.MAX_VALUE
? LAST_TIME_CONNECTED_NONE : new Date(mLastTimeConnectedMs))
+ ", mSystemDataSyncFlags=" + mSystemDataSyncFlags
+ + ", mDeviceId='" + mDeviceId
+ '}';
}
@@ -386,21 +386,22 @@
&& mTimeApprovedMs == that.mTimeApprovedMs
&& mLastTimeConnectedMs == that.mLastTimeConnectedMs
&& Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mTag, that.mTag)
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mDisplayName, that.mDisplayName)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
&& mSystemDataSyncFlags == that.mSystemDataSyncFlags
&& (mDeviceIcon == null ? that.mDeviceIcon == null
- : mDeviceIcon.sameAs(that.mDeviceIcon));
+ : mDeviceIcon.sameAs(that.mDeviceIcon))
+ && Objects.equals(mDeviceId, that.mDeviceId);
}
@Override
public int hashCode() {
- return Objects.hash(mId, mUserId, mPackageName, mTag, mDeviceMacAddress, mDisplayName,
+ return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
- mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon);
+ mPending, mTimeApprovedMs, mLastTimeConnectedMs, mSystemDataSyncFlags, mDeviceIcon,
+ mDeviceId);
}
@Override
@@ -413,7 +414,6 @@
dest.writeInt(mId);
dest.writeInt(mUserId);
dest.writeString(mPackageName);
- dest.writeString(mTag);
dest.writeTypedObject(mDeviceMacAddress, 0);
dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
@@ -431,13 +431,19 @@
} else {
dest.writeInt(0);
}
+
+ if (Flags.associationTag() && mDeviceId != null) {
+ dest.writeInt(1);
+ dest.writeTypedObject(mDeviceId, flags);
+ } else {
+ dest.writeInt(0);
+ }
}
private AssociationInfo(@NonNull Parcel in) {
mId = in.readInt();
mUserId = in.readInt();
mPackageName = in.readString();
- mTag = in.readString();
mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
@@ -454,6 +460,12 @@
} else {
mDeviceIcon = null;
}
+
+ if (Flags.associationTag() && in.readInt() == 1) {
+ mDeviceId = in.readTypedObject(DeviceId.CREATOR);
+ } else {
+ mDeviceId = null;
+ }
}
@NonNull
@@ -481,7 +493,6 @@
private final int mId;
private final int mUserId;
private final String mPackageName;
- private String mTag;
private MacAddress mDeviceMacAddress;
private CharSequence mDisplayName;
private String mDeviceProfile;
@@ -494,6 +505,7 @@
private long mLastTimeConnectedMs;
private int mSystemDataSyncFlags;
private Icon mDeviceIcon;
+ private DeviceId mDeviceId;
/** @hide */
@TestApi
@@ -509,7 +521,6 @@
mId = info.mId;
mUserId = info.mUserId;
mPackageName = info.mPackageName;
- mTag = info.mTag;
mDeviceMacAddress = info.mDeviceMacAddress;
mDisplayName = info.mDisplayName;
mDeviceProfile = info.mDeviceProfile;
@@ -522,6 +533,7 @@
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
mDeviceIcon = info.mDeviceIcon;
+ mDeviceId = info.mDeviceId;
}
/**
@@ -534,7 +546,6 @@
mId = id;
mUserId = userId;
mPackageName = packageName;
- mTag = info.mTag;
mDeviceMacAddress = info.mDeviceMacAddress;
mDisplayName = info.mDisplayName;
mDeviceProfile = info.mDeviceProfile;
@@ -547,14 +558,15 @@
mLastTimeConnectedMs = info.mLastTimeConnectedMs;
mSystemDataSyncFlags = info.mSystemDataSyncFlags;
mDeviceIcon = info.mDeviceIcon;
+ mDeviceId = info.mDeviceId;
}
/** @hide */
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@TestApi
@NonNull
- public Builder setTag(@Nullable String tag) {
- mTag = tag;
+ public Builder setDeviceId(@Nullable DeviceId deviceId) {
+ mDeviceId = deviceId;
return this;
}
@@ -684,7 +696,6 @@
mId,
mUserId,
mPackageName,
- mTag,
mDeviceMacAddress,
mDisplayName,
mDeviceProfile,
@@ -696,7 +707,8 @@
mTimeApprovedMs,
mLastTimeConnectedMs,
mSystemDataSyncFlags,
- mDeviceIcon
+ mDeviceIcon,
+ mDeviceId
);
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 0466847..a96ba11 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -278,12 +278,6 @@
public static final int MESSAGE_ONEWAY_TO_WEARABLE = 0x43847987; // +TOW
/**
- * The length limit of Association tag.
- * @hide
- */
- private static final int ASSOCIATION_TAG_LENGTH_LIMIT = 1024;
-
- /**
* Callback for applications to receive updates about and the outcome of
* {@link AssociationRequest} issued via {@code associate()} call.
*
@@ -1780,57 +1774,25 @@
}
/**
- * Sets the {@link AssociationInfo#getTag() tag} for this association.
+ * Sets the {@link DeviceId deviceId} for this association.
*
- * <p>The length of the tag must be at most 1024 characters to save disk space.
- *
- * <p>This allows to store useful information about the associated devices.
+ * <p>This device id helps the system uniquely identify your device for efficient device
+ * management and prevents duplicate entries.
*
* @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
- * of the companion device recorded by CompanionDeviceManager
- * @param tag the tag of this association
+ * of the companion device recorded by CompanionDeviceManager.
+ * @param deviceId to be used as device identifier to represent the associated device.
*/
@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
@UserHandleAware
- public void setAssociationTag(int associationId, @NonNull String tag) {
- if (mService == null) {
- Log.w(TAG, "CompanionDeviceManager service is not available.");
- return;
- }
-
- Objects.requireNonNull(tag, "tag cannot be null");
-
- if (tag.length() > ASSOCIATION_TAG_LENGTH_LIMIT) {
- throw new IllegalArgumentException("Length of the tag must be at most"
- + ASSOCIATION_TAG_LENGTH_LIMIT + " characters");
- }
-
- try {
- mService.setAssociationTag(associationId, tag);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Clears the {@link AssociationInfo#getTag() tag} for this association.
- *
- * <p>The tag will be set to null for this association when calling this API.
- *
- * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the Association
- * of the companion device recorded by CompanionDeviceManager
- * @see CompanionDeviceManager#setAssociationTag(int, String)
- */
- @FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
- @UserHandleAware
- public void clearAssociationTag(int associationId) {
+ public void setDeviceId(int associationId, @Nullable DeviceId deviceId) {
if (mService == null) {
Log.w(TAG, "CompanionDeviceManager service is not available.");
return;
}
try {
- mService.clearAssociationTag(associationId);
+ mService.setDeviceId(associationId, deviceId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/DeviceId.aidl b/core/java/android/companion/DeviceId.aidl
new file mode 100644
index 0000000..d60d5f4
--- /dev/null
+++ b/core/java/android/companion/DeviceId.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+parcelable DeviceId;
\ No newline at end of file
diff --git a/core/java/android/companion/DeviceId.java b/core/java/android/companion/DeviceId.java
new file mode 100644
index 0000000..f66a1ae
--- /dev/null
+++ b/core/java/android/companion/DeviceId.java
@@ -0,0 +1,210 @@
+/*
+ * 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.companion;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.MacAddress;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.OneTimeUseBuilder;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * A device id represents a device identifier managed by the companion app.
+ */
+@FlaggedApi(Flags.FLAG_ASSOCIATION_TAG)
+public final class DeviceId implements Parcelable {
+ /**
+ * The length limit of custom id.
+ */
+ private static final int CUSTOM_ID_LENGTH_LIMIT = 1024;
+
+ private final String mCustomId;
+ private final MacAddress mMacAddress;
+
+ /**
+ * @hide
+ */
+ public DeviceId(@Nullable String customId, @Nullable MacAddress macAddress) {
+ mCustomId = customId;
+ mMacAddress = macAddress;
+ }
+
+ /**
+ * Returns true if two Device ids are represent the same device. False otherwise.
+ * @hide
+ */
+ public boolean isSameDevice(@Nullable DeviceId other) {
+ if (other == null) {
+ return false;
+ }
+
+ if (this.mCustomId != null && other.mCustomId != null) {
+ return this.mCustomId.equals(other.mCustomId);
+ }
+ if (this.mMacAddress != null && other.mMacAddress != null) {
+ return this.mMacAddress.equals(other.mMacAddress);
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ @Nullable
+ public String getMacAddressAsString() {
+ return mMacAddress != null ? mMacAddress.toString().toUpperCase(Locale.US) : null;
+ }
+
+ /**
+ * @return the custom id that managed by the companion app.
+ */
+ @Nullable
+ public String getCustomId() {
+ return mCustomId;
+ }
+
+ /**
+ * @return the mac address that managed by the companion app.
+ */
+ @Nullable
+ public MacAddress getMacAddress() {
+ return mMacAddress;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mCustomId != null) {
+ dest.writeInt(1);
+ dest.writeString8(mCustomId);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeTypedObject(mMacAddress, 0);
+
+ }
+
+ private DeviceId(@NonNull Parcel in) {
+ int flg = in.readInt();
+ if (flg == 1) {
+ mCustomId = in.readString8();
+ } else {
+ mCustomId = null;
+ }
+ mMacAddress = in.readTypedObject(MacAddress.CREATOR);
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<DeviceId> CREATOR =
+ new Parcelable.Creator<DeviceId>() {
+ @Override
+ public DeviceId[] newArray(int size) {
+ return new DeviceId[size];
+ }
+
+ @Override
+ public DeviceId createFromParcel(@android.annotation.NonNull Parcel in) {
+ return new DeviceId(in);
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mCustomId, mMacAddress);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DeviceId that)) return false;
+
+ return Objects.equals(mCustomId, that.mCustomId)
+ && Objects.equals(mMacAddress, that.mMacAddress);
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceId{"
+ + "," + "mCustomId= " + mCustomId
+ + "," + "mMacAddress= " + mMacAddress
+ + "}";
+ }
+
+ /**
+ * A builder for {@link DeviceId}
+ */
+ public static final class Builder extends OneTimeUseBuilder<DeviceId> {
+ private String mCustomId;
+ private MacAddress mMacAddress;
+
+ public Builder() {}
+
+ /**
+ * Sets the custom device id. This id is used by the Companion app to
+ * identify a specific device.
+ *
+ * @param customId the custom device id
+ * @throws IllegalArgumentException length of the custom id must more than 1024
+ * characters to save disk space.
+ */
+ @NonNull
+ public Builder setCustomId(@Nullable String customId) {
+ checkNotUsed();
+ if (customId != null
+ && customId.length() > CUSTOM_ID_LENGTH_LIMIT) {
+ throw new IllegalArgumentException("Length of the custom id must be at most "
+ + CUSTOM_ID_LENGTH_LIMIT + " characters");
+ }
+ this.mCustomId = customId;
+ return this;
+ }
+
+ /**
+ * Sets the mac address. This mac address is used by the Companion app to
+ * identify a specific device.
+ *
+ * @param macAddress the remote device mac address
+ * @throws IllegalArgumentException length of the custom id must more than 1024
+ * characters to save disk space.
+ */
+ @NonNull
+ public Builder setMacAddress(@Nullable MacAddress macAddress) {
+ checkNotUsed();
+ mMacAddress = macAddress;
+ return this;
+ }
+
+ @NonNull
+ @Override
+ public DeviceId build() {
+ markUsed();
+ if (mCustomId == null && mMacAddress == null) {
+ throw new IllegalArgumentException("At least one device id property must be"
+ + "non-null to build a DeviceId.");
+ }
+ return new DeviceId(mCustomId, mMacAddress);
+ }
+ }
+}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index de3ddec..a2b7dd9 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -28,6 +28,7 @@
import android.companion.datatransfer.PermissionSyncRequest;
import android.content.ComponentName;
import android.os.ParcelUuid;
+import android.companion.DeviceId;
/**
@@ -134,9 +135,7 @@
@EnforcePermission("MANAGE_COMPANION_DEVICES")
void enableSecureTransport(boolean enabled);
- void setAssociationTag(int associationId, String tag);
-
- void clearAssociationTag(int associationId);
+ void setDeviceId(int associationId, in DeviceId deviceId);
byte[] getBackupPayload(int userId);
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 367f1af..f8ac27d 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -90,6 +90,12 @@
*/
boolean hasCustomAudioInputSupport();
+ /**
+ * Returns whether this device is allowed to create mirror displays.
+ */
+ boolean canCreateMirrorDisplays();
+
+ /*
/*
* Turns off all trusted non-mirror displays of the virtual device.
*/
diff --git a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
index 767f52a..448793d 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceActivityListener.aidl
@@ -63,4 +63,11 @@
* @param user The user associated with the activity.
*/
void onSecureWindowShown(int displayId, in ComponentName componentName, in UserHandle user);
+
+ /**
+ * Called when a secure surface is no longer shown on the device.
+ *
+ * @param displayId The display ID on which the secure surface was shown.
+ */
+ void onSecureWindowHidden(int displayId);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceInternal.java b/core/java/android/companion/virtual/VirtualDeviceInternal.java
index d63a443..42c7441 100644
--- a/core/java/android/companion/virtual/VirtualDeviceInternal.java
+++ b/core/java/android/companion/virtual/VirtualDeviceInternal.java
@@ -166,6 +166,20 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void onSecureWindowHidden(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mActivityListenersLock) {
+ for (int i = 0; i < mActivityListeners.size(); i++) {
+ mActivityListeners.valueAt(i).onSecureWindowHidden(displayId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
};
private final IVirtualDeviceSoundEffectListener mSoundEffectListener =
@@ -617,6 +631,10 @@
mExecutor.execute(() ->
mActivityListener.onSecureWindowShown(displayId, componentName, user));
}
+
+ public void onSecureWindowHidden(int displayId) {
+ mExecutor.execute(() -> mActivityListener.onSecureWindowHidden(displayId));
+ }
}
/**
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 6ea7834..b3f09a9 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -1288,6 +1288,17 @@
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
default void onSecureWindowShown(int displayId, @NonNull ComponentName componentName,
@NonNull UserHandle user) {}
+
+ /**
+ * Called when a window with a secure surface is no longer shown on the device.
+ *
+ * @param displayId The display ID on which the window was shown before.
+ *
+ * @see Display#FLAG_SECURE
+ * @see WindowManager.LayoutParams#FLAG_SECURE
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_ACTIVITY_CONTROL_API)
+ default void onSecureWindowHidden(int displayId) {}
}
/**
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index cc57dc0..e271cf4 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -946,6 +946,34 @@
}
/**
+ * Make a clone of ClipData that only contains URIs. This reduces the size of data transfer over
+ * IPC and only retains important information for the purpose of verifying creator token of an
+ * Intent.
+ * @return a copy of ClipData with only URIs remained.
+ * @hide
+ */
+ public ClipData cloneOnlyUriItems() {
+ ArrayList<Item> items = null;
+ final int N = mItems.size();
+ for (int i = 0; i < N; i++) {
+ Item item = mItems.get(i);
+ if (item.getUri() != null) {
+ if (items == null) {
+ items = new ArrayList<>(N);
+ }
+ items.add(new Item(item.getUri()));
+ } else if (item.getIntent() != null) {
+ if (items == null) {
+ items = new ArrayList<>(N);
+ }
+ items.add(new Item(item.getIntent().cloneForCreatorToken()));
+ }
+ }
+ if (items == null || items.isEmpty()) return null;
+ return new ClipData(new ClipDescription("", new String[0]), items);
+ }
+
+ /**
* Create a new ClipData holding data of the type
* {@link ClipDescription#MIMETYPE_TEXT_PLAIN}.
*
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6e2ca2c..6ec6a62 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -18,6 +18,7 @@
import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
import static android.content.flags.Flags.FLAG_ENABLE_BIND_PACKAGE_ISOLATED_PROCESS;
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE;
import static android.security.Flags.FLAG_SECURE_LOCKDOWN;
import android.annotation.AttrRes;
@@ -640,12 +641,15 @@
public static final int BIND_FOREGROUND_SERVICE_WHILE_AWAKE = 0x02000000;
/**
- * @hide Flag for {@link #bindService}: For only the case where the binding
+ * Flag for {@link #bindService}: For only the case where the binding
* is coming from the system, set the process state to BOUND_FOREGROUND_SERVICE
* instead of the normal maximum of IMPORTANT_FOREGROUND. That is, this is
* saying that the process shouldn't participate in the normal power reduction
* modes (removing network access etc).
+ * @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE_MODULE)
public static final int BIND_FOREGROUND_SERVICE = 0x04000000;
/**
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 02eed1a..d766017 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7970,6 +7970,24 @@
}
/**
+ * Make a copy of all members important to identify an intent with its creator token.
+ * @hide
+ */
+ public @NonNull Intent cloneForCreatorToken() {
+ Intent clone = new Intent()
+ .setAction(this.mAction)
+ .setDataAndType(this.mData, this.mType)
+ .setPackage(this.mPackage)
+ .setComponent(this.mComponent)
+ .setFlags(this.mFlags & IMMUTABLE_FLAGS);
+ if (this.mClipData != null) {
+ clone.setClipData(this.mClipData.cloneOnlyUriItems());
+ }
+ clone.mCreatorTokenInfo = this.mCreatorTokenInfo;
+ return clone;
+ }
+
+ /**
* Create an intent with a given action. All other fields (data, type,
* class) are null. Note that the action <em>must</em> be in a
* namespace because Intents are used globally in the system -- for
@@ -11684,7 +11702,7 @@
Log.w(TAG, "Failure filling in extras", e);
}
}
- mCreatorTokenInfo = other.mCreatorTokenInfo;
+ fillInCreatorTokenInfo(other.mCreatorTokenInfo, changes);
if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT
&& other.mContentUserHint != UserHandle.USER_CURRENT) {
mContentUserHint = other.mContentUserHint;
@@ -11692,6 +11710,45 @@
return changes;
}
+ // keep original creator token and merge nested intent keys.
+ private void fillInCreatorTokenInfo(CreatorTokenInfo otherCreatorTokenInfo, int changes) {
+ if (otherCreatorTokenInfo != null && otherCreatorTokenInfo.mNestedIntentKeys != null) {
+ if (mCreatorTokenInfo == null) {
+ mCreatorTokenInfo = new CreatorTokenInfo();
+ }
+ ArraySet<NestedIntentKey> otherNestedIntentKeys =
+ otherCreatorTokenInfo.mNestedIntentKeys;
+ if (mCreatorTokenInfo.mNestedIntentKeys == null) {
+ mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(otherNestedIntentKeys);
+ } else {
+ ArraySet<NestedIntentKey> otherKeys;
+ if ((changes & FILL_IN_CLIP_DATA) == 0) {
+ // If clip data is Not filled in from other, do not merge clip data keys.
+ otherKeys = new ArraySet<>();
+ int N = otherNestedIntentKeys.size();
+ for (int i = 0; i < N; i++) {
+ NestedIntentKey key = otherNestedIntentKeys.valueAt(i);
+ if (key.mType != NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) {
+ otherKeys.add(key);
+ }
+ }
+ } else {
+ // If clip data is filled in from other, remove clip data keys from this
+ // creatorTokenInfo and then merge every key from the others.
+ int N = mCreatorTokenInfo.mNestedIntentKeys.size();
+ for (int i = N - 1; i >= 0; i--) {
+ NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i);
+ if (key.mType == NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) {
+ mCreatorTokenInfo.mNestedIntentKeys.removeAt(i);
+ }
+ }
+ otherKeys = otherNestedIntentKeys;
+ }
+ mCreatorTokenInfo.mNestedIntentKeys.addAll(otherKeys);
+ }
+ }
+ }
+
/**
* Merge the extras data in this intent with that of other supplied intent using the
* strategy specified using {@code extrasMerger}.
@@ -12228,6 +12285,7 @@
private IBinder mCreatorToken;
// Stores all extra keys whose values are intents for a top level intent.
private ArraySet<NestedIntentKey> mNestedIntentKeys;
+
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 23d3693..a06eb1c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -16,6 +16,7 @@
package android.content.pm;
+import static android.content.pm.SigningInfo.AppSigningSchemeVersion;
import static android.media.audio.Flags.FLAG_FEATURE_SPATIAL_AUDIO_HEADTRACKING_LOW_LATENCY;
import static com.android.internal.pm.pkg.parsing.ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES;
@@ -59,6 +60,8 @@
import android.content.IntentSender;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.dex.ArtManager;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -94,6 +97,7 @@
import android.telephony.ims.SipDelegateManager;
import android.util.AndroidException;
import android.util.Log;
+import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.parsing.PackageInfoCommonUtils;
@@ -3147,6 +3151,16 @@
public static final long MAXIMUM_VERIFICATION_TIMEOUT = 60*60*1000;
/**
+ * As the generated feature count is useful for classes that may not be compiled in the same
+ * annotation processing unit as PackageManager, we redeclare it here for visibility.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int SDK_FEATURE_COUNT =
+ com.android.internal.pm.SystemFeaturesMetadata.SDK_FEATURE_COUNT;
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
* audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
* lag in sound input or output.
@@ -11987,4 +12001,31 @@
throw new UnsupportedOperationException(
"parseServiceMetadata not implemented in subclass");
}
+
+ /**
+ * Verifies and returns the
+ * <a href="https://source.android.com/docs/security/features/apksigning">app signing</a>
+ * information of the file at the given path. This operation takes a few milliseconds.
+ *
+ * Unlike {@link #getPackageArchiveInfo(String, PackageInfoFlags)} with {@link
+ * #GET_SIGNING_CERTIFICATES}, this method does not require the file to be a package archive
+ * file.
+ *
+ * @throws SigningInfoException if the verification fails
+ *
+ * @hide
+ */
+ @FlaggedApi(android.content.pm.Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static @NonNull SigningInfo getVerifiedSigningInfo(@NonNull String path,
+ @AppSigningSchemeVersion int minAppSigningSchemeVersion) throws SigningInfoException {
+ ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ ParseResult<SigningDetails> result =
+ ApkSignatureVerifier.verify(input, path, minAppSigningSchemeVersion);
+ if (result.isError()) {
+ throw new SigningInfoException(
+ result.getErrorCode(), result.getErrorMessage(), result.getException());
+ }
+ return new SigningInfo(result.getResult());
+ }
}
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
index 23daaf2..e4fbd1f 100644
--- a/core/java/android/content/pm/SigningInfo.java
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -16,14 +16,20 @@
package android.content.pm;
+import static android.content.pm.SigningDetails.SignatureSchemeVersion;
+
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.content.pm.SigningDetails.SignatureSchemeVersion;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.security.PublicKey;
import java.util.Collection;
@@ -31,6 +37,55 @@
* Information pertaining to the signing certificates used to sign a package.
*/
public final class SigningInfo implements Parcelable {
+ /**
+ * JAR signing (v1 scheme).
+ * See https://source.android.com/docs/security/features/apksigning#v1.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int VERSION_JAR = SignatureSchemeVersion.JAR;
+
+ /**
+ * APK signature scheme v2.
+ * See https://source.android.com/docs/security/features/apksigning/v2.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int VERSION_SIGNING_BLOCK_V2 = SignatureSchemeVersion.SIGNING_BLOCK_V2;
+
+ /**
+ * APK signature scheme v3.
+ * See https://source.android.com/docs/security/features/apksigning/v3.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int VERSION_SIGNING_BLOCK_V3 = SignatureSchemeVersion.SIGNING_BLOCK_V3;
+
+ /**
+ * APK signature scheme v4.
+ * See https://source.android.com/docs/security/features/apksigning/v4.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int VERSION_SIGNING_BLOCK_V4 = SignatureSchemeVersion.SIGNING_BLOCK_V4;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"VERSION_"}, value = {
+ VERSION_JAR,
+ VERSION_SIGNING_BLOCK_V2,
+ VERSION_SIGNING_BLOCK_V3,
+ VERSION_SIGNING_BLOCK_V4,
+ })
+ public @interface AppSigningSchemeVersion {}
@NonNull
private final SigningDetails mSigningDetails;
@@ -198,6 +253,17 @@
return mSigningDetails;
}
+ /**
+ * Returns true if the signing certificates in this and other match exactly.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public boolean signersMatchExactly(@NonNull SigningInfo other) {
+ return mSigningDetails.signaturesMatchExactly(other.mSigningDetails);
+ }
+
public static final @android.annotation.NonNull Parcelable.Creator<SigningInfo> CREATOR =
new Parcelable.Creator<SigningInfo>() {
@Override
diff --git a/core/java/android/content/pm/SigningInfoException.java b/core/java/android/content/pm/SigningInfoException.java
new file mode 100644
index 0000000..a81e07e
--- /dev/null
+++ b/core/java/android/content/pm/SigningInfoException.java
@@ -0,0 +1,50 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+/**
+ * Indicates an error when verifying the
+ * <a href="https://source.android.com/docs/security/features/apksigning">app signing</a>
+ * information.
+ *
+ * @hide
+ */
+@FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class SigningInfoException extends Exception {
+ private final int mCode;
+
+ /** @hide */
+ public SigningInfoException(int code, @NonNull String message, @Nullable Throwable cause) {
+ super(message, cause);
+ mCode = code;
+ }
+
+ /**
+ * Returns a code representing the cause, in one of the installation parse return codes in
+ * {@link PackageManager}.
+ */
+ @FlaggedApi(Flags.FLAG_CLOUD_COMPILATION_PM)
+ public int getCode() {
+ return mCode;
+ }
+}
diff --git a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
index ba089f7..35e5c44 100644
--- a/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
+++ b/core/java/android/content/pm/dependencyinstaller/DependencyInstallerCallback.java
@@ -53,7 +53,14 @@
* Callback to indicate that all the requested dependencies have been resolved and their
* sessions created. See {@link DependencyInstallerService#onDependenciesRequired}.
*
+ * The system will wait for the sessions to be installed before resuming the original session
+ * which requested dependency installation.
+ *
+ * If any of the session fails to install, the system may fail the original session. The caller
+ * is expected to handle clean up of any other pending sessions remanining.
+ *
* @param sessionIds the install session IDs for all requested dependencies
+ * @throws IllegalArgumentException if session id doesn't exist or has already failed.
*/
public void onAllDependenciesResolved(@NonNull int[] sessionIds) {
try {
diff --git a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
index 92d1d9e..e4cf55d 100644
--- a/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
+++ b/core/java/android/content/pm/dependencyinstaller/IDependencyInstallerCallback.aidl
@@ -24,7 +24,7 @@
*
* {@hide}
*/
-oneway interface IDependencyInstallerCallback {
+interface IDependencyInstallerCallback {
/**
* Callback to indicate that all the requested dependencies have been resolved and have been
* committed for installation. See {@link DependencyInstallerService#onDependenciesRequired}.
@@ -38,4 +38,4 @@
* and any associated sessions have been abandoned.
*/
void onFailureToResolveAllDependencies();
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 833260a..d351ebc 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -363,6 +363,17 @@
is_fixed_read_only: true
}
+flag {
+ name: "cache_user_restrictions_read_only"
+ namespace: "multiuser"
+ description: "Cache hasUserRestriction to avoid unnecessary binder calls"
+ bug: "350419621"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ is_fixed_read_only: true
+}
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
diff --git a/core/java/android/hardware/contexthub/HubEndpointSession.java b/core/java/android/hardware/contexthub/HubEndpointSession.java
index cf952cb..b3d65c1 100644
--- a/core/java/android/hardware/contexthub/HubEndpointSession.java
+++ b/core/java/android/hardware/contexthub/HubEndpointSession.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.chre.flags.Flags;
import android.hardware.location.ContextHubTransaction;
@@ -70,6 +71,7 @@
* receiving the response for the message.
*/
@NonNull
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public ContextHubTransaction<Void> sendMessage(@NonNull HubMessage message) {
if (mIsClosed.get()) {
throw new IllegalStateException("Session is already closed.");
@@ -120,6 +122,7 @@
* <p>When this function is invoked, the messaging associated with this session is invalidated.
* All futures messages targeted for this client are dropped.
*/
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
public void close() {
if (!mIsClosed.getAndSet(true)) {
mCloseGuard.close();
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
index 1c98b4b..4a724ce 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -38,6 +38,7 @@
* @throws IllegalArgumentException If the HubEndpointInfo is not valid.
* @throws IllegalStateException If there are too many opened sessions.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
int openSession(in HubEndpointInfo destination, in @nullable HubServiceInfo serviceInfo);
/**
@@ -48,6 +49,7 @@
*
* @throws IllegalStateException If the session wasn't opened.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void closeSession(int sessionId, int reason);
/**
@@ -59,11 +61,13 @@
*
* @throws IllegalStateException If the session wasn't opened.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void openSessionRequestComplete(int sessionId);
/**
* Unregister this endpoint from the HAL, invalidate the EndpointInfo previously assigned.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void unregister();
/**
@@ -75,6 +79,7 @@
* @param transactionCallback Nullable. If the hub message requires a reply, the transactionCallback
* will be set to non-null.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void sendMessage(int sessionId, in HubMessage message,
in @nullable IContextHubTransactionCallback transactionCallback);
@@ -86,5 +91,6 @@
* @param messageSeqNumber The message sequence number, this should match a previously received HubMessage.
* @param errorCode The message delivery status detail.
*/
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode);
}
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1e66bee..a31b87f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1507,13 +1507,6 @@
mExecutor.execute(mCallback::onStopped);
}
}
-
- @Override // Binder call
- public void onRequestedBrightnessChanged(float brightness) {
- if (mCallback != null) {
- mExecutor.execute(() -> mCallback.onRequestedBrightnessChanged(brightness));
- }
- }
}
/**
diff --git a/core/java/android/hardware/display/IBrightnessListener.aidl b/core/java/android/hardware/display/IBrightnessListener.aidl
new file mode 100644
index 0000000..f5d3743
--- /dev/null
+++ b/core/java/android/hardware/display/IBrightnessListener.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+/**
+ * Interface for notifying the display owner about brightness changes.
+ *
+ * @hide
+ */
+oneway interface IBrightnessListener {
+ /**
+ * Called when the display's brightness has changed.
+ *
+ * @param brightness the new brightness of the display. Value of {@code 0.0} indicates the
+ * minimum supported brightness and value of {@code 1.0} indicates the maximum supported
+ * brightness.
+ */
+ void onBrightnessChanged(float brightness);
+}
diff --git a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
index 9cc0364..c3490d1 100644
--- a/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
+++ b/core/java/android/hardware/display/IVirtualDisplayCallback.aidl
@@ -38,9 +38,4 @@
* of the application to release() the virtual display.
*/
void onStopped();
-
- /**
- * Called when the virtual display's requested brightness has changed.
- */
- void onRequestedBrightnessChanged(float brightness);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 3b573ea..32b6405 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -16,8 +16,6 @@
package android.hardware.display;
import android.annotation.FlaggedApi;
-import android.annotation.FloatRange;
-import android.annotation.SystemApi;
import android.view.Display;
import android.view.Surface;
@@ -166,25 +164,5 @@
* of the application to release() the virtual display.
*/
public void onStopped() { }
-
- /**
- * Called when the requested brightness of the display has changed.
- *
- * <p>The system may adjust the display's brightness based on user or app activity. This
- * callback will only be invoked if the display has an explicitly specified default
- * brightness value.</p>
- *
- * <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
- * {@code 1.0} indicates the maximum supported brightness.</p>
- *
- * @see android.view.View#setKeepScreenOn(boolean)
- * @see android.view.WindowManager.LayoutParams#screenBrightness
- * @see VirtualDisplayConfig.Builder#setDefaultBrightness(float)
- * @hide
- */
- @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
- @SystemApi
- public void onRequestedBrightnessChanged(
- @FloatRange(from = 0.0f, to = 1.0f) float brightness) {}
}
}
diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java
index 57d9d28..eceaa8f 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,11 +18,13 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.hardware.display.DisplayManager.VirtualDisplayFlag;
import android.media.projection.MediaProjection;
@@ -38,6 +40,7 @@
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.Executor;
/**
* Holds configuration used to create {@link VirtualDisplay} instances.
@@ -63,6 +66,7 @@
private final DisplayCutout mDisplayCutout;
private final boolean mIgnoreActivitySizeRestrictions;
private final float mDefaultBrightness;
+ private final IBrightnessListener mBrightnessListener;
private VirtualDisplayConfig(
@NonNull String name,
@@ -79,7 +83,8 @@
boolean isHomeSupported,
@Nullable DisplayCutout displayCutout,
boolean ignoreActivitySizeRestrictions,
- @FloatRange(from = 0.0f, to = 1.0f) float defaultBrightness) {
+ @FloatRange(from = 0.0f, to = 1.0f) float defaultBrightness,
+ IBrightnessListener brightnessListener) {
mName = name;
mWidth = width;
mHeight = height;
@@ -95,6 +100,7 @@
mDisplayCutout = displayCutout;
mIgnoreActivitySizeRestrictions = ignoreActivitySizeRestrictions;
mDefaultBrightness = defaultBrightness;
+ mBrightnessListener = brightnessListener;
}
/**
@@ -167,14 +173,20 @@
* indicates the maximum supported brightness.</p>
*
* @see Builder#setDefaultBrightness(float)
- * @hide
*/
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
- @SystemApi
public @FloatRange(from = 0.0f, to = 1.0f) float getDefaultBrightness() {
return mDefaultBrightness;
}
+ /**
+ * Returns the listener to get notified about changes in the display brightness.
+ * @hide
+ */
+ @Nullable
+ public IBrightnessListener getBrightnessListener() {
+ return mBrightnessListener;
+ }
/**
* Returns the unique identifier for the display. Shouldn't be displayed to the user.
@@ -266,6 +278,7 @@
DisplayCutout.ParcelableWrapper.writeCutoutToParcel(mDisplayCutout, dest, flags);
dest.writeBoolean(mIgnoreActivitySizeRestrictions);
dest.writeFloat(mDefaultBrightness);
+ dest.writeStrongBinder(mBrightnessListener != null ? mBrightnessListener.asBinder() : null);
}
@Override
@@ -294,7 +307,9 @@
&& mIsHomeSupported == that.mIsHomeSupported
&& mIgnoreActivitySizeRestrictions == that.mIgnoreActivitySizeRestrictions
&& Objects.equals(mDisplayCutout, that.mDisplayCutout)
- && mDefaultBrightness == that.mDefaultBrightness;
+ && mDefaultBrightness == that.mDefaultBrightness
+ && Objects.equals(mBrightnessListener, that.mBrightnessListener);
+
}
@Override
@@ -303,7 +318,7 @@
mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
mRequestedRefreshRate, mIsHomeSupported, mDisplayCutout,
- mIgnoreActivitySizeRestrictions, mDefaultBrightness);
+ mIgnoreActivitySizeRestrictions, mDefaultBrightness, mBrightnessListener);
return hashCode;
}
@@ -345,6 +360,43 @@
mDisplayCutout = DisplayCutout.ParcelableWrapper.readCutoutFromParcel(in);
mIgnoreActivitySizeRestrictions = in.readBoolean();
mDefaultBrightness = in.readFloat();
+ mBrightnessListener = IBrightnessListener.Stub.asInterface(in.readStrongBinder());
+
+ }
+
+ /**
+ * Listener for display brightness changes.
+ */
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ public interface BrightnessListener {
+
+ /**
+ * Called when the display's brightness has changed.
+ *
+ * @param brightness the new brightness of the display. Value of {@code 0.0} indicates the
+ * minimum supported brightness and value of {@code 1.0} indicates the maximum supported
+ * brightness.
+ */
+ void onBrightnessChanged(@FloatRange(from = 0.0f, to = 1.0f) float brightness);
+ }
+
+ private static class BrightnessListenerDelegate extends IBrightnessListener.Stub {
+
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final BrightnessListener mListener;
+
+ BrightnessListenerDelegate(@NonNull @CallbackExecutor Executor executor,
+ @NonNull BrightnessListener listener) {
+ mExecutor = executor;
+ mListener = listener;
+ }
+
+ @Override
+ public void onBrightnessChanged(float brightness) {
+ mExecutor.execute(() -> mListener.onBrightnessChanged(brightness));
+ }
}
@NonNull
@@ -380,6 +432,7 @@
private DisplayCutout mDisplayCutout = null;
private boolean mIgnoreActivitySizeRestrictions = false;
private float mDefaultBrightness = 0.0f;
+ private IBrightnessListener mBrightnessListener = null;
/**
* Creates a new Builder.
@@ -575,7 +628,7 @@
* Sets the default brightness of the display.
*
* <p>The system will use this brightness value whenever the display should be bright, i.e.
- * it is powered on and not dimmed due to user activity or app activity.</p>
+ * it is powered on and not modified due to user activity or app activity.</p>
*
* <p>Value of {@code 0.0} indicates the minimum supported brightness and value of
* {@code 1.0} indicates the maximum supported brightness.</p>
@@ -583,12 +636,9 @@
* <p>If unset, defaults to {@code 0.0}</p>
*
* @see android.view.View#setKeepScreenOn(boolean)
- * @see Builder#setDefaultBrightness(float)
- * @see VirtualDisplay.Callback#onRequestedBrightnessChanged(float)
- * @hide
+ * @see #setBrightnessListener(Executor, BrightnessListener)
*/
@FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
- @SystemApi
@NonNull
public Builder setDefaultBrightness(@FloatRange(from = 0.0f, to = 1.0f) float brightness) {
if (brightness < PowerManager.BRIGHTNESS_MIN
@@ -601,6 +651,22 @@
}
/**
+ * Sets the listener to get notified about changes in the display brightness.
+ *
+ * @param executor The executor where the callback is executed on.
+ * @param listener The listener to get notified when the display brightness has changed.
+ */
+ @SuppressLint("MissingGetterMatchingBuilder") // The hidden getter returns the AIDL object
+ @FlaggedApi(android.companion.virtualdevice.flags.Flags.FLAG_DEVICE_AWARE_DISPLAY_POWER)
+ @NonNull
+ public Builder setBrightnessListener(@NonNull @CallbackExecutor Executor executor,
+ @NonNull BrightnessListener listener) {
+ mBrightnessListener = new BrightnessListenerDelegate(
+ Objects.requireNonNull(executor), Objects.requireNonNull(listener));
+ return this;
+ }
+
+ /**
* Builds the {@link VirtualDisplayConfig} instance.
*/
@NonNull
@@ -620,7 +686,8 @@
mIsHomeSupported,
mDisplayCutout,
mIgnoreActivitySizeRestrictions,
- mDefaultBrightness);
+ mDefaultBrightness,
+ mBrightnessListener);
}
}
}
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 114459e..3f9317a 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -27,6 +27,7 @@
import static com.android.hardware.input.Flags.keyboardA11yStickyKeysFlag;
import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
+import static com.android.hardware.input.Flags.touchpadSystemGestureDisable;
import static com.android.hardware.input.Flags.touchpadTapDragging;
import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
import static com.android.hardware.input.Flags.touchpadVisualizer;
@@ -374,6 +375,15 @@
}
/**
+ * Returns true if the feature flag for disabling system gestures on touchpads is enabled.
+ *
+ * @hide
+ */
+ public static boolean isTouchpadSystemGestureDisableFeatureFlagEnabled() {
+ return touchpadSystemGestureDisable();
+ }
+
+ /**
* Returns true if the feature flag for touchpad visualizer is enabled.
*
* @hide
@@ -530,6 +540,40 @@
}
/**
+ * Returns true if system gestures (three- and four-finger swipes) should be enabled for
+ * touchpads.
+ *
+ * @param context The application context.
+ * @return Whether system gestures on touchpads are enabled
+ *
+ * @hide
+ */
+ public static boolean useTouchpadSystemGestures(@NonNull Context context) {
+ if (!isTouchpadSystemGestureDisableFeatureFlagEnabled()) {
+ return true;
+ }
+ return Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_SYSTEM_GESTURES, 1, UserHandle.USER_CURRENT) == 1;
+ }
+
+ /**
+ * Sets whether system gestures are enabled for touchpads.
+ *
+ * @param context The application context.
+ * @param enabled True to enable system gestures.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+ public static void setTouchpadSystemGesturesEnabled(@NonNull Context context, boolean enabled) {
+ if (!isTouchpadSystemGestureDisableFeatureFlagEnabled()) {
+ return;
+ }
+ Settings.System.putIntForUser(context.getContentResolver(),
+ Settings.System.TOUCHPAD_SYSTEM_GESTURES, enabled ? 1 : 0, UserHandle.USER_CURRENT);
+ }
+
+ /**
* Whether a pointer icon will be shown over the location of a stylus pointer.
*
* @hide
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 0c89059..dc4273d 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -149,6 +149,13 @@
}
flag {
+ name: "touchpad_system_gesture_disable"
+ namespace: "input"
+ description: "Adds an accessibility setting to disable system navigation gestures (3- and 4-finger swipes) on touchpads"
+ bug: "353947750"
+}
+
+flag {
name: "enable_customizable_input_gestures"
namespace: "input"
description: "Enables keyboard shortcut customization support"
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 117d8fe..3a74130 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -1267,6 +1267,9 @@
* registration succeeds, the endpoint can receive notifications through the provided callback.
*
* @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder}
+ * @throws IllegalStateException if the registration failed, for example if too many endpoints
+ * are registered at the service
+ * @throws UnsupportedOperationException if endpoint registration is not supported
*/
@RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@FlaggedApi(Flags.FLAG_OFFLOAD_API)
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index 2ba1078..73c8e3e 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -157,7 +157,7 @@
SoundTrigger.RecognitionConfig apiConfig) {
RecognitionConfig aidlConfig = new RecognitionConfig();
aidlConfig.captureRequested = apiConfig.isCaptureRequested();
- // apiConfig.isAllowMultipleTriggers() is ignored by the lower layers.
+ // apiConfig.isMultipleTriggersAllowed() is ignored by the lower layers.
aidlConfig.phraseRecognitionExtras =
new PhraseRecognitionExtra[apiConfig.getKeyphrases().size()];
for (int i = 0; i < apiConfig.getKeyphrases().size(); ++i) {
@@ -178,7 +178,7 @@
}
return new SoundTrigger.RecognitionConfig.Builder()
.setCaptureRequested(aidlConfig.captureRequested)
- .setAllowMultipleTriggers(false)
+ .setMultipleTriggersAllowed(false)
.setKeyphrases(keyphrases)
.setData(Arrays.copyOf(aidlConfig.data, aidlConfig.data.length))
.setAudioCapabilities(aidl2apiAudioCapabilities(aidlConfig.audioCapabilities))
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 7745b03..7c4ddc6 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -1518,7 +1518,7 @@
@FlaggedApi(android.media.soundtrigger.Flags.FLAG_MANAGER_API)
public static final class RecognitionConfig implements Parcelable {
private final boolean mCaptureRequested;
- private final boolean mAllowMultipleTriggers;
+ private final boolean mMultipleTriggersAllowed;
private final KeyphraseRecognitionExtra mKeyphrases[];
private final byte[] mData;
private final @ModuleProperties.AudioCapabilities int mAudioCapabilities;
@@ -1529,7 +1529,7 @@
* {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)}
*
* @param captureRequested Whether the DSP should capture the trigger sound.
- * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+ * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
* triggers.
* @param keyphrases List of keyphrases in the sound model.
* @param data Opaque data for use by system applications who know about voice engine
@@ -1537,11 +1537,11 @@
* @param audioCapabilities Bit field encoding of the AudioCapabilities. See
* {@link ModuleProperties.AudioCapabilities} for details.
*/
- private RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+ private RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
@ModuleProperties.AudioCapabilities int audioCapabilities) {
this.mCaptureRequested = captureRequested;
- this.mAllowMultipleTriggers = allowMultipleTriggers;
+ this.mMultipleTriggersAllowed = multipleTriggersAllowed;
this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
this.mData = data != null ? data : new byte[0];
this.mAudioCapabilities = audioCapabilities;
@@ -1553,7 +1553,7 @@
*
* @deprecated Use {@link Builder} instead.
* @param captureRequested Whether the DSP should capture the trigger sound.
- * @param allowMultipleTriggers Whether the service should restart listening after the DSP
+ * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
* triggers.
* @param keyphrases List of keyphrases in the sound model.
* @param data Opaque data for use by system applications.
@@ -1563,9 +1563,9 @@
@UnsupportedAppUsage
@Deprecated
@TestApi
- public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
+ public RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
@Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
- this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
+ this(captureRequested, multipleTriggersAllowed, keyphrases, data, 0);
}
public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
@@ -1593,8 +1593,8 @@
* <p><b>Note:</b> This config flag is currently used at the service layer rather than by
* the DSP.
*/
- public boolean isAllowMultipleTriggers() {
- return mAllowMultipleTriggers;
+ public boolean isMultipleTriggersAllowed() {
+ return mMultipleTriggersAllowed;
}
/**
@@ -1627,19 +1627,19 @@
private static RecognitionConfig fromParcel(Parcel in) {
boolean captureRequested = in.readBoolean();
- boolean allowMultipleTriggers = in.readBoolean();
+ boolean multipleTriggersAllowed = in.readBoolean();
KeyphraseRecognitionExtra[] keyphrases =
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
byte[] data = in.createByteArray();
int audioCapabilities = in.readInt();
- return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
- audioCapabilities);
+ return new RecognitionConfig(captureRequested, multipleTriggersAllowed, keyphrases,
+ data, audioCapabilities);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mCaptureRequested);
- dest.writeBoolean(mAllowMultipleTriggers);
+ dest.writeBoolean(mMultipleTriggersAllowed);
dest.writeTypedArray(mKeyphrases, flags);
dest.writeByteArray(mData);
dest.writeInt(mAudioCapabilities);
@@ -1653,7 +1653,7 @@
@Override
public String toString() {
return "RecognitionConfig [captureRequested=" + mCaptureRequested
- + ", allowMultipleTriggers=" + mAllowMultipleTriggers + ", keyphrases="
+ + ", multipleTriggersAllowed=" + mMultipleTriggersAllowed + ", keyphrases="
+ Arrays.toString(mKeyphrases) + ", data=" + Arrays.toString(mData)
+ ", audioCapabilities=" + Integer.toHexString(mAudioCapabilities) + "]";
}
@@ -1670,7 +1670,7 @@
if (mCaptureRequested != other.mCaptureRequested) {
return false;
}
- if (mAllowMultipleTriggers != other.mAllowMultipleTriggers) {
+ if (mMultipleTriggersAllowed != other.mMultipleTriggersAllowed) {
return false;
}
if (!Arrays.equals(mKeyphrases, other.mKeyphrases)) {
@@ -1690,7 +1690,7 @@
final int prime = 31;
int result = 1;
result = prime * result + (mCaptureRequested ? 1 : 0);
- result = prime * result + (mAllowMultipleTriggers ? 1 : 0);
+ result = prime * result + (mMultipleTriggersAllowed ? 1 : 0);
result = prime * result + Arrays.hashCode(mKeyphrases);
result = prime * result + Arrays.hashCode(mData);
result = prime * result + mAudioCapabilities;
@@ -1702,7 +1702,7 @@
*/
public static final class Builder {
private boolean mCaptureRequested;
- private boolean mAllowMultipleTriggers;
+ private boolean mMultipleTriggersAllowed;
@Nullable private KeyphraseRecognitionExtra[] mKeyphrases;
@Nullable private byte[] mData;
private @ModuleProperties.AudioCapabilities int mAudioCapabilities;
@@ -1725,12 +1725,12 @@
/**
* Sets allow multiple triggers state.
- * @param allowMultipleTriggers Whether the service should restart listening after the
+ * @param multipleTriggersAllowed Whether the service should restart listening after the
* DSP triggers.
* @return the same Builder instance.
*/
- public @NonNull Builder setAllowMultipleTriggers(boolean allowMultipleTriggers) {
- mAllowMultipleTriggers = allowMultipleTriggers;
+ public @NonNull Builder setMultipleTriggersAllowed(boolean multipleTriggersAllowed) {
+ mMultipleTriggersAllowed = multipleTriggersAllowed;
return this;
}
@@ -1779,7 +1779,7 @@
public @NonNull RecognitionConfig build() {
RecognitionConfig config = new RecognitionConfig(
/* captureRequested= */ mCaptureRequested,
- /* allowMultipleTriggers= */ mAllowMultipleTriggers,
+ /* multipleTriggersAllowed= */ mMultipleTriggersAllowed,
/* keyphrases= */ mKeyphrases,
/* data= */ mData,
/* audioCapabilities= */ mAudioCapabilities);
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index a86396c..0fedf8e 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -65,7 +65,7 @@
}
/**
- * Creates a AF_LOCAL/UNIX domain stream socket with given socket type
+ * Creates a AF_LOCAL/UNIX domain socket with the given socket type.
*
* @param sockType either {@link #SOCKET_DGRAM}, {@link #SOCKET_STREAM}
* or {@link #SOCKET_SEQPACKET}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index c0398ce..ded9415 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -23,18 +23,19 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.getMatchCriteriaString;
+import static android.net.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
+import static android.net.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
+import static android.net.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static android.net.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -44,7 +45,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index a27e923..0d0efb2 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -18,10 +18,10 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
+import static android.net.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -29,6 +29,7 @@
import android.content.Context;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
@@ -37,7 +38,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index b270062..067144e 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -32,12 +32,12 @@
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index c7b2f18..770a8c1 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -17,22 +17,22 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
+import static android.net.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static android.net.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.net.NetworkCapabilities;
import android.net.vcn.VcnUnderlyingNetworkTemplate.MatchCriteria;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
index ce5ec75..48c1b25 100644
--- a/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/ChildSaProposalUtils.java
@@ -20,10 +20,10 @@
import android.annotation.NonNull;
import android.net.ipsec.ike.ChildSaProposal;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.util.List;
import java.util.Objects;
diff --git a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
index 853a52d..dc1ee36 100644
--- a/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtils.java
@@ -28,10 +28,10 @@
import android.net.eap.EapSessionConfig.EapSimConfig;
import android.net.eap.EapSessionConfig.EapTtlsConfig;
import android.net.eap.EapSessionConfig.EapUiccConfig;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
index 6acb34e..6e8616f 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtils.java
@@ -27,10 +27,10 @@
import android.net.ipsec.ike.IkeIpv6AddrIdentification;
import android.net.ipsec.ike.IkeKeyIdIdentification;
import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
index 1459671..b590148 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSaProposalUtils.java
@@ -20,10 +20,10 @@
import android.annotation.NonNull;
import android.net.ipsec.ike.IkeSaProposal;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.util.List;
import java.util.Objects;
diff --git a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
index d1531a1..aefac2e8 100644
--- a/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtils.java
@@ -35,12 +35,12 @@
import android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
import android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
import android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.net.InetAddress;
import java.security.PrivateKey;
diff --git a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
index 0c9ee84..469966a 100644
--- a/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
+++ b/core/java/android/net/vcn/persistablebundleutils/SaProposalUtilsBase.java
@@ -18,11 +18,10 @@
import android.annotation.NonNull;
import android.net.ipsec.ike.SaProposal;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.util.Pair;
-import com.android.server.vcn.util.PersistableBundleUtils;
-
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
index e62acac..3f4ba34 100644
--- a/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtils.java
@@ -34,11 +34,11 @@
import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6Address;
import android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer;
import android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.PersistableBundle;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.vcn.util.PersistableBundleUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
diff --git a/services/core/java/com/android/server/vcn/util/LogUtils.java b/core/java/android/net/vcn/util/LogUtils.java
similarity index 96%
rename from services/core/java/com/android/server/vcn/util/LogUtils.java
rename to core/java/android/net/vcn/util/LogUtils.java
index 93728ce..7f7f852 100644
--- a/services/core/java/com/android/server/vcn/util/LogUtils.java
+++ b/core/java/android/net/vcn/util/LogUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
import android.annotation.Nullable;
import android.os.ParcelUuid;
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/core/java/android/net/vcn/util/MtuUtils.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/util/MtuUtils.java
rename to core/java/android/net/vcn/util/MtuUtils.java
index 356c71f..c3123bc 100644
--- a/services/core/java/com/android/server/vcn/util/MtuUtils.java
+++ b/core/java/android/net/vcn/util/MtuUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_3DES;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
diff --git a/services/core/java/com/android/server/vcn/util/OneWayBoolean.java b/core/java/android/net/vcn/util/OneWayBoolean.java
similarity index 96%
rename from services/core/java/com/android/server/vcn/util/OneWayBoolean.java
rename to core/java/android/net/vcn/util/OneWayBoolean.java
index e79bb2d..a7ef67b 100644
--- a/services/core/java/com/android/server/vcn/util/OneWayBoolean.java
+++ b/core/java/android/net/vcn/util/OneWayBoolean.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
/**
* OneWayBoolean is an abstraction for a boolean that MUST only ever be flipped from false to true
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/core/java/android/net/vcn/util/PersistableBundleUtils.java
similarity index 99%
rename from services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
rename to core/java/android/net/vcn/util/PersistableBundleUtils.java
index d6761a2..4dc42c7 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/core/java/android/net/vcn/util/PersistableBundleUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 9b39c62..8b6da7e 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -41,6 +41,8 @@
import android.util.Slog;
import android.view.View;
+import com.android.internal.util.FrameworkStatsLog;
+
import dalvik.system.VMRuntime;
import java.lang.annotation.Retention;
@@ -1666,11 +1668,14 @@
*/
@FlaggedApi(android.os.Flags.FLAG_API_FOR_BACKPORTED_FIXES)
public static @BackportedFixStatus int getBackportedFixStatus(long id) {
- if (id <= 0 || id > 1023) {
- return BACKPORTED_FIX_STATUS_UNKNOWN;
+ @BackportedFixStatus int status = BACKPORTED_FIX_STATUS_UNKNOWN;
+ int uid = Binder.getCallingUid();
+ if (id > 0 && id <= 1023) {
+ status = isBitSet(BackportedFixesProperties.alias_bitset(), (int) id)
+ ? BACKPORTED_FIX_STATUS_FIXED : BACKPORTED_FIX_STATUS_UNKNOWN;
}
- return isBitSet(BackportedFixesProperties.alias_bitset(), (int) id)
- ? BACKPORTED_FIX_STATUS_FIXED : BACKPORTED_FIX_STATUS_UNKNOWN;
+ FrameworkStatsLog.write(FrameworkStatsLog.BACKPORTED_FIX_STATUS_REPORTED, uid, id, status);
+ return status;
}
private static boolean isBitSet(List<Long> bitsetLongArray, int bitIndex) {
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 4769681..23114c4 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -30,6 +30,8 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
import dalvik.annotation.optimization.NeverCompile;
import java.io.FileDescriptor;
@@ -116,41 +118,60 @@
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
- if (sIsProcessAllowedToUseConcurrent == null) {
- // Concurrent mode modifies behavior that is observable via reflection and is commonly
- // used by tests.
- // For now, we limit it to system processes to avoid breaking apps and their tests.
- boolean useConcurrent = UserHandle.isCore(Process.myUid());
-
- // Some platform tests run in system UIDs.
- // Use this awful heuristic to detect them.
- if (useConcurrent) {
- final String processName = Process.myProcessName();
- if (processName == null
- || processName.contains("test")
- || processName.contains("Test")) {
- useConcurrent = false;
- }
- }
-
- // We can lift this restriction in the future after we've made it possible for test
- // authors to test Looper and MessageQueue without resorting to reflection.
-
- // Holdback study.
- if (useConcurrent && Flags.messageQueueForceLegacy()) {
- useConcurrent = false;
- }
-
- sIsProcessAllowedToUseConcurrent = useConcurrent;
- mUseConcurrent = useConcurrent;
- } else {
- mUseConcurrent = sIsProcessAllowedToUseConcurrent;
- }
-
+ initIsProcessAllowedToUseConcurrent();
+ mUseConcurrent = sIsProcessAllowedToUseConcurrent;
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
+ private static void initIsProcessAllowedToUseConcurrent() {
+ if (sIsProcessAllowedToUseConcurrent != null) {
+ return;
+ }
+
+ if (RavenwoodEnvironment.getInstance().isRunningOnRavenwood()) {
+ sIsProcessAllowedToUseConcurrent = false;
+ return;
+ }
+
+ final String processName = Process.myProcessName();
+ if (processName == null) {
+ // Assume that this is a host-side test and avoid concurrent mode for now.
+ sIsProcessAllowedToUseConcurrent = false;
+ return;
+ }
+
+ // Concurrent mode modifies behavior that is observable via reflection and is commonly
+ // used by tests.
+ // For now, we limit it to system processes to avoid breaking apps and their tests.
+ sIsProcessAllowedToUseConcurrent = UserHandle.isCore(Process.myUid());
+
+ if (sIsProcessAllowedToUseConcurrent) {
+ // Some platform tests run in core UIDs.
+ // Use this awful heuristic to detect them.
+ if (processName.contains("test") || processName.contains("Test")) {
+ sIsProcessAllowedToUseConcurrent = false;
+ }
+ } else {
+ // Also explicitly allow SystemUI processes.
+ // SystemUI doesn't run in a core UID, but we want to give it the performance boost,
+ // and we know that it's safe to use the concurrent implementation in SystemUI.
+ sIsProcessAllowedToUseConcurrent =
+ processName.equals("com.android.systemui")
+ || processName.startsWith("com.android.systemui:");
+ // On Android distributions where SystemUI has a different process name,
+ // the above condition may need to be adjusted accordingly.
+ }
+
+ // We can lift these restrictions in the future after we've made it possible for test
+ // authors to test Looper and MessageQueue without resorting to reflection.
+
+ // Holdback study.
+ if (sIsProcessAllowedToUseConcurrent && Flags.messageQueueForceLegacy()) {
+ sIsProcessAllowedToUseConcurrent = false;
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
index 8e78b7e..072c012 100644
--- a/core/java/android/os/CpuHeadroomParams.java
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -56,15 +56,9 @@
*/
public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
- /**
- * Minimum CPU headroom calculation window size.
- */
- public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
-
- /**
- * Maximum CPU headroom calculation window size.
- */
- public static final int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+ private static final int CALCULATION_WINDOW_MILLIS_MIN = 50;
+ private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000;
+ private static final int MAX_TID_COUNT = 5;
/**
* Sets the headroom calculation type.
@@ -99,20 +93,18 @@
* Sets the headroom calculation window size in milliseconds.
* <p>
*
- * @param windowMillis the window size in milliseconds, ranged from
- * [{@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN},
- * {@link #CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller
- * the value, the larger fluctuation in value should be expected. The
- * default value can be retrieved from the
+ * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the
+ * window size, the larger fluctuation in the headroom value should be
+ * expected. The default value can be retrieved from the
* {@link #getCalculationWindowMillis}. The device will try to use the
* closest feasible window size to this param.
* @throws IllegalArgumentException if the window size is not in allowed range.
*/
public void setCalculationWindowMillis(
- @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
- CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
- if (windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN
- || windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) {
+ @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
+ CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
+ if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN
+ || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) {
throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
}
mInternal.calculationWindowMillis = windowMillis;
@@ -121,10 +113,10 @@
/**
* Gets the headroom calculation window size in milliseconds.
* <p>
- * This will return the default value chosen by the device if not set.
+ * This will return the default value chosen by the device if the params is not set.
*/
- public @IntRange(from = CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
- CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() {
+ public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
+ CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() {
return mInternal.calculationWindowMillis;
}
@@ -141,7 +133,7 @@
* positive.
*/
public void setTids(@NonNull int... tids) {
- if (tids.length == 0 || tids.length > 5) {
+ if (tids.length == 0 || tids.length > MAX_TID_COUNT) {
throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length);
}
for (int tid : tids) {
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
index 4dc9826..126ee8c 100644
--- a/core/java/android/os/GpuHeadroomParams.java
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -54,15 +54,8 @@
*/
public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
- /**
- * Minimum GPU headroom calculation window size.
- */
- public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
-
- /**
- * Maximum GPU headroom calculation window size.
- */
- public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+ private static final int CALCULATION_WINDOW_MILLIS_MIN = 50;
+ private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000;
/**
* Sets the headroom calculation type.
@@ -82,7 +75,7 @@
/**
* Gets the headroom calculation type.
- * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+ * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if the params is not set.
*/
public @GpuHeadroomCalculationType int getCalculationType() {
@GpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
@@ -97,20 +90,18 @@
* Sets the headroom calculation window size in milliseconds.
* <p>
*
- * @param windowMillis the window size in milliseconds, ranged from
- * [{@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN},
- * {@link #GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX}]. The smaller
- * the value, the larger fluctuation in value should be expected. The
- * default value can be retrieved from the
- * {@link #getCalculationWindowMillis}. If the device will try to use the
+ * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the
+ * window size, the larger fluctuation in the headroom value should be
+ * expected. The default value can be retrieved from the
+ * {@link #getCalculationWindowMillis}. The device will try to use the
* closest feasible window size to this param.
* @throws IllegalArgumentException if the window is invalid.
*/
public void setCalculationWindowMillis(
- @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
- GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
- if (windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN
- || windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) {
+ @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
+ CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
+ if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN
+ || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) {
throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
}
mInternal.calculationWindowMillis = windowMillis;
@@ -121,8 +112,8 @@
* <p>
* This will return the default value chosen by the device if not set.
*/
- public @IntRange(from = GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN, to =
- GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() {
+ public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
+ CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() {
return mInternal.calculationWindowMillis;
}
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index f1936b5..4a14a8d 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -64,4 +64,10 @@
* Get Maximum number of graphics pipeline threads allowed per-app.
*/
int getMaxGraphicsPipelineThreadsCount();
+
+ /**
+ * Used by the JNI to pass an interface to the SessionManager;
+ * for internal use only.
+ */
+ oneway void passSessionManagerBinder(in IBinder sessionManager);
}
diff --git a/core/java/android/os/IHintSession.aidl b/core/java/android/os/IHintSession.aidl
index 6fd4f3c..e3f899d 100644
--- a/core/java/android/os/IHintSession.aidl
+++ b/core/java/android/os/IHintSession.aidl
@@ -27,4 +27,9 @@
void sendHint(int hint);
void setMode(int mode, boolean enabled);
void reportActualWorkDuration2(in WorkDuration[] workDurations);
+
+ /**
+ * Used by apps to associate a session to a given set of layers
+ */
+ oneway void associateToLayers(in IBinder[] layerTokens);
}
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index f9789c1..bfcc5cc 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -93,10 +93,9 @@
per-file GpuHeadroom*.aidl = file:/ADPF_OWNERS
per-file CpuHeadroom*.java = file:/ADPF_OWNERS
per-file GpuHeadroom*.java = file:/ADPF_OWNERS
-per-file PerformanceHintManager.java = file:/ADPF_OWNERS
per-file WorkDuration.java = file:/ADPF_OWNERS
-per-file IHintManager.aidl = file:/ADPF_OWNERS
-per-file IHintSession.aidl = file:/ADPF_OWNERS
+per-file *Hint* = file:/ADPF_OWNERS
+per-file *Session* = file:/ADPF_OWNERS
# IThermal interfaces
per-file IThermal* = file:/THERMAL_OWNERS
diff --git a/core/java/android/os/SessionCreationConfig.aidl b/core/java/android/os/SessionCreationConfig.aidl
index cdc0ef4..17147e4 100644
--- a/core/java/android/os/SessionCreationConfig.aidl
+++ b/core/java/android/os/SessionCreationConfig.aidl
@@ -36,4 +36,12 @@
* List of the modes to be enabled upon session creation.
*/
SessionMode[] modesToEnable;
+
+ /**
+ * List of layers to attach this session to.
+ *
+ * Note: DO NOT STORE THESE IN HintSessionManager, as
+ * it will break the layer lifecycle.
+ */
+ IBinder[] layerTokens;
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a1ede5f..7e73a5d 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3846,7 +3846,7 @@
}
/**
- * Return the time when the context user was unlocked elapsed milliseconds since boot,
+ * Return the time when the calling user was unlocked elapsed milliseconds since boot,
* or 0 if not unlocked.
*
* @hide
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index a719583..d318b62 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -445,3 +445,30 @@
description: "Enable the Wallet role within profiles"
bug: "356107987"
}
+
+flag {
+ name: "text_classifier_choice_api_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "API change to enable getTextClassifier by type"
+ bug: "377229653"
+}
+
+flag {
+ name: "updatable_text_classifier_for_otp_detection_enabled"
+ is_fixed_read_only: true
+ is_exported: true
+ namespace: "permissions"
+ description: "Enables text classifier for OTP detection that is updatable from mainline module"
+ bug: "377229653"
+}
+
+flag {
+ name: "cross_user_role_platform_api_enabled"
+ is_exported: true
+ is_fixed_read_only: true
+ namespace: "permissions"
+ description: "Enable cross-user roles platform API"
+ bug: "367732307"
+}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index f6bdc18..e4a3c9f 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -16,6 +16,8 @@
package android.preference;
+import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
+
import android.animation.LayoutTransition;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -54,6 +56,8 @@
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.util.XmlUtils;
@@ -209,6 +213,8 @@
private int mPreferenceHeaderItemResId = 0;
private boolean mPreferenceHeaderRemoveEmptyIcon = false;
+ private final OnBackInvokedCallback mOnBackInvokedCallback = this::onBackInvoked;
+
/**
* The starting request code given out to preference framework.
*/
@@ -699,10 +705,26 @@
skipButton.setVisibility(View.VISIBLE);
}
}
+ updateBackCallbackRegistrationState();
}
@Override
public void onBackPressed() {
+ onBackInvoked();
+ }
+
+ private void updateBackCallbackRegistrationState() {
+ if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)) return;
+ if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
+ && getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
+ getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(PRIORITY_DEFAULT, mOnBackInvokedCallback);
+ } else {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ }
+ }
+
+ private void onBackInvoked() {
if (mCurHeader != null && mSinglePane && getFragmentManager().getBackStackEntryCount() == 0
&& getIntent().getStringExtra(EXTRA_SHOW_FRAGMENT) == null) {
mCurHeader = null;
@@ -713,9 +735,10 @@
showBreadCrumbs(mActivityTitle, null);
}
getListView().clearChoices();
- } else {
+ } else if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(this)) {
super.onBackPressed();
}
+ updateBackCallbackRegistrationState();
}
/**
@@ -1221,6 +1244,7 @@
getListView().clearChoices();
}
showBreadCrumbs(header);
+ updateBackCallbackRegistrationState();
}
void showBreadCrumbs(Header header) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8d054f4..0ae9ffa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -85,6 +85,7 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.service.voice.VisualQueryDetectedResult;
import android.speech.tts.TextToSpeech;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -6289,6 +6290,13 @@
public static final String TOUCHPAD_RIGHT_CLICK_ZONE = "touchpad_right_click_zone";
/**
+ * Whether to enable system gestures (three- and four-finger swipes) on touchpads.
+ *
+ * @hide
+ */
+ public static final String TOUCHPAD_SYSTEM_GESTURES = "touchpad_system_gestures";
+
+ /**
* Whether to enable reversed vertical scrolling for connected mice.
*
* When enabled, scrolling down on the mouse wheel will move the screen up and vice versa.
@@ -6549,6 +6557,7 @@
PRIVATE_SETTINGS.add(TOUCHPAD_TAP_TO_CLICK);
PRIVATE_SETTINGS.add(TOUCHPAD_TAP_DRAGGING);
PRIVATE_SETTINGS.add(TOUCHPAD_RIGHT_CLICK_ZONE);
+ PRIVATE_SETTINGS.add(TOUCHPAD_SYSTEM_GESTURES);
PRIVATE_SETTINGS.add(CAMERA_FLASH_NOTIFICATION);
PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION);
PRIVATE_SETTINGS.add(SCREEN_FLASH_NOTIFICATION_COLOR);
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 0302faf..59628e8 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -16,7 +16,10 @@
package android.security.advancedprotection;
+import static android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.UserManager.DISALLOW_CELLULAR_2G;
+import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY;
import android.Manifest;
import android.annotation.CallbackExecutor;
@@ -343,6 +346,28 @@
return intent;
}
+ /** @hide */
+ public @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction(
+ @NonNull String identifier, @Nullable @SupportDialogType String type) {
+ Objects.requireNonNull(identifier);
+ if (type != null && !ALL_SUPPORT_DIALOG_TYPES.contains(type)) {
+ throw new IllegalArgumentException(type + " is not a valid type. See"
+ + " SUPPORT_DIALOG_TYPE_* APIs.");
+ }
+ final String featureId;
+ if (DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY.equals(identifier)) {
+ featureId = FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+ } else if (DISALLOW_CELLULAR_2G.equals(identifier)) {
+ featureId = FEATURE_ID_DISALLOW_CELLULAR_2G;
+ } else if (android.app.admin.flags.Flags.setMtePolicyCoexistence() && MEMORY_TAGGING_POLICY
+ .equals(identifier)) {
+ featureId = FEATURE_ID_ENABLE_MTE;
+ } else {
+ throw new UnsupportedOperationException("Unsupported identifier: " + identifier);
+ }
+ return createSupportIntent(featureId, type);
+ }
+
/**
* A callback class for monitoring changes to Advanced Protection state
*
diff --git a/core/java/android/security/advancedprotection/OWNERS b/core/java/android/security/advancedprotection/OWNERS
index ddac8ed..bfb7e16 100644
--- a/core/java/android/security/advancedprotection/OWNERS
+++ b/core/java/android/security/advancedprotection/OWNERS
@@ -2,7 +2,6 @@
achim@google.com
azharaa@google.com
-cpinelli@google.com
eranm@google.com
hanikazmi@google.com
haok@google.com
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index 2007a5f..6228bc9 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -104,6 +104,13 @@
}
flag {
+ name: "prevent_intent_redirect_show_toast_if_nested_keys_not_collected_r_w"
+ namespace: "responsible_apis"
+ description: "Prevent intent redirect attacks by showing a toast if not yet collected"
+ bug: "361143368"
+}
+
+flag {
name: "prevent_intent_redirect_throw_exception_if_nested_keys_not_collected"
namespace: "responsible_apis"
description: "Prevent intent redirect attacks by throwing exception if the intent does not collect nested keys"
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 7d79fd3..68fd115 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -1541,7 +1541,7 @@
mInternalCallback,
new RecognitionConfig.Builder()
.setCaptureRequested(captureTriggerAudio)
- .setAllowMultipleTriggers(allowMultipleTriggers)
+ .setMultipleTriggersAllowed(allowMultipleTriggers)
.setKeyphrases(recognitionExtra)
.setData(data)
.setAudioCapabilities(audioCapabilities)
diff --git a/core/java/android/service/voice/OWNERS b/core/java/android/service/voice/OWNERS
index 5f9f6bd..b6f0270 100644
--- a/core/java/android/service/voice/OWNERS
+++ b/core/java/android/service/voice/OWNERS
@@ -1,6 +1,6 @@
# Bug component: 533220
-
include /core/java/android/app/assist/OWNERS
+atneya@google.com
# The owner here should not be assist owner
adudani@google.com
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index 2c585e6..e8b32ce 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -658,12 +658,15 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED = 42;
/**
* Event for listening to changes in carrier roaming non-terrestrial network eligibility.
*
- * @see CarrierRoamingNtnModeListener
+ * @see CarrierRoamingNtnModeListener#onCarrierRoamingNtnEligibleStateChanged(boolean)
*
* Device is eligible for satellite communication if all the following conditions are met:
* <ul>
@@ -679,11 +682,15 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED = 43;
/**
* Event for listening to changes in carrier roaming non-terrestrial network available services
- * via callback onCarrierRoamingNtnAvailableServicesChanged().
+ * via callback {@link
+ * CarrierRoamingNtnModeListener#onCarrierRoamingNtnAvailableServicesChanged(List)}
* This callback is triggered when the available services provided by the carrier roaming
* satellite changes. The carrier roaming satellite is defined by the following conditions.
* <ul>
@@ -693,15 +700,22 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED = 44;
/**
* Event for listening to carrier roaming non-terrestrial network signal strength changes.
*
- * @see CarrierRoamingNtnModeListener
+ * @see CarrierRoamingNtnModeListener#onCarrierRoamingNtnSignalStrengthChanged(
+ * NtnSignalStrength)
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
public static final int EVENT_CARRIER_ROAMING_NTN_SIGNAL_STRENGTH_CHANGED = 45;
/**
@@ -1803,6 +1817,8 @@
*
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface CarrierRoamingNtnModeListener {
/**
* Callback invoked when carrier roaming non-terrestrial network mode changes.
@@ -1836,10 +1852,10 @@
* Callback invoked when carrier roaming non-terrestrial network available
* service changes.
*
- * @param availableServices The list of the supported services.
+ * @param availableServices array of supported services.
*/
default void onCarrierRoamingNtnAvailableServicesChanged(
- @NetworkRegistrationInfo.ServiceType List<Integer> availableServices) {}
+ @NonNull @NetworkRegistrationInfo.ServiceType int[] availableServices) {}
/**
* Callback invoked when carrier roaming non-terrestrial network signal strength changes.
@@ -2343,10 +2359,8 @@
(CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
if (listener == null) return;
- List<Integer> ServiceList = Arrays.stream(availableServices).boxed()
- .collect(Collectors.toList());
Binder.withCleanCallingIdentity(() -> mExecutor.execute(
- () -> listener.onCarrierRoamingNtnAvailableServicesChanged(ServiceList)));
+ () -> listener.onCarrierRoamingNtnAvailableServicesChanged(availableServices)));
}
public void onCarrierRoamingNtnSignalStrengthChanged(
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 1dd9d46..f8737a5 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -75,7 +75,7 @@
@android.ravenwood.annotation.RavenwoodClassLoadHook(
"com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded")
// Uncomment the following annotation to switch to the Java substitution version.
-@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_host")
+@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_ravenwood")
public final class Log {
/** @hide */
@IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE})
diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl
index 45dbe43..21b969c 100644
--- a/core/java/android/view/IDisplayWindowInsetsController.aidl
+++ b/core/java/android/view/IDisplayWindowInsetsController.aidl
@@ -60,5 +60,5 @@
* Reports the requested IME visibility of the IME input target to
* the IDisplayWindowInsetsController
*/
- void setImeInputTargetRequestedVisibility(boolean visible);
+ void setImeInputTargetRequestedVisibility(boolean visible, in ImeTracker.Token statsToken);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e5be531..6d85e75 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -62,6 +62,7 @@
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.IInputFilter;
+import android.view.inputmethod.ImeTracker;
import android.view.AppTransitionAnimationSpec;
import android.view.WindowContentFrameStats;
import android.view.WindowManager;
@@ -765,7 +766,8 @@
* container.
*/
@EnforcePermission("MANAGE_APP_TOKENS")
- void updateDisplayWindowRequestedVisibleTypes(int displayId, int requestedVisibleTypes);
+ void updateDisplayWindowRequestedVisibleTypes(int displayId, int requestedVisibleTypes,
+ in @nullable ImeTracker.Token statsToken);
/**
* Called to get the expected window insets.
diff --git a/core/java/android/view/LetterboxScrollProcessor.java b/core/java/android/view/LetterboxScrollProcessor.java
index 1364a82..8c1b0f8 100644
--- a/core/java/android/view/LetterboxScrollProcessor.java
+++ b/core/java/android/view/LetterboxScrollProcessor.java
@@ -78,6 +78,11 @@
@Nullable
@VisibleForTesting(visibility = PACKAGE)
public List<MotionEvent> processMotionEvent(@NonNull MotionEvent motionEvent) {
+ if (!motionEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) {
+ // This is a non-pointer event that doesn't correspond to any location on the screen.
+ // Ignore it.
+ return null;
+ }
mProcessedEvents.clear();
final Rect appBounds = getAppBounds();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a0feccd..3133020 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,7 +16,9 @@
package android.view;
+import static android.adpf.Flags.adpfViewrootimplActionDownBoost;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.content.pm.ActivityInfo.OVERRIDE_SANDBOX_VIEW_BOUNDS_APIS;
import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
@@ -1208,6 +1210,8 @@
private long mRenderThreadDrawStartTimeNs = -1;
private long mFirstFramePresentedTimeNs = -1;
+ private final boolean mSendPerfHintOnTouch;
+
private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue;
private static boolean sToolkitMetricsForFrameRateDecisionFlagValue;
@@ -1337,6 +1341,8 @@
CompatChanges.isChangeEnabled(DISABLE_DRAW_WAKE_LOCK) && disableDrawWakeLock();
mIsSubscribeGranularDisplayEventsEnabled =
com.android.server.display.feature.flags.Flags.subscribeGranularDisplayEvents();
+
+ mSendPerfHintOnTouch = adpfViewrootimplActionDownBoost();
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -2647,7 +2653,8 @@
mStopped = stopped;
final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer;
if (renderer != null) {
- if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+ if (DEBUG_DRAW)
+ Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
renderer.setStopped(mStopped);
}
if (!mStopped) {
@@ -7110,6 +7117,10 @@
+ "touch mode is " + mAttachInfo.mInTouchMode);
if (mAttachInfo.mInTouchMode == inTouchMode) return false;
+ if (inTouchMode && mAttachInfo.mThreadedRenderer != null && mSendPerfHintOnTouch) {
+ mAttachInfo.mThreadedRenderer.notifyExpensiveFrame();
+ }
+
// tell the window manager
try {
IWindowManager windowManager = WindowManagerGlobal.getWindowManagerService();
@@ -7968,8 +7979,9 @@
}
private boolean moveFocusToAdjacentWindow(@FocusDirection int direction) {
- if (getConfiguration().windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_MULTI_WINDOW) {
+ final int windowingMode = getConfiguration().windowConfiguration.getWindowingMode();
+ if (!(windowingMode == WINDOWING_MODE_MULTI_WINDOW
+ || windowingMode == WINDOWING_MODE_FREEFORM)) {
return false;
}
try {
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index 905f350..d527007 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -603,7 +603,7 @@
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_ENABLE_RELAYOUT,
- false);
+ true);
}
/** @hide */
@@ -611,7 +611,7 @@
return DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_AUTOFILL,
DEVICE_CONFIG_ENABLE_RELATIVE_LOCATION_FOR_RELAYOUT,
- false);
+ true);
}
/** @hide **/
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 52c5af8..1de0474 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.relayoutFix;
import static android.view.ContentInfo.SOURCE_AUTOFILL;
import static android.view.autofill.Helper.sDebug;
import static android.view.autofill.Helper.sVerbose;
@@ -1013,7 +1014,7 @@
AutofillFeatureFlags.shouldIncludeInvisibleViewInAssistStructure();
mRelayoutFixDeprecated = AutofillFeatureFlags.shouldIgnoreRelayoutWhenAuthPending();
- mRelayoutFix = AutofillFeatureFlags.enableRelayoutFixes();
+ mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
}
diff --git a/core/java/android/view/flags/scroll_capture.aconfig b/core/java/android/view/flags/scroll_capture.aconfig
index fdf9c1e..9080b16 100644
--- a/core/java/android/view/flags/scroll_capture.aconfig
+++ b/core/java/android/view/flags/scroll_capture.aconfig
@@ -3,7 +3,7 @@
flag {
name: "scroll_capture_target_z_order_fix"
- namespace: "system_ui"
+ namespace: "systemui"
description: "Always prefer targets with higher z-order"
bug: "365969802"
metadata {
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index dd32d57..4d354e0 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -222,6 +222,8 @@
PHASE_CLIENT_ALREADY_HIDDEN,
PHASE_CLIENT_VIEW_HANDLER_AVAILABLE,
PHASE_SERVER_UPDATE_CLIENT_VISIBILITY,
+ PHASE_WM_DISPLAY_IME_CONTROLLER_SET_IME_REQUESTED_VISIBLE,
+ PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES,
})
@Retention(RetentionPolicy.SOURCE)
@interface Phase {}
@@ -436,6 +438,12 @@
* app or the RemoteInsetsControlTarget).
*/
int PHASE_SERVER_UPDATE_CLIENT_VISIBILITY = ImeProtoEnums.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY;
+ /** DisplayImeController received the requested visibility for the IME and stored it. */
+ int PHASE_WM_DISPLAY_IME_CONTROLLER_SET_IME_REQUESTED_VISIBLE =
+ ImeProtoEnums.PHASE_WM_DISPLAY_IME_CONTROLLER_SET_IME_REQUESTED_VISIBLE;
+ /** The control target reported its requestedVisibleTypes back to WindowManagerService. */
+ int PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES =
+ ImeProtoEnums.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES;
/**
* Called when an IME request is started.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 6303c76..5dd29b2 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2626,10 +2626,12 @@
// The view is running on a different thread than our own, so
// we need to reschedule our work for over there.
if (DEBUG) Log.v(TAG, "Hiding soft input: reschedule to view thread");
+ final var finalStatsToken = statsToken;
vh.post(() -> viewRootImpl.getInsetsController().hide(
- WindowInsets.Type.ime()));
+ WindowInsets.Type.ime(), false /* fromIme */, finalStatsToken));
} else {
- viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime());
+ viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime(),
+ false /* fromIme */, statsToken);
}
}
return true;
diff --git a/core/java/android/webkit/UserPackage.java b/core/java/android/webkit/UserPackage.java
index b18dbbc..1bc952c 100644
--- a/core/java/android/webkit/UserPackage.java
+++ b/core/java/android/webkit/UserPackage.java
@@ -21,7 +21,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
@@ -36,8 +35,6 @@
private final UserHandle mUser;
private final PackageInfo mPackageInfo;
- public static final int MINIMUM_SUPPORTED_SDK = Build.VERSION_CODES.TIRAMISU;
-
public UserPackage(@NonNull UserHandle user, @Nullable PackageInfo packageInfo) {
mUser = user;
mPackageInfo = packageInfo;
@@ -83,14 +80,6 @@
& ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0));
}
- /**
- * Returns whether the package represented by {@param packageInfo} targets a sdk version
- * supported by the current framework version.
- */
- public static boolean hasCorrectTargetSdkVersion(PackageInfo packageInfo) {
- return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_SDK;
- }
-
public UserHandle getUser() {
return mUser;
}
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index de303f8..1a48bbb 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -51,12 +51,6 @@
*/
@SystemApi
public final class WebViewFactory {
-
- // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
- /** @hide */
- private static final String CHROMIUM_WEBVIEW_FACTORY =
- "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
-
private static final String CHROMIUM_WEBVIEW_FACTORY_METHOD = "create";
private static final String LOGTAG = "WebViewFactory";
@@ -275,8 +269,8 @@
*/
public static Class<WebViewFactoryProvider> getWebViewProviderClass(ClassLoader clazzLoader)
throws ClassNotFoundException {
- return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY,
- true, clazzLoader);
+ return (Class<WebViewFactoryProvider>) Class.forName(
+ WebViewFactoryProvider.getWebViewFactoryClassName(), true, clazzLoader);
}
/**
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 3d64506..4a2f9ba 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -20,8 +20,11 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
import android.net.Network;
import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
import java.util.List;
@@ -34,6 +37,61 @@
@SystemApi
public interface WebViewFactoryProvider {
/**
+ * Used as the requirement when Flags.useBEntryPoint() is false.
+ * @hide
+ */
+ int MINIMUM_SUPPORTED_TARGET_SDK = Build.VERSION_CODES.TIRAMISU;
+
+ /**
+ * Used as the requirement when Flags.useBEntryPoint() is true.
+ * TODO: set to the actual minimum required version code - this is just the
+ * version shipped in V.
+ * @hide
+ */
+ long MINIMUM_SUPPORTED_VERSION_CODE = 661308800L;
+
+ /**
+ * Returns whether the WebView implementation represented by {@code packageInfo}
+ * is compatible with this version of Android.
+ * @hide
+ */
+ static boolean isCompatibleImplementationPackage(@NonNull PackageInfo packageInfo) {
+ if (Flags.useBEntryPoint()) {
+ return packageInfo.versionCode >= MINIMUM_SUPPORTED_VERSION_CODE;
+ } else {
+ return packageInfo.applicationInfo.targetSdkVersion >= MINIMUM_SUPPORTED_TARGET_SDK;
+ }
+ }
+
+ /**
+ * Returns a string describing the minimum requirement for a WebView implementation
+ * to be compatible with this version of Android, for debugging purposes.
+ * @hide
+ */
+ static @NonNull String describeCompatibleImplementationPackage() {
+ if (Flags.useBEntryPoint()) {
+ return TextUtils.formatSimple("Minimum versionCode for OS support: %d",
+ MINIMUM_SUPPORTED_VERSION_CODE);
+ } else {
+ return TextUtils.formatSimple("Minimum targetSdkVersion: %d",
+ MINIMUM_SUPPORTED_TARGET_SDK);
+ }
+ }
+
+ /**
+ * Returns the name of the class that should be used when loading the
+ * WebView implementation on this version of Android.
+ * @hide
+ */
+ static @NonNull String getWebViewFactoryClassName() {
+ if (Flags.useBEntryPoint()) {
+ return "com.android.webview.chromium.WebViewChromiumFactoryProviderForB";
+ } else {
+ return "com.android.webview.chromium.WebViewChromiumFactoryProviderForT";
+ }
+ }
+
+ /**
* This Interface provides glue for implementing the backend of WebView static methods which
* cannot be implemented in-situ in the proxy class.
*/
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index c9e94d2..c5176a2 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -42,3 +42,11 @@
description: "New APIs required by File System Access"
bug: "40101963"
}
+
+flag {
+ name: "use_b_entry_point"
+ namespace: "webview"
+ description: "Use B-specific entry point to WebView APK"
+ bug: "373617389"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/window/KeyguardState.java b/core/java/android/window/KeyguardState.java
index 6584d30..159275a 100644
--- a/core/java/android/window/KeyguardState.java
+++ b/core/java/android/window/KeyguardState.java
@@ -30,28 +30,23 @@
*/
public final class KeyguardState implements Parcelable {
- private final int mDisplayId;
-
private final boolean mKeyguardShowing;
private final boolean mAodShowing;
- private KeyguardState(int displayId, boolean keyguardShowing, boolean aodShowing) {
- mDisplayId = displayId;
+ private KeyguardState(boolean keyguardShowing, boolean aodShowing) {
mKeyguardShowing = keyguardShowing;
mAodShowing = aodShowing;
}
private KeyguardState(Parcel in) {
- mDisplayId = in.readInt();
mKeyguardShowing = in.readBoolean();
mAodShowing = in.readBoolean();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(mDisplayId);
dest.writeBoolean(mKeyguardShowing);
dest.writeBoolean(mAodShowing);
}
@@ -70,13 +65,6 @@
}
};
- /**
- * Gets the display id of this {@link KeyguardState}.
- */
- public int getDisplayId() {
- return mDisplayId;
- }
-
/** Returns the keyguard showing value. */
public boolean getKeyguardShowing() {
return mKeyguardShowing;
@@ -89,15 +77,14 @@
@Override
public String toString() {
- return "KeyguardState{ displayId=" + mDisplayId
- + ", keyguardShowing=" + mKeyguardShowing
+ return "KeyguardState{ keyguardShowing=" + mKeyguardShowing
+ ", aodShowing=" + mAodShowing
+ '}';
}
@Override
public int hashCode() {
- return Objects.hash(mDisplayId, mKeyguardShowing, mAodShowing);
+ return Objects.hash(mKeyguardShowing, mAodShowing);
}
@Override
@@ -105,8 +92,7 @@
if (!(obj instanceof KeyguardState other)) {
return false;
}
- return mDisplayId == other.mDisplayId
- && mKeyguardShowing == other.mKeyguardShowing
+ return mKeyguardShowing == other.mKeyguardShowing
&& mAodShowing == other.mAodShowing;
}
@@ -117,18 +103,11 @@
/** Builder to construct the {@link KeyguardState}. */
public static final class Builder {
-
- private final int mDisplayId;
-
private boolean mKeyguardShowing;
private boolean mAodShowing;
- /**
- * @param displayId the display of this {@link KeyguardState}.
- */
- public Builder(int displayId) {
- mDisplayId = displayId;
+ public Builder() {
}
/**
@@ -154,7 +133,7 @@
*/
@NonNull
public KeyguardState build() {
- return new KeyguardState(mDisplayId, mKeyguardShowing, mAodShowing);
+ return new KeyguardState(mKeyguardShowing, mAodShowing);
}
}
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index 7ad14b0..801698c 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -112,6 +112,18 @@
}
flag {
+ name: "app_compat_async_relayout"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether we use the SurfaceViewHost overload to apply a change in /n"
+ "position and size in a Transaction provided by a callback invoked /n"
+ "after the View relayout."
+ bug: "322463856"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "app_compat_refactoring"
namespace: "large_screen_experiences_app_compat"
description: "Whether the changes about app compat refactoring are enabled./n"
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 7faa5d7..65e5679 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -58,10 +58,13 @@
}
flag {
- name: "enable_desktop_windowing_scvh_cache"
+ name: "enable_desktop_windowing_scvh_cache_bug_fix"
namespace: "lse_desktop_experience"
description: "Enables a SurfaceControlViewHost cache for window decorations"
- bug: "345146928"
+ bug: "360452034"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index f690bd3..92f9e60 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -20,9 +20,13 @@
import android.annotation.Nullable;
import android.app.AlertDialog;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
@@ -58,6 +62,7 @@
import android.widget.ScrollView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
+import android.widget.flags.Flags;
import com.android.internal.R;
@@ -66,6 +71,12 @@
public class AlertController {
public static final int MICRO = 1;
+ private static boolean sUseWearMaterial3Style;
+
+ @ChangeId
+ @EnabledSince(targetSdkVersion = 36)
+ private static final long WEAR_MATERIAL3_ALERTDIALOG = 379365266L;
+
private final Context mContext;
private final DialogInterface mDialogInterface;
protected final Window mWindow;
@@ -210,7 +221,8 @@
mHandler = new ButtonHandler(di);
final TypedArray a = context.obtainStyledAttributes(null,
- R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
+ R.styleable.AlertDialog, getAlertDialogDefStyleAttr(context),
+ getAlertDialogDefStyleRes());
mAlertDialogLayout = a.getResourceId(
R.styleable.AlertDialog_layout, R.layout.alert_dialog);
@@ -236,6 +248,28 @@
window.requestFeature(Window.FEATURE_NO_TITLE);
}
+ private int getAlertDialogDefStyleAttr(Context context) {
+ sUseWearMaterial3Style = useWearMaterial3Style(context);
+ if (sUseWearMaterial3Style) {
+ return 0;
+ }
+ return R.attr.alertDialogStyle;
+ }
+
+ private int getAlertDialogDefStyleRes() {
+ if (sUseWearMaterial3Style) {
+ return com.android.internal.R.style.AlertDialog_DeviceDefault_WearMaterial3;
+ }
+ return 0;
+ }
+
+ private static boolean useWearMaterial3Style(Context context) {
+ return Flags.useWearMaterial3Ui()
+ && CompatChanges.isChangeEnabled(WEAR_MATERIAL3_ALERTDIALOG)
+ && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ && context.getThemeResId() == com.android.internal.R.style.Theme_DeviceDefault;
+ }
+
static boolean canTextInput(View v) {
if (v.onCheckIsTextEditor()) {
return true;
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 0c56c67..6ad7fef 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -26,6 +26,7 @@
import static android.system.OsConstants.S_IXOTH;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
@@ -177,6 +178,13 @@
private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
String abiToCopy, boolean extractNativeLibs, boolean debuggable);
+ private static native int nativeCheckAlignment(
+ long handle,
+ String sharedLibraryPath,
+ String abi,
+ boolean extractNativeLibs,
+ boolean debuggable);
+
private static long sumNativeBinaries(Handle handle, String abi) {
long sum = 0;
for (long apkHandle : handle.apkHandles) {
@@ -432,6 +440,51 @@
}
}
+ /**
+ * Checks alignment of APK and native libraries for 16KB device
+ *
+ * @param handle APK file to scan for native libraries
+ * @param libraryRoot directory for libraries
+ * @param abiOverride abiOverride for package
+ * @return {@link Modes from ApplicationInfo.PageSizeAppCompat} if successful or error code
+ * which suggests undefined mode
+ */
+ @ApplicationInfo.PageSizeAppCompatFlags
+ public static int checkAlignmentForCompatMode(
+ Handle handle,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String abiOverride) {
+ // Keep the code below in sync with copyNativeBinariesForSupportedAbi
+ int abi = findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);
+ if (abi < 0) {
+ return ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ final String supportedAbi = Build.SUPPORTED_64_BIT_ABIS[abi];
+ final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
+ String subDir = libraryRoot;
+ if (nativeLibraryRootRequiresIsa) {
+ subDir += "/" + instructionSet;
+ }
+
+ int mode = ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ for (long apkHandle : handle.apkHandles) {
+ int res =
+ nativeCheckAlignment(
+ apkHandle,
+ subDir,
+ Build.SUPPORTED_64_BIT_ABIS[abi],
+ handle.extractNativeLibs,
+ handle.debuggable);
+ if (res == ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ return res;
+ }
+ mode |= res;
+ }
+ return mode;
+ }
+
public static long sumNativeBinariesWithOverride(Handle handle, String abiOverride)
throws IOException {
long sum = 0;
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index eb6a810..429a6a2 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -89,6 +89,8 @@
SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION,
SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
+ SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED,
+ SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
})
public @interface SoftInputShowHideReason {
/** Default, undefined reason. */
@@ -337,6 +339,18 @@
int HIDE_WINDOW_LEGACY_DIRECT = ImeProtoEnums.REASON_HIDE_WINDOW_LEGACY_DIRECT;
/**
+ * Show soft input because the input target changed
+ * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
+ */
+ int SHOW_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_SHOW_INPUT_TARGET_CHANGED;
+
+ /**
+ * Hide soft input because the input target changed by
+ * {@link com.android.server.wm.ImeInsetsSourceProvider#onInputTargetChanged}.
+ */
+ int HIDE_INPUT_TARGET_CHANGED = ImeProtoEnums.REASON_HIDE_INPUT_TARGET_CHANGED;
+
+ /**
* Show / Hide soft input by
* {@link android.inputmethodservice.InputMethodService#resetStateForNewConfiguration}.
*/
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 2834e68..454323b 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -685,14 +685,6 @@
}
}
- ThreadedRendererWrapper getThreadedRenderer() {
- return mRendererWrapper;
- }
-
- ViewRootWrapper getViewRoot() {
- return mViewRoot;
- }
-
private boolean shouldTriggerPerfetto(int missedFramesCount, int maxFrameTimeNanos) {
boolean overMissedFramesThreshold = mTraceThresholdMissedFrames != -1
&& missedFramesCount >= mTraceThresholdMissedFrames;
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index ef08e49..cbc20dc 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -31,6 +31,7 @@
import android.annotation.UiThread;
import android.annotation.WorkerThread;
import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
@@ -184,10 +185,12 @@
@GuardedBy("mLock")
private final SparseArray<RunningTracker> mRunningTrackers = new SparseArray<>();
private final Handler mWorker;
+ private final Application mCurrentApplication;
private final DisplayResolutionTracker mDisplayResolutionTracker;
private final Object mLock = new Object();
private @ColorInt int mDebugBgColor = Color.CYAN;
private double mDebugYOffset = 0.1;
+ @GuardedBy("mLock")
private InteractionMonitorDebugOverlay mDebugOverlay;
private volatile boolean mEnabled = DEFAULT_ENABLED;
@@ -216,13 +219,15 @@
mWorker = worker.getThreadHandler();
mDisplayResolutionTracker = new DisplayResolutionTracker(mWorker);
- final Context context = ActivityThread.currentApplication();
- if (context == null || context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
+ mCurrentApplication = ActivityThread.currentApplication();
+ if (mCurrentApplication == null || mCurrentApplication.checkCallingOrSelfPermission(
+ READ_DEVICE_CONFIG) != PERMISSION_GRANTED) {
Log.w(TAG, "Initializing without READ_DEVICE_CONFIG permission."
+ " enabled=" + mEnabled + ", interval=" + mSamplingInterval
+ ", missedFrameThreshold=" + mTraceThresholdMissedFrames
+ ", frameTimeThreshold=" + mTraceThresholdFrameTimeMillis
- + ", package=" + (context == null ? "null" : context.getPackageName()));
+ + ", package=" + (mCurrentApplication == null ? "null"
+ : mCurrentApplication.getPackageName()));
return;
}
@@ -234,8 +239,8 @@
new HandlerExecutor(mWorker), this::updateProperties);
} catch (SecurityException ex) {
Log.d(TAG, "Can't get properties: READ_DEVICE_CONFIG granted="
- + context.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
- + ", package=" + context.getPackageName());
+ + mCurrentApplication.checkCallingOrSelfPermission(READ_DEVICE_CONFIG)
+ + ", package=" + mCurrentApplication.getPackageName());
}
});
}
@@ -405,7 +410,11 @@
RunningTracker tracker = putTrackerIfNoCurrent(cujType, () ->
new RunningTracker(
- conf, createFrameTracker(conf), () -> cancel(cujType, REASON_CANCEL_TIMEOUT)));
+ conf, createFrameTracker(conf), () -> {
+ Log.w(TAG, "CUJ cancelled due to timeout, CUJ="
+ + Cuj.getNameOfCuj(cujType));
+ cancel(cujType, REASON_CANCEL_TIMEOUT);
+ }));
if (tracker == null) {
return false;
}
@@ -534,7 +543,7 @@
mRunningTrackers.put(cuj, tracker);
if (mDebugOverlay != null) {
- mDebugOverlay.onTrackerAdded(cuj, tracker);
+ mDebugOverlay.onTrackerAdded(cuj);
}
return tracker;
@@ -569,7 +578,7 @@
running.mConfig.getHandler().removeCallbacks(running.mTimeoutAction);
mRunningTrackers.remove(cuj);
if (mDebugOverlay != null) {
- mDebugOverlay.onTrackerRemoved(cuj, reason, mRunningTrackers);
+ mDebugOverlay.onTrackerRemoved(cuj, reason);
}
return false;
}
@@ -592,14 +601,18 @@
mEnabled = properties.getBoolean(property, DEFAULT_ENABLED);
case SETTINGS_DEBUG_OVERLAY_ENABLED_KEY -> {
// Never allow the debug overlay to be used on user builds
- boolean debugOverlayEnabled = Build.IS_DEBUGGABLE
- && properties.getBoolean(property, DEFAULT_DEBUG_OVERLAY_ENABLED);
- if (debugOverlayEnabled && mDebugOverlay == null) {
- mDebugOverlay = new InteractionMonitorDebugOverlay(
- mLock, mDebugBgColor, mDebugYOffset);
- } else if (!debugOverlayEnabled && mDebugOverlay != null) {
- mDebugOverlay.dispose();
- mDebugOverlay = null;
+ if (Build.IS_USER) break;
+ boolean debugOverlayEnabled = properties.getBoolean(property,
+ DEFAULT_DEBUG_OVERLAY_ENABLED);
+ synchronized (mLock) {
+ if (debugOverlayEnabled && mDebugOverlay == null) {
+ // Use the worker thread as the UI thread for the debug overlay:
+ mDebugOverlay = new InteractionMonitorDebugOverlay(
+ mCurrentApplication, mWorker, mDebugBgColor, mDebugYOffset);
+ } else if (!debugOverlayEnabled && mDebugOverlay != null) {
+ mDebugOverlay.dispose();
+ mDebugOverlay = null;
+ }
}
}
default -> Log.w(TAG, "Got a change event for an unknown property: "
diff --git a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
index d9cac12..009837b 100644
--- a/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
+++ b/core/java/com/android/internal/jank/InteractionMonitorDebugOverlay.java
@@ -16,24 +16,36 @@
package com.android.internal.jank;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Gravity.CENTER;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
+
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
+import android.annotation.AnyThread;
import android.annotation.ColorInt;
+import android.annotation.NonNull;
import android.annotation.UiThread;
-import android.app.ActivityThread;
+import android.app.Application;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.RecordingCanvas;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.Trace;
+import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.view.WindowCallbacks;
+import android.view.Display;
+import android.view.View;
+import android.view.WindowManager;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.jank.FrameTracker.Reasons;
/**
@@ -50,210 +62,171 @@
* <li> Grey text indicates the CUJ ended normally and is no longer running
* <li> Red text with a strikethrough indicates the CUJ was canceled or ended abnormally
* </ul>
+ *
* @hide
*/
-class InteractionMonitorDebugOverlay implements WindowCallbacks {
+class InteractionMonitorDebugOverlay {
private static final String TAG = "InteractionMonitorDebug";
private static final int REASON_STILL_RUNNING = -1000;
- private final Object mLock;
// Sparse array where the key in the CUJ and the value is the session status, or null if
// it's currently running
- @GuardedBy("mLock")
+ private final Application mCurrentApplication;
+ private final Handler mUiThread;
+ private final DebugOverlayView mDebugOverlayView;
+ private final WindowManager mWindowManager;
private final SparseIntArray mRunningCujs = new SparseIntArray();
- private Handler mHandler = null;
- private FrameTracker.ViewRootWrapper mViewRoot = null;
- private final Paint mDebugPaint;
- private final Paint.FontMetrics mDebugFontMetrics;
- // Used to display the overlay in a different color and position for different processes.
- // Otherwise, two overlays will overlap and be difficult to read.
- private final int mBgColor;
- private final double mYOffset;
- private final String mPackageName;
- private static final String TRACK_NAME = "InteractionJankMonitor";
- InteractionMonitorDebugOverlay(Object lock, @ColorInt int bgColor, double yOffset) {
- mLock = lock;
- mBgColor = bgColor;
- mYOffset = yOffset;
- mDebugPaint = new Paint();
- mDebugPaint.setAntiAlias(false);
- mDebugFontMetrics = new Paint.FontMetrics();
- final Context context = ActivityThread.currentApplication();
- mPackageName = context == null ? "null" : context.getPackageName();
- }
+ InteractionMonitorDebugOverlay(@NonNull Application currentApplication,
+ @NonNull @UiThread Handler uiThread, @ColorInt int bgColor, double yOffset) {
+ mCurrentApplication = currentApplication;
+ mUiThread = uiThread;
+ final Display display = mCurrentApplication.getSystemService(
+ DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
+ final Context windowContext = mCurrentApplication.createDisplayContext(
+ display).createWindowContext(TYPE_SYSTEM_OVERLAY, null /* options */);
+ mWindowManager = windowContext.getSystemService(WindowManager.class);
- @UiThread
- void dispose() {
- if (mViewRoot != null && mHandler != null) {
- mHandler.runWithScissors(() -> mViewRoot.removeWindowCallbacks(this),
- InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- forceRedraw();
+ final Rect size = mWindowManager.getCurrentWindowMetrics().getBounds();
+
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+
+ lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+
+ lp.width = size.width();
+ lp.height = size.height();
+ lp.gravity = CENTER;
+ lp.setTitle("InteractionMonitorDebugOverlay");
+
+ if (!mUiThread.getLooper().isCurrentThread()) {
+ Log.e(TAG, "InteractionMonitorDebugOverlay must be constructed on "
+ + "InteractionJankMonitor's worker thread");
}
- mHandler = null;
- mViewRoot = null;
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0);
+ mDebugOverlayView = new DebugOverlayView(mCurrentApplication, bgColor, yOffset);
+ mWindowManager.addView(mDebugOverlayView, lp);
+ }
+
+ @AnyThread
+ void onTrackerAdded(@Cuj.CujType int addedCuj) {
+ mUiThread.post(() -> {
+ String cujName = Cuj.getNameOfCuj(addedCuj);
+ Log.i(TAG, cujName + " started");
+ // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
+ // is still running
+ mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
+ mDebugOverlayView.setVisibility(VISIBLE);
+ mDebugOverlayView.invalidate();
+ });
+ }
+
+ @AnyThread
+ void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason) {
+ mUiThread.post(() -> {
+ mRunningCujs.put(removedCuj, reason);
+ String cujName = Cuj.getNameOfCuj(removedCuj);
+ Log.i(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled"));
+ // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
+ if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
+ Log.i(TAG, "All CUJs ended");
+ mRunningCujs.clear();
+ mDebugOverlayView.setVisibility(INVISIBLE);
+ }
+ mDebugOverlayView.invalidate();
+ });
+ }
+
+ @AnyThread
+ void dispose() {
+ mUiThread.post(() -> {
+ mWindowManager.removeView(mDebugOverlayView);
+ });
}
@UiThread
- private boolean attachViewRootIfNeeded(InteractionJankMonitor.RunningTracker tracker) {
- FrameTracker.ViewRootWrapper viewRoot = tracker.mTracker.getViewRoot();
- if (mViewRoot == null && viewRoot != null) {
+ private class DebugOverlayView extends View {
+ private static final String TRACK_NAME = "InteractionJankMonitor";
+
+ // Used to display the overlay in a different color and position for different processes.
+ // Otherwise, two overlays will overlap and be difficult to read.
+ private final int mBgColor;
+ private final double mYOffset;
+
+ private final float mDensity;
+ private final Paint mDebugPaint;
+ private final Paint.FontMetrics mDebugFontMetrics;
+ private final String mPackageName;
+
+ private DebugOverlayView(Context context, @ColorInt int bgColor, double yOffset) {
+ super(context);
+ setVisibility(INVISIBLE);
+ mBgColor = bgColor;
+ mYOffset = yOffset;
+ final DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ mDensity = displayMetrics.density;
+ mDebugPaint = new Paint();
+ mDebugPaint.setAntiAlias(false);
+ mDebugFontMetrics = new Paint.FontMetrics();
+ mPackageName = mCurrentApplication.getPackageName();
+ }
+
+ private int dipToPx(int dip) {
+ return (int) (mDensity * dip + 0.5f);
+ }
+
+ private float getTextHeight(int textSize) {
+ mDebugPaint.setTextSize(textSize);
+ mDebugPaint.getFontMetrics(mDebugFontMetrics);
+ return mDebugFontMetrics.descent - mDebugFontMetrics.ascent;
+ }
+
+ private float getWidthOfLongestCujName(int cujFontSize) {
+ mDebugPaint.setTextSize(cujFontSize);
+ float maxLength = 0;
+ for (int i = 0; i < mRunningCujs.size(); i++) {
+ String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
+ float textLength = mDebugPaint.measureText(cujName);
+ if (textLength > maxLength) {
+ maxLength = textLength;
+ }
+ }
+ return maxLength;
+ }
+
+ @Override
+ protected void onDraw(@NonNull Canvas canvas) {
+ super.onDraw(canvas);
+
// Add a trace marker so we can identify traces that were captured while the debug
// overlay was enabled. Traces that use the debug overlay should NOT be used for
// performance analysis.
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TRACK_NAME, "DEBUG_OVERLAY_DRAW", 0);
- mHandler = tracker.mConfig.getHandler();
- mViewRoot = viewRoot;
- mHandler.runWithScissors(() -> viewRoot.addWindowCallbacks(this),
- InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- forceRedraw();
- return true;
- }
- return false;
- }
- @GuardedBy("mLock")
- private float getWidthOfLongestCujName(int cujFontSize) {
- mDebugPaint.setTextSize(cujFontSize);
- float maxLength = 0;
- for (int i = 0; i < mRunningCujs.size(); i++) {
- String cujName = Cuj.getNameOfCuj(mRunningCujs.keyAt(i));
- float textLength = mDebugPaint.measureText(cujName);
- if (textLength > maxLength) {
- maxLength = textLength;
- }
- }
- return maxLength;
- }
-
- private float getTextHeight(int textSize) {
- mDebugPaint.setTextSize(textSize);
- mDebugPaint.getFontMetrics(mDebugFontMetrics);
- return mDebugFontMetrics.descent - mDebugFontMetrics.ascent;
- }
-
- private int dipToPx(int dip) {
- if (mViewRoot != null) {
- return mViewRoot.dipToPx(dip);
- } else {
- return dip;
- }
- }
-
- @UiThread
- private void forceRedraw() {
- if (mViewRoot != null && mHandler != null) {
- mHandler.runWithScissors(() -> {
- mViewRoot.requestInvalidateRootRenderNode();
- mViewRoot.getView().invalidate();
- }, InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT);
- }
- }
-
- @UiThread
- void onTrackerRemoved(@Cuj.CujType int removedCuj, @Reasons int reason,
- SparseArray<InteractionJankMonitor.RunningTracker> runningTrackers) {
- synchronized (mLock) {
- mRunningCujs.put(removedCuj, reason);
- boolean isLoggable = Log.isLoggable(TAG, Log.DEBUG);
- if (isLoggable) {
- String cujName = Cuj.getNameOfCuj(removedCuj);
- Log.d(TAG, cujName + (reason == REASON_END_NORMAL ? " ended" : " cancelled"));
- }
- // If REASON_STILL_RUNNING is not in mRunningCujs, then all CUJs have ended
- if (mRunningCujs.indexOfValue(REASON_STILL_RUNNING) < 0) {
- if (isLoggable) Log.d(TAG, "All CUJs ended");
- mRunningCujs.clear();
- dispose();
- } else {
- boolean needsNewViewRoot = true;
- if (mViewRoot != null) {
- // Check to see if this viewroot is still associated with one of the running
- // trackers
- for (int i = 0; i < runningTrackers.size(); i++) {
- if (mViewRoot.equals(
- runningTrackers.valueAt(i).mTracker.getViewRoot())) {
- needsNewViewRoot = false;
- break;
- }
- }
- }
- if (needsNewViewRoot) {
- dispose();
- for (int i = 0; i < runningTrackers.size(); i++) {
- if (attachViewRootIfNeeded(runningTrackers.valueAt(i))) {
- break;
- }
- }
- } else {
- forceRedraw();
- }
- }
- }
- }
-
- @UiThread
- void onTrackerAdded(@Cuj.CujType int addedCuj, InteractionJankMonitor.RunningTracker tracker) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- String cujName = Cuj.getNameOfCuj(addedCuj);
- Log.d(TAG, cujName + " started");
- }
- synchronized (mLock) {
- // Use REASON_STILL_RUNNING (not technically one of the '@Reasons') to indicate the CUJ
- // is still running
- mRunningCujs.put(addedCuj, REASON_STILL_RUNNING);
- attachViewRootIfNeeded(tracker);
- forceRedraw();
- }
- }
-
- @Override
- public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen,
- Rect systemInsets, Rect stableInsets) {
- }
-
- @Override
- public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen,
- Rect systemInsets, Rect stableInsets) {
- }
-
- @Override
- public void onWindowDragResizeEnd() {
- }
-
- @Override
- public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
- return false;
- }
-
- @Override
- public void onRequestDraw(boolean reportNextDraw) {
- }
-
- @Override
- public void onPostDraw(RecordingCanvas canvas) {
- final int padding = dipToPx(5);
- final int h = canvas.getHeight();
- final int w = canvas.getWidth();
- // Draw sysui CUjs near the bottom of the screen so they don't overlap with the shade,
- // and draw launcher CUJs near the top of the screen so they don't overlap with gestures
- final int dy = (int) (h * mYOffset);
- int packageNameFontSize = dipToPx(12);
- int cujFontSize = dipToPx(18);
- final float cujNameTextHeight = getTextHeight(cujFontSize);
- final float packageNameTextHeight = getTextHeight(packageNameFontSize);
-
- synchronized (mLock) {
+ final int padding = dipToPx(5);
+ final int h = getHeight();
+ final int w = getWidth();
+ final int dy = (int) (h * mYOffset);
+ int packageNameFontSize = dipToPx(12);
+ int cujFontSize = dipToPx(18);
+ final float cujNameTextHeight = getTextHeight(cujFontSize);
+ final float packageNameTextHeight = getTextHeight(packageNameFontSize);
float maxLength = getWidthOfLongestCujName(cujFontSize);
final int dx = (int) ((w - maxLength) / 2f);
canvas.translate(dx, dy);
// Draw background rectangle for displaying the text showing the CUJ name
mDebugPaint.setColor(mBgColor);
- canvas.drawRect(
- -padding * 2, // more padding on top so we can draw the package name
- -padding,
- padding * 2 + maxLength,
+ canvas.drawRect(-padding * 2, // more padding on top so we can draw the package name
+ -padding, padding * 2 + maxLength,
padding * 2 + packageNameTextHeight + cujNameTextHeight * mRunningCujs.size(),
mDebugPaint);
mDebugPaint.setTextSize(packageNameFontSize);
@@ -280,6 +253,7 @@
canvas.translate(0, cujNameTextHeight);
canvas.drawText(cujName, 0, 0, mDebugPaint);
}
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TRACK_NAME, 0);
}
}
}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fafa085..cd17ed8 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -201,6 +201,9 @@
*/
public static final int DEBUG_ENABLE_PTRACE = 1 << 25;
+ /** Load 4KB ELF files on 16KB device using appcompat mode */
+ public static final int ENABLE_PAGE_SIZE_APP_COMPAT = 1 << 26;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
index c953d88..324e84c 100644
--- a/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
+++ b/core/java/com/android/internal/pm/pkg/component/AconfigFlags.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Flags;
-import android.content.res.XmlResourceParser;
import android.os.Environment;
import android.os.Process;
import android.util.ArrayMap;
@@ -247,13 +246,12 @@
negated = true;
featureFlag = featureFlag.substring(1).strip();
}
- final Boolean flagValue = getFlagValue(featureFlag);
- boolean shouldSkip = false;
+ Boolean flagValue = getFlagValue(featureFlag);
if (flagValue == null) {
- Slog.w(LOG_TAG, "Skipping element " + parser.getName()
- + " due to unknown feature flag " + featureFlag);
- shouldSkip = true;
- } else if (flagValue == negated) {
+ flagValue = false;
+ }
+ boolean shouldSkip = false;
+ if (flagValue == negated) {
// Skip if flag==false && attr=="flag" OR flag==true && attr=="!flag" (negated)
Slog.i(LOG_TAG, "Skipping element " + parser.getName()
+ " behind feature flag " + featureFlag + " = " + flagValue);
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 2bfbf84..4b90420 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import static android.app.Flags.notificationsRedesignTemplates;
import static android.widget.flags.Flags.conversationLayoutUseMaximumChildHeight;
import static com.android.internal.widget.MessagingGroup.IMAGE_DISPLAY_LOCATION_EXTERNAL;
@@ -692,7 +693,7 @@
}
private void updateActionListPadding() {
- if (mActions != null) {
+ if (!notificationsRedesignTemplates() && mActions != null) {
mActions.setCollapsibleIndentDimen(R.dimen.call_notification_collapsible_indent);
}
}
diff --git a/core/java/com/android/internal/widget/NotificationActionListLayout.java b/core/java/com/android/internal/widget/NotificationActionListLayout.java
index 301dc39..cac2024 100644
--- a/core/java/com/android/internal/widget/NotificationActionListLayout.java
+++ b/core/java/com/android/internal/widget/NotificationActionListLayout.java
@@ -20,6 +20,7 @@
import static android.app.Flags.evenlyDividedCallStyleActionLayout;
import android.annotation.DimenRes;
+import android.app.Flags;
import android.app.Notification;
import android.content.Context;
import android.content.res.TypedArray;
@@ -58,7 +59,7 @@
private int mEmphasizedPaddingBottom;
private int mEmphasizedHeight;
private int mRegularHeight;
- @DimenRes private int mCollapsibleIndentDimen = R.dimen.notification_actions_padding_start;
+ @DimenRes private int mCollapsibleIndentDimen;
int mNumNotGoneChildren;
int mNumPriorityChildren;
@@ -73,6 +74,10 @@
public NotificationActionListLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mCollapsibleIndentDimen = Flags.notificationsRedesignTemplates()
+ ? R.dimen.notification_2025_actions_margin_start
+ : R.dimen.notification_actions_padding_start;
+
int[] attrIds = { android.R.attr.gravity };
TypedArray ta = context.obtainStyledAttributes(attrs, attrIds, defStyleAttr, defStyleRes);
mGravity = ta.getInt(0, 0);
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 7ad18b8..b2eeff3 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -21,6 +21,7 @@
#include <androidfw/ApkParsing.h>
#include <androidfw/ZipFileRO.h>
#include <androidfw/ZipUtils.h>
+#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -38,6 +39,7 @@
#include <memory>
#include <string>
+#include <vector>
#include "com_android_internal_content_FileSystemUtils.h"
#include "core_jni_helpers.h"
@@ -60,6 +62,12 @@
NO_NATIVE_LIBRARIES = -114
};
+// These code should match with PageSizeAppCompatFlags inside ApplicationInfo.java
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_ERROR = -1;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED = 0;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED = 1 << 1;
+constexpr int PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED = 1 << 2;
+
typedef install_status_t (*iterFunc)(JNIEnv*, void*, ZipFileRO*, ZipEntryRO, const char*);
static bool
@@ -524,11 +532,7 @@
static const size_t kPageSize = getpagesize();
// App compat is only applicable on 16kb-page-size devices.
- if (kPageSize != 0x4000) {
- return false;
- }
-
- return android::base::GetBoolProperty("bionic.linker.16kb.app_compat.enabled", false);
+ return kPageSize == 0x4000;
}
static jint
@@ -626,6 +630,166 @@
return reinterpret_cast<jlong>(zipFile);
}
+static jint checkLoadSegmentAlignment(const char* fileName, off64_t offset) {
+ std::vector<Elf64_Phdr> programHeaders;
+ if (!getLoadSegmentPhdrs(fileName, offset, programHeaders)) {
+ ALOGE("Failed to read program headers from ELF file.");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ for (auto programHeader : programHeaders) {
+ if (programHeader.p_type != PT_LOAD) {
+ continue;
+ }
+
+ // Set ELF alignment bit if 4 KB aligned LOAD segment is found
+ if (programHeader.p_align == 0x1000) {
+ ALOGI("Setting page size compat mode PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED");
+ mode |= PAGE_SIZE_APP_COMPAT_FLAG_ELF_NOT_ALIGNED;
+ break;
+ }
+ }
+
+ return mode;
+}
+
+static jint checkExtractedLibAlignment(ZipFileRO* zipFile, ZipEntryRO zipEntry,
+ const char* fileName, const std::string nativeLibPath) {
+ // Build local file path
+ const size_t fileNameLen = strlen(fileName);
+ char localFileName[nativeLibPath.size() + fileNameLen + 2];
+
+ if (strlcpy(localFileName, nativeLibPath.c_str(), sizeof(localFileName)) !=
+ nativeLibPath.size()) {
+ ALOGE("Couldn't allocate local file name for library");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ *(localFileName + nativeLibPath.size()) = '/';
+
+ if (strlcpy(localFileName + nativeLibPath.size() + 1, fileName,
+ sizeof(localFileName) - nativeLibPath.size() - 1) != fileNameLen) {
+ ALOGE("Couldn't allocate local file name for library");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ struct statfs64 fsInfo;
+ int result = statfs64(localFileName, &fsInfo);
+ if (result < 0) {
+ ALOGE("Failed to stat file :%s", localFileName);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ return checkLoadSegmentAlignment(localFileName, 0);
+}
+
+static jint checkAlignment(JNIEnv* env, jstring javaNativeLibPath, jboolean extractNativeLibs,
+ ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName) {
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ // Need two separate install status for APK and ELF alignment
+ static const size_t kPageSize = getpagesize();
+ jint ret = INSTALL_SUCCEEDED;
+
+ ScopedUtfChars nativeLibPath(env, javaNativeLibPath);
+ if (extractNativeLibs) {
+ ALOGI("extractNativeLibs specified, checking for extracted lib %s", fileName);
+ return checkExtractedLibAlignment(zipFile, zipEntry, fileName, nativeLibPath.c_str());
+ }
+
+ uint16_t method;
+ off64_t offset;
+ if (!zipFile->getEntryInfo(zipEntry, &method, nullptr, nullptr, &offset, nullptr, nullptr,
+ nullptr)) {
+ ALOGE("Couldn't read zip entry info from zipFile %s", zipFile->getZipFileName());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ // check if library is uncompressed and page-aligned
+ if (method != ZipFileRO::kCompressStored) {
+ ALOGE("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+ fileName);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ if (offset % kPageSize != 0) {
+ ALOGW("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
+ "from apk.\n",
+ fileName, kPageSize);
+ mode |= PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED;
+ ALOGI("Setting page size compat mode "
+ "PAGE_SIZE_APP_COMPAT_FLAG_UNCOMPRESSED_LIBS_NOT_ALIGNED for %s",
+ zipFile->getZipFileName());
+ }
+
+ int loadMode = checkLoadSegmentAlignment(zipFile->getZipFileName(), offset);
+ if (loadMode == PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ mode |= loadMode;
+ return mode;
+}
+
+// TODO(b/371049373): This function is copy of iterateOverNativeFiles with different way of handling
+// and combining return values for all ELF and APKs. Find a way to consolidate two functions.
+static jint com_android_internal_content_NativeLibraryHelper_checkApkAlignment(
+ JNIEnv* env, jclass clazz, jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
+ jboolean extractNativeLibs, jboolean debuggable) {
+ int mode = PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED;
+ ZipFileRO* zipFile = reinterpret_cast<ZipFileRO*>(apkHandle);
+ if (zipFile == nullptr) {
+ ALOGE("zipfile handle is null");
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ auto result = NativeLibrariesIterator::create(zipFile, debuggable);
+ if (!result.ok()) {
+ ALOGE("Can't iterate over native libs for file:%s", zipFile->getZipFileName());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ std::unique_ptr<NativeLibrariesIterator> it(std::move(result.value()));
+
+ const ScopedUtfChars cpuAbi(env, javaCpuAbi);
+ if (cpuAbi.c_str() == nullptr) {
+ ALOGE("cpuAbi is nullptr");
+ // This would've thrown, so this return code isn't observable by Java.
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+
+ while (true) {
+ auto next = it->next();
+ if (!next.ok()) {
+ ALOGE("next iterator not found Error:%d", next.error());
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ auto entry = next.value();
+ if (entry == nullptr) {
+ break;
+ }
+
+ const char* fileName = it->currentEntry();
+ const char* lastSlash = it->lastSlash();
+
+ // Check to make sure the CPU ABI of this file is one we support.
+ const char* cpuAbiOffset = fileName + APK_LIB_LEN;
+ const size_t cpuAbiRegionSize = lastSlash - cpuAbiOffset;
+
+ if (cpuAbi.size() == cpuAbiRegionSize &&
+ !strncmp(cpuAbiOffset, cpuAbi.c_str(), cpuAbiRegionSize)) {
+ int ret = checkAlignment(env, javaNativeLibPath, extractNativeLibs, zipFile, entry,
+ lastSlash + 1);
+ if (ret == PAGE_SIZE_APP_COMPAT_FLAG_ERROR) {
+ ALOGE("Alignment check returned for zipfile: %s, entry:%s",
+ zipFile->getZipFileName(), lastSlash + 1);
+ return PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ mode |= ret;
+ }
+ }
+
+ return mode;
+}
+
static void
com_android_internal_content_NativeLibraryHelper_close(JNIEnv *env, jclass, jlong apkHandle)
{
@@ -633,29 +797,23 @@
}
static const JNINativeMethod gMethods[] = {
- {"nativeOpenApk",
- "(Ljava/lang/String;)J",
- (void *)com_android_internal_content_NativeLibraryHelper_openApk},
- {"nativeOpenApkFd",
- "(Ljava/io/FileDescriptor;Ljava/lang/String;)J",
- (void *)com_android_internal_content_NativeLibraryHelper_openApkFd},
- {"nativeClose",
- "(J)V",
- (void *)com_android_internal_content_NativeLibraryHelper_close},
- {"nativeCopyNativeBinaries",
- "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
- (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
- {"nativeSumNativeBinaries",
- "(JLjava/lang/String;Z)J",
- (void *)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
- {"nativeFindSupportedAbi",
- "(J[Ljava/lang/String;Z)I",
- (void *)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
- {"hasRenderscriptBitcode", "(J)I",
- (void *)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
+ {"nativeOpenApk", "(Ljava/lang/String;)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_openApk},
+ {"nativeOpenApkFd", "(Ljava/io/FileDescriptor;Ljava/lang/String;)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_openApkFd},
+ {"nativeClose", "(J)V", (void*)com_android_internal_content_NativeLibraryHelper_close},
+ {"nativeCopyNativeBinaries", "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
+ {"nativeSumNativeBinaries", "(JLjava/lang/String;Z)J",
+ (void*)com_android_internal_content_NativeLibraryHelper_sumNativeBinaries},
+ {"nativeFindSupportedAbi", "(J[Ljava/lang/String;Z)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_findSupportedAbi},
+ {"hasRenderscriptBitcode", "(J)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_hasRenderscriptBitcode},
+ {"nativeCheckAlignment", "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
+ (void*)com_android_internal_content_NativeLibraryHelper_checkApkAlignment},
};
-
int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env)
{
return RegisterMethodsOrDie(env,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 284c299..aeaeeca 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -89,6 +89,7 @@
#if defined(__BIONIC__)
extern "C" void android_reset_stack_guards();
+extern "C" void android_set_16kb_appcompat_mode(bool enable_app_compat);
#endif
namespace {
@@ -350,6 +351,7 @@
NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23,
PROFILEABLE = 1 << 24,
DEBUG_ENABLE_PTRACE = 1 << 25,
+ ENABLE_PAGE_SIZE_APP_COMPAT = 1 << 26,
};
enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -2117,6 +2119,12 @@
SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
fail_fn);
+ if ((runtime_flags & RuntimeFlags::ENABLE_PAGE_SIZE_APP_COMPAT) != 0) {
+ android_set_16kb_appcompat_mode(true);
+ // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
+ // runtime.
+ runtime_flags &= ~RuntimeFlags::ENABLE_PAGE_SIZE_APP_COMPAT;
+ }
__android_log_close();
AStatsSocket_close();
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 16d2565..e424e82 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -218,6 +218,7 @@
optional SettingProto tap_to_click = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto tap_dragging = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto three_finger_tap_customization = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto system_gestures = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];;
}
optional Touchpad touchpad = 36;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e2f3d2a..7d77ff0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -8696,7 +8696,7 @@
android:featureFlag="android.security.secure_lockdown" />
<!-- Allows app to enter trade-in-mode.
- <p>Protection level: signature|privileged
+ <p>Protection level: signature
@hide
-->
<permission android:name="android.permission.ENTER_TRADE_IN_MODE"
diff --git a/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml b/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml
new file mode 100644
index 0000000..5c0e5f6
--- /dev/null
+++ b/core/res/res/drawable-watch-v36/progress_ring_wear_material3.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<rotate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromDegrees = "270"
+ android:toDegrees="270"
+ android:pivotX="50%"
+ android:pivotY="50%" >
+ <layer-list>
+ <item>
+ <shape
+ android:shape="ring"
+ android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio"
+ android:thickness="@dimen/progressbar_thickness"
+ android:useLevel="false">
+ <solid android:color="?attr/materialColorSurfaceContainer"/>
+ </shape>
+ </item>
+ <item>
+ <shape
+ android:shape="ring"
+ android:innerRadiusRatio="@dimen/progressbar_inner_radius_ratio"
+ android:thickness="@dimen/progressbar_thickness"
+ android:useLevel="true">
+ <solid android:color="?attr/materialColorPrimary"/>
+ </shape>
+ </item>
+ </layer-list>
+</rotate>
\ No newline at end of file
diff --git a/core/res/res/layout-watch-v36/alert_dialog_material.xml b/core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
similarity index 100%
rename from core/res/res/layout-watch-v36/alert_dialog_material.xml
rename to core/res/res/layout-watch-v36/alert_dialog_wear_material3.xml
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index a790e5d..09c02c9 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:minHeight="@dimen/notification_headerless_min_height"
+ android:minHeight="@dimen/notification_2025_min_height"
android:tag="base"
>
@@ -72,8 +72,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
- android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:orientation="vertical"
>
@@ -81,7 +81,7 @@
android:id="@+id/notification_top_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_headerless_line_height"
+ android:minHeight="@dimen/notification_2025_content_min_height"
android:clipChildren="false"
android:theme="@style/Theme.DeviceDefault.Notification"
>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index 06f5f06..614444d 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -32,7 +32,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="88dp"
+ android:minHeight="@dimen/notification_2025_min_height"
android:orientation="horizontal"
>
@@ -43,8 +43,7 @@
android:layout_weight="1"
android:layout_marginStart="@dimen/conversation_content_start"
android:orientation="vertical"
- android:minHeight="68dp"
- android:paddingBottom="@dimen/notification_headerless_margin_twoline"
+ android:paddingBottom="@dimen/notification_2025_margin"
>
<include
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 427c4e4..f539105 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -23,7 +23,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_min_height"
+ android:minHeight="@dimen/notification_2025_min_height"
android:tag="media"
>
@@ -74,8 +74,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
- android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:orientation="vertical"
>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f0e4c0f..ddf3ebc 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -38,7 +38,7 @@
<com.android.internal.widget.NotificationMaxHeightFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/notification_min_height"
+ android:minHeight="@dimen/notification_2025_min_height"
android:clipChildren="false"
>
@@ -98,8 +98,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/notification_headerless_margin_twoline"
- android:layout_marginTop="@dimen/notification_headerless_margin_twoline"
+ android:layout_marginBottom="@dimen/notification_2025_margin"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:layout_marginStart="@dimen/notification_2025_content_margin_start"
android:clipChildren="false"
android:orientation="vertical"
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
new file mode 100644
index 0000000..18bafe0
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -0,0 +1,93 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tag="bigPicture"
+ android:clipChildren="false"
+ >
+
+ <include layout="@layout/notification_2025_template_header" />
+
+ <include layout="@layout/notification_template_right_icon" />
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ >
+
+ <include layout="@layout/notification_template_part_line1" />
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ />
+
+ <include layout="@layout/notification_template_text_multiline" />
+ </LinearLayout>
+
+ <com.android.internal.widget.BigPictureNotificationImageView
+ android:id="@+id/big_picture"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:adjustViewBounds="true"
+ android:layout_weight="1"
+ android:layout_marginTop="13dp"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:background="@drawable/notification_big_picture_outline"
+ android:clipToOutline="true"
+ android:scaleType="centerCrop"
+ />
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_2025_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
new file mode 100644
index 0000000..9ff141b
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -0,0 +1,94 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:tag="bigText"
+ >
+
+ <include layout="@layout/notification_2025_template_header" />
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:layout_marginBottom="@dimen/notification_content_margin"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ >
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingStart="@dimen/notification_2025_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_end"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ android:layout_weight="1"
+ >
+
+ <include layout="@layout/notification_template_part_line1" />
+
+ <include
+ layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ android:layout_marginBottom="6dp"
+ />
+
+ <com.android.internal.widget.ImageFloatingTextView
+ android:id="@+id/big_text"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_text_margin_top"
+ android:singleLine="false"
+ android:gravity="top"
+ android:visibility="gone"
+ android:textAlignment="viewStart"
+ />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_2025_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
new file mode 100644
index 0000000..9fb44ccc
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -0,0 +1,131 @@
+<?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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:tag="inbox"
+ android:clipChildren="false"
+ >
+ <include layout="@layout/notification_2025_template_header" />
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:paddingStart="@dimen/notification_2025_content_margin_start"
+ android:paddingEnd="@dimen/notification_content_margin_end"
+ android:layout_weight="1"
+ android:clipToPadding="false"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_progress"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_progress_bar_height"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ android:layout_marginBottom="2dp"/>
+ <TextView android:id="@+id/inbox_text0"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text1"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text2"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text3"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text4"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text5"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ <TextView android:id="@+id/inbox_text6"
+ style="@style/Widget.DeviceDefault.Notification.Text"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:visibility="gone"
+ android:layout_weight="1"
+ />
+ </LinearLayout>
+ <ViewStub android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_2025_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin" />
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+ <include layout="@layout/notification_template_right_icon" />
+</FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
new file mode 100644
index 0000000..578a0b2
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -0,0 +1,103 @@
+<?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
+ -->
+
+<!-- Note: This is the expanded version of the old media style notification (different from UMO). -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.MediaNotificationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:tag="bigMediaNarrow"
+ >
+
+ <include layout="@layout/notification_2025_template_header" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/notification_media_content"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ >
+ <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_template_text"/>
+ </LinearLayout>
+
+ <!-- this FrameLayout's minHeight serves as a padding for the content above -->
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_media_actions_margin_start"
+ android:minHeight="@dimen/notification_content_margin"
+ >
+
+ <!-- Nesting in FrameLayout is required to ensure that the marginStart actually applies
+ at the start instead of always the left, given that the media_actions LinearLayout
+ has layoutDirection="ltr". -->
+ <LinearLayout
+ android:id="@+id/media_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/media_notification_actions_padding_bottom"
+ android:gravity="top"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"
+ >
+
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action0"
+ />
+
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action1"
+ />
+
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action2"
+ />
+
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action3"
+ />
+
+ <include
+ layout="@layout/notification_material_media_action"
+ android:id="@+id/action4"
+ />
+ </LinearLayout>
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+
+</com.android.internal.widget.MediaNotificationView>
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
new file mode 100644
index 0000000..5b58726
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -0,0 +1,71 @@
+<?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
+ -->
+
+<!-- extends FrameLayout -->
+<com.android.internal.widget.MessagingLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:tag="messaging"
+ >
+
+ <include layout="@layout/notification_2025_template_header"/>
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:clipChildren="false"
+ android:orientation="vertical">
+
+ <com.android.internal.widget.RemeasuringLinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ >
+ <com.android.internal.widget.MessagingLinearLayout
+ android:id="@+id/notification_messaging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:spacing="@dimen/notification_messaging_spacing" />
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end" />
+
+ <include layout="@layout/notification_material_action_list" />
+
+ </com.android.internal.widget.RemeasuringLinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+
+</com.android.internal.widget.MessagingLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
new file mode 100644
index 0000000..afa4bc6
--- /dev/null
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -0,0 +1,122 @@
+<?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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:tag="progress"
+ >
+
+ <LinearLayout
+ android:id="@+id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ >
+
+ <include layout="@layout/notification_2025_template_header" />
+
+ <LinearLayout
+ android:id="@+id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin_top"
+ android:orientation="vertical"
+ >
+
+ <include layout="@layout/notification_template_part_line1" />
+
+ <include layout="@layout/notification_template_text_multiline" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:layout_marginTop="@dimen/notification_progress_margin_top"
+ android:orientation="horizontal">
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/notification_progress_start_icon"
+ android:layout_width="@dimen/notification_progress_icon_size"
+ android:layout_height="@dimen/notification_progress_icon_size"
+ android:background="@drawable/notification_progress_icon_background"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:layout_marginEnd="@dimen/notification_progress_margin_horizontal"
+ android:scaleType="centerCrop"
+ android:maxDrawableWidth="@dimen/notification_progress_icon_size"
+ android:maxDrawableHeight="@dimen/notification_progress_icon_size"
+ />
+
+
+ <include
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="@dimen/notification_progress_tracker_height"
+ layout="@layout/notification_template_notification_progress_bar"
+ />
+
+ <com.android.internal.widget.CachingIconView
+ android:id="@+id/notification_progress_end_icon"
+ android:layout_width="@dimen/notification_progress_icon_size"
+ android:layout_height="@dimen/notification_progress_icon_size"
+ android:background="@drawable/notification_progress_icon_background"
+ android:clipToOutline="true"
+ android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
+ android:layout_marginStart="@dimen/notification_progress_margin_horizontal"
+ android:maxDrawableWidth="@dimen/notification_progress_icon_size"
+ android:maxDrawableHeight="@dimen/notification_progress_icon_size"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/notification_template_right_icon" />
+ </FrameLayout>
+
+ <ViewStub
+ android:layout="@layout/notification_material_reply_text"
+ android:id="@+id/notification_2025_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/notification_2025_content_margin_start"
+ android:layout_marginEnd="@dimen/notification_content_margin_end"
+ android:layout_marginTop="@dimen/notification_content_margin"
+ />
+
+ <include layout="@layout/notification_material_action_list" />
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/values-watch-v36/dimens_material.xml b/core/res/res/values-watch-v36/dimens_material.xml
index ffa3b9c..7232786 100644
--- a/core/res/res/values-watch-v36/dimens_material.xml
+++ b/core/res/res/values-watch-v36/dimens_material.xml
@@ -31,4 +31,9 @@
<!-- Opacity factor for disabled material3 widget -->
<dimen name="disabled_alpha_device_default">0.12</dimen>
<dimen name="primary_content_alpha_device_default">0.38</dimen>
+
+ <!-- values for material3 progress bar(progress indicator) -->
+ <item name="progressbar_inner_radius_ratio" format="float" type="dimen">2.12</item>
+ <dimen name="progressbar_thickness">8dp</dimen>
+ <dimen name="progressbar_elevation">0.1dp</dimen>
</resources>
diff --git a/core/res/res/values-watch-v36/styles_material.xml b/core/res/res/values-watch-v36/styles_material.xml
index 7da7435..fc9f669 100644
--- a/core/res/res/values-watch-v36/styles_material.xml
+++ b/core/res/res/values-watch-v36/styles_material.xml
@@ -57,6 +57,10 @@
</style>
<!-- AlertDialog Styles -->
+ <style name="AlertDialog.DeviceDefault.WearMaterial3">
+ <item name="layout">@layout/alert_dialog_wear_material3</item>
+ </style>
+
<style name="Widget.DeviceDefault.Button.ButtonBar.AlertDialog.WearMaterial3" parent="Widget.DeviceDefault.Button">
<item name="android:textSize">0sp</item>
<item name="android:gravity">center</item>
@@ -79,4 +83,13 @@
<item name="maxWidth">@dimen/dialog_btn_negative_width</item>
<item name="maxHeight">@dimen/dialog_btn_negative_height</item>
</style>
+
+ <!-- Wear Material3 Progress Bar style: progressed ring.-->
+ <style name="Widget.DeviceDefault.ProgressBar.WearMaterial3">
+ <item name="indeterminateOnly">false</item>
+ <item name="progressDrawable">@drawable/progress_ring_wear_material3</item>
+ <item name="minHeight">@dimen/progress_bar_height</item>
+ <item name="maxHeight">@dimen/progress_bar_height</item>
+ <item name="mirrorForRtl">true</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0d13ca8..01c2e9c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2451,6 +2451,9 @@
<string name="config_systemCallStreaming" translatable="false"></string>
<!-- The name of the package that will hold the default retail demo role. -->
<string name="config_defaultRetailDemo" translatable="false"></string>
+ <!-- The name of the package that will hold the default reserved for testing profile group
+ exclusivity role. -->
+ <string name="config_defaultReservedForTestingProfileGroupExclusivity" translatable="false">android.app.rolemultiuser.cts.app</string>
<!-- The component name of the wear service class that will be started by the system server. -->
<string name="config_wearServiceComponent" translatable="false"></string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index c8df662..f53acbf 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -255,6 +255,19 @@
This represents 16dp for the left margin + 40dp for the icon + 16dp for the right margin -->
<dimen name="notification_2025_content_margin_start">72dp</dimen>
+ <!-- The margin on the start of the media actions, selected to ensure that action icons which
+ are visually 12x12 in a 24x24 drawable will align correctly with the text. This means that
+ stock media action icons will align, but icons may be visually up to 20x20 and remain in-spec,
+ in which case they will protrude into the start column slightly.
+ 72dp (content margin) - 8dp (media action padding) - 6dp (visual padding within drawable) -->
+ <dimen name="notification_2025_media_actions_margin_start">58dp</dimen>
+
+ <!-- The margin on the start of notification actions (2025 redesign version), to align them to
+ the rest of the notification content. Note that this can be set to 0 if the actions would not
+ fit with it included.
+ 72dp (content margin) - 12dp (action padding) - 4dp (button inset) -->
+ <dimen name="notification_2025_actions_margin_start">56dp</dimen>
+
<!-- The margin on the end of most content views (ignores the expander) -->
<dimen name="notification_content_margin_end">16dp</dimen>
@@ -377,6 +390,9 @@
<!-- the size of the notification close button -->
<dimen name="notification_close_button_size">16dp</dimen>
+ <!-- Margin for all notification content -->
+ <dimen name="notification_2025_margin">16dp</dimen>
+
<!-- Vertical margin for the headerless notification content, when content has 1 line -->
<!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
<dimen name="notification_headerless_margin_oneline">16dp</dimen>
@@ -388,10 +404,19 @@
<!-- The height of each of the 1 or 2 lines in the headerless notification template -->
<dimen name="notification_headerless_line_height">24dp</dimen>
- <!-- vertical margin for the headerless notification content -->
+ <!-- The minimum height of the notification content (even when there's only one line of text) -->
+ <dimen name="notification_2025_content_min_height">40dp</dimen>
+
+ <!-- Height of a headerless notification with one or two lines -->
+ <!-- 16 * 2 (margins) + 40 (min content height) = 72 (notification) -->
+ <dimen name="notification_2025_min_height">72dp</dimen>
+
+ <!-- Height of a headerless notification with one line -->
+ <!-- 16 * 2 (margins) + 24 (1 line) = 56 (notification) -->
<dimen name="notification_headerless_min_height">56dp</dimen>
- <!-- Height of a small notification in the status bar -->
+ <!-- Height of a small two-line notification -->
+ <!-- 20 * 2 (margins) + 24 * 2 (2 lines) = 88 (notification) -->
<dimen name="notification_min_height">88dp</dimen>
<!-- The width of the big icons in notifications. -->
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 76ff565..0701128 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -151,6 +151,9 @@
<!-- @FlaggedApi(android.content.pm.Flags.FLAG_SDK_DEPENDENCY_INSTALLER)
@hide @SystemApi -->
<public name="config_systemDependencyInstaller" />
+ <!-- @FlaggedApi(android.permission.flags.Flags.FLAG_CROSS_USER_ROLE_PLATFORM_API_ENABLED)
+ @hide @SystemApi -->
+ <public name="config_defaultReservedForTestingProfileGroupExclusivity" />
</staging-public-group>
<staging-public-group type="dimen" first-id="0x01b30000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4f029cd..380b297 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2396,6 +2396,12 @@
<java-symbol type="layout" name="notification_2025_template_header" />
<java-symbol type="layout" name="notification_2025_template_collapsed_messaging" />
<java-symbol type="layout" name="notification_2025_template_collapsed_media" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_big_picture" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_inbox" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_media" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_big_text" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_messaging" />
+ <java-symbol type="layout" name="notification_2025_template_expanded_progress" />
<java-symbol type="layout" name="notification_template_material_base" />
<java-symbol type="layout" name="notification_template_material_heads_up_base" />
<java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
@@ -2407,6 +2413,8 @@
<java-symbol type="layout" name="notification_template_material_big_media" />
<java-symbol type="layout" name="notification_template_material_big_text" />
<java-symbol type="layout" name="notification_template_material_progress" />
+ <java-symbol type="layout" name="notification_template_material_messaging" />
+ <java-symbol type="layout" name="notification_template_material_big_messaging" />
<java-symbol type="layout" name="notification_template_header" />
<java-symbol type="layout" name="notification_material_media_action" />
<java-symbol type="color" name="notification_progress_background_color" />
@@ -3346,8 +3354,6 @@
<java-symbol type="bool" name="config_strongAuthRequiredOnBoot" />
<java-symbol type="layout" name="app_anr_dialog" />
- <java-symbol type="layout" name="notification_template_material_messaging" />
- <java-symbol type="layout" name="notification_template_material_big_messaging" />
<java-symbol type="id" name="aerr_wait" />
@@ -3475,6 +3481,7 @@
<java-symbol type="bool" name="config_supportPreRebootSecurityLogs" />
+ <java-symbol type="dimen" name="notification_2025_actions_margin_start"/>
<java-symbol type="id" name="notification_action_list_margin_target" />
<java-symbol type="dimen" name="notification_actions_padding_start"/>
<java-symbol type="dimen" name="notification_actions_collapsed_priority_width"/>
@@ -5817,5 +5824,8 @@
<!-- Style for Wear Material3 Button. Will only be used for sdk 36 or above. -->
<java-symbol type="style" name="Widget.DeviceDefault.Button.WearMaterial3" />
+ <!-- Style for Wear Material3 AlertDialog. Will only be used for sdk 36 or above. -->
+ <java-symbol type="style" name="AlertDialog.DeviceDefault.WearMaterial3" />
+
<java-symbol type="bool" name="config_allowNormalBrightnessForDozePolicy" />
</resources>
diff --git a/core/res/res/xml/bookmarks.xml b/core/res/res/xml/bookmarks.xml
index 17860ef..e735784 100644
--- a/core/res/res/xml/bookmarks.xml
+++ b/core/res/res/xml/bookmarks.xml
@@ -20,10 +20,14 @@
Typical shortcuts (not necessarily defined here):
'b': Browser
- 'p': Contacts
+ 'c': Contacts
'e': Email
- 'c': Calendar
+ 'g': GMail
+ 'k': Calendar
'm': Maps
+ 'p': Music
+ 's': SMS
+ 't': Talk
'u': Calculator
'y': YouTube
-->
@@ -34,7 +38,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_P"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -42,13 +46,21 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_K"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
+ category="android.intent.category.APP_MUSIC"
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
+ <bookmark
+ role="android.app.role.SMS"
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
+ <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
diff --git a/core/tests/FileSystemUtilsTest/Android.bp b/core/tests/FileSystemUtilsTest/Android.bp
index ae04aa4..962ff3c 100644
--- a/core/tests/FileSystemUtilsTest/Android.bp
+++ b/core/tests/FileSystemUtilsTest/Android.bp
@@ -17,14 +17,40 @@
default_team: "trendy_team_android_kernel",
}
-cc_library {
- name: "libpunchtest",
+cc_defaults {
+ name: "libpunch_defaults",
stl: "none",
host_supported: true,
srcs: ["jni/android_test_jni_source.cpp"],
header_libs: ["jni_headers"],
}
+cc_library {
+ name: "libpunchtest",
+ defaults: ["libpunch_defaults"],
+}
+
+cc_library {
+ name: "libpunchtest_4kb",
+ defaults: ["libpunch_defaults"],
+ ldflags: ["-z max-page-size=0x1000"],
+}
+
+android_test_helper_app {
+ name: "app_with_4kb_elf",
+ srcs: ["app_with_4kb_elf/src/**/*.java"],
+ manifest: "app_with_4kb_elf/app_with_4kb_elf.xml",
+ compile_multilib: "64",
+ jni_libs: [
+ "libpunchtest_4kb",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "platform-test-annotations",
+ ],
+ use_embedded_native_libs: true,
+}
+
android_test_helper_app {
name: "embedded_native_libs_test_app",
srcs: ["apk_embedded_native_libs/src/**/*.java"],
@@ -72,6 +98,7 @@
device_common_data: [
":embedded_native_libs_test_app",
":extract_native_libs_test_app",
+ ":app_with_4kb_elf",
],
test_suites: ["general-tests"],
test_config: "AndroidTest.xml",
diff --git a/core/tests/FileSystemUtilsTest/AndroidTest.xml b/core/tests/FileSystemUtilsTest/AndroidTest.xml
index 27f49b2..651a7ca 100644
--- a/core/tests/FileSystemUtilsTest/AndroidTest.xml
+++ b/core/tests/FileSystemUtilsTest/AndroidTest.xml
@@ -22,6 +22,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="embedded_native_libs_test_app.apk" />
<option name="test-file-name" value="extract_native_libs_test_app.apk" />
+ <option name="test-file-name" value="app_with_4kb_elf.apk" />
</target_preparer>
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
new file mode 100644
index 0000000..b9d6d4d
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/app_with_4kb_elf.xml
@@ -0,0 +1,37 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.pagesizecompat">
+ <application
+ android:extractNativeLibs="false"
+ android:pageSizeCompat="enabled">
+ <uses-library android:name="android.test.runner"/>
+ <activity android:name=".MainActivity"
+ android:exported="true"
+ android:process=":NewProcess">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.pagesizecompat"/>
+</manifest>
\ No newline at end of file
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
new file mode 100644
index 0000000..893f9cd
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/MainActivity.java
@@ -0,0 +1,60 @@
+/*
+ * 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.test.pagesizecompat;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.VisibleForTesting;
+
+public class MainActivity extends Activity {
+
+ static {
+ System.loadLibrary("punchtest_4kb");
+ }
+
+ @VisibleForTesting
+ static final String INTENT_TYPE = "android.test.pagesizecompat.EMBEDDED_4KB_LIB_LOADED";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_1 = "OP1";
+
+ @VisibleForTesting
+ static final String KEY_OPERAND_2 = "OP2";
+
+ @VisibleForTesting
+ static final String KEY_RESULT = "RESULT";
+
+ @Override
+ public void onCreate(Bundle savedOnstanceState) {
+ super.onCreate(savedOnstanceState);
+
+ Intent received = getIntent();
+ int op1 = received.getIntExtra(KEY_OPERAND_1, -1);
+ int op2 = received.getIntExtra(KEY_OPERAND_2, -1);
+ int result = add(op1, op2);
+
+ // Send broadcast so that test can know app has launched and lib is loaded
+ // attach result which has been fetched from JNI lib
+ Intent intent = new Intent(INTENT_TYPE);
+ intent.putExtra(KEY_RESULT, result);
+ sendBroadcast(intent);
+ }
+
+ private native int add(int op1, int op2);
+}
diff --git a/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
new file mode 100644
index 0000000..9cbe414a
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/app_with_4kb_elf/src/android/test/pagesizecompat/PageSizeCompatTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.test.pagesizecompat;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class PageSizeCompatTest {
+
+ @Test
+ public void testPageSizeCompat_embedded4KbLib() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ CountDownLatch receivedSignal = new CountDownLatch(1);
+
+ // Test app is expected to receive this and perform addition of operands using ELF
+ // loaded in compat mode on 16 KB device
+ int op1 = 48;
+ int op2 = 75;
+ IntentFilter intentFilter = new IntentFilter(MainActivity.INTENT_TYPE);
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ receivedSignal.countDown();
+ int result = intent.getIntExtra(MainActivity.KEY_RESULT, 1000);
+ Assert.assertEquals(result, op1 + op2);
+
+ }
+ };
+ context.registerReceiver(broadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED);
+
+ Intent launchIntent =
+ context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_1, op1);
+ launchIntent.putExtra(MainActivity.KEY_OPERAND_2, op2);
+ context.startActivity(launchIntent);
+
+ Assert.assertTrue("Failed to launch app", receivedSignal.await(10, TimeUnit.SECONDS));
+ }
+}
diff --git a/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
index 2a5ba81..5bcd0b6 100644
--- a/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
+++ b/core/tests/FileSystemUtilsTest/jni/android_test_jni_source.cpp
@@ -22,6 +22,12 @@
return op1 + op2;
}
+extern "C" JNIEXPORT
+jint JNICALL Java_android_test_pagesizecompat_MainActivity_add(JNIEnv*, jclass, jint op1, jint op2)
+{
+ return op1 + op2;
+}
+
// This will be called from extract_native_libs_test_app
extern "C" JNIEXPORT
jint JNICALL Java_android_test_extract_MainActivity_subtract(JNIEnv*, jclass, jint op1, jint op2) {
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
index 77802e5..aed907a 100644
--- a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -47,4 +47,13 @@
assertTrue(isPackageInstalled(appPackage));
runDeviceTests(appPackage, appPackage + "." + testName);
}
+
+ @Test
+ @AppModeFull
+ public void runAppWith4KbLib_overrideCompatMode() throws DeviceNotAvailableException {
+ String appPackage = "android.test.pagesizecompat";
+ String testName = "PageSizeCompatTest";
+ assertTrue(isPackageInstalled(appPackage));
+ runDeviceTests(appPackage, appPackage + "." + testName);
+ }
}
diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java
index 7bc4abd..fdfb0c3 100644
--- a/core/tests/coretests/src/android/content/IntentTest.java
+++ b/core/tests/coretests/src/android/content/IntentTest.java
@@ -19,8 +19,9 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
-import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -29,6 +30,7 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.security.Flags;
+import android.util.ArraySet;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -40,6 +42,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
/**
* Build/Install/Run:
@@ -51,7 +54,6 @@
public class IntentTest {
private static final String TEST_ACTION = "android.content.IntentTest_test";
private static final String TEST_EXTRA_NAME = "testExtraName";
- private static final Uri TEST_URI = Uri.parse("content://com.example/people");
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -129,4 +131,111 @@
}
}
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT)
+ public void testFillInCreatorTokenInfo() {
+ // case 1: intent does not have creatorTokenInfo; fillinIntent contains creatorTokenInfo
+ Intent intent = new Intent();
+ Intent fillInIntent = new Intent();
+ fillInIntent.setCreatorToken(new Binder());
+ fillInIntent.putExtra("extraKey", new Intent());
+
+ fillInIntent.collectExtraIntentKeys();
+ intent.fillIn(fillInIntent, 0);
+
+ // extra intent keys are merged
+ assertThat(intent.getExtraIntentKeys()).isEqualTo(fillInIntent.getExtraIntentKeys());
+ // but creator token is not overwritten.
+ assertThat(intent.getCreatorToken()).isNull();
+
+
+ // case 2: Both intent and fillInIntent contains creatorToken, intent's creatorToken is not
+ // overwritten.
+ intent = new Intent();
+ IBinder creatorToken = new Binder();
+ intent.setCreatorToken(creatorToken);
+ fillInIntent = new Intent();
+ fillInIntent.setCreatorToken(new Binder());
+
+ intent.fillIn(fillInIntent, 0);
+
+ assertThat(intent.getCreatorToken()).isEqualTo(creatorToken);
+
+
+ // case 3: Contains duplicate extra keys
+ intent = new Intent();
+ intent.putExtra("key1", new Intent());
+ intent.putExtra("key2", new Intent());
+ fillInIntent = new Intent();
+ fillInIntent.putExtra("key1", new Intent());
+ fillInIntent.putExtra("key3", new Intent());
+
+ intent.collectExtraIntentKeys();
+ Set originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys());
+
+ fillInIntent.collectExtraIntentKeys();
+ intent.fillIn(fillInIntent, 0);
+
+ assertThat(intent.getExtraIntentKeys()).hasSize(3);
+ assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys));
+ assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys()));
+
+
+ // case 4: Both contains a mixture of extras and clip data. NOT force to fill in clip data.
+ intent = new Intent();
+ ClipData clipData = ClipData.newIntent("clip", new Intent());
+ clipData.addItem(new ClipData.Item(new Intent()));
+ intent.setClipData(clipData);
+ intent.putExtra("key1", new Intent());
+ intent.putExtra("key2", new Intent());
+ fillInIntent = new Intent();
+ ClipData fillInClipData = ClipData.newIntent("clip", new Intent());
+ fillInClipData.addItem(new ClipData.Item(new Intent()));
+ fillInClipData.addItem(new ClipData.Item(new Intent()));
+ fillInIntent.setClipData(fillInClipData);
+ fillInIntent.putExtra("key1", new Intent());
+ fillInIntent.putExtra("key3", new Intent());
+
+ intent.collectExtraIntentKeys();
+ originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys());
+ fillInIntent.collectExtraIntentKeys();
+ intent.fillIn(fillInIntent, 0);
+
+ // size is 5 ( 3 extras merged from both + 2 clip data in the original.
+ assertThat(intent.getExtraIntentKeys()).hasSize(5);
+ // all keys from original are kept.
+ assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys));
+ // Not all keys from fillInIntent are kept - clip data keys are dropped.
+ assertFalse(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys()));
+
+
+ // case 5: Both contains a mixture of extras and clip data. Force to fill in clip data.
+ intent = new Intent();
+ clipData = ClipData.newIntent("clip", new Intent());
+ clipData.addItem(new ClipData.Item(new Intent()));
+ clipData.addItem(new ClipData.Item(new Intent()));
+ clipData.addItem(new ClipData.Item(new Intent()));
+ intent.setClipData(clipData);
+ intent.putExtra("key1", new Intent());
+ intent.putExtra("key2", new Intent());
+ fillInIntent = new Intent();
+ fillInClipData = ClipData.newIntent("clip", new Intent());
+ fillInClipData.addItem(new ClipData.Item(new Intent()));
+ fillInClipData.addItem(new ClipData.Item(new Intent()));
+ fillInIntent.setClipData(fillInClipData);
+ fillInIntent.putExtra("key1", new Intent());
+ fillInIntent.putExtra("key3", new Intent());
+
+ intent.collectExtraIntentKeys();
+ originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys());
+ fillInIntent.collectExtraIntentKeys();
+ intent.fillIn(fillInIntent, Intent.FILL_IN_CLIP_DATA);
+
+ // size is 6 ( 3 extras merged from both + 3 clip data in the fillInIntent.
+ assertThat(intent.getExtraIntentKeys()).hasSize(6);
+ // all keys from fillInIntent are kept.
+ assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys()));
+ // Not all keys from intent are kept - clip data keys are dropped.
+ assertFalse(intent.getExtraIntentKeys().containsAll(originalIntentKeys));
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTest.java b/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
index b60d614..c55008e 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTest.java
@@ -24,6 +24,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class PackageManagerTest {
@@ -46,4 +49,25 @@
public void testResolveInfoFlags() throws Exception {
assertThat(PackageManager.ResolveInfoFlags.of(42L).getValue()).isEqualTo(42L);
}
+
+ @Test
+ public void testSdkFeatureCount() throws Exception {
+ // Check to make sure the system feature `SdkConst` annotation processor yields sensible
+ // results. We don't care about the exactness, just that it's not pathologically wrong.
+ assertThat(PackageManager.SDK_FEATURE_COUNT).isGreaterThan(150);
+ assertThat(PackageManager.SDK_FEATURE_COUNT).isLessThan(500);
+ assertThat(PackageManager.SDK_FEATURE_COUNT)
+ .isWithin(50)
+ .of(getApproximateFeatureCountUsingReflection());
+ }
+
+ /* Return a ballpark estimate of the feature count using FEATURE_ field names. */
+ private static int getApproximateFeatureCountUsingReflection() {
+ return (int)
+ Arrays.stream(PackageManager.class.getFields())
+ .filter(field -> Modifier.isStatic(field.getModifiers()))
+ .filter(field -> Modifier.isFinal(field.getModifiers()))
+ .filter(field -> field.getName().startsWith("FEATURE_"))
+ .count();
+ }
}
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
index 549e666..30f6636 100644
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -26,262 +26,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@Suppress // Failing.
@RunWith(AndroidJUnit4.class)
public class MessageQueueTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
- private static class BaseTestHandler extends TestHandlerThread {
- Handler mHandler;
- int mLastMessage;
- int mCount;
-
- public BaseTestHandler() {
- }
-
- public void go() {
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- BaseTestHandler.this.handleMessage(msg);
- }
- };
- }
-
- public void handleMessage(Message msg) {
- if (!msg.isInUse()) {
- failure(new RuntimeException(
- "msg.isInuse is false, should always be true, #" + msg.what));
- }
- if (mCount <= mLastMessage) {
- if (msg.what != mCount) {
- failure(new RuntimeException(
- "Expected message #" + mCount
- + ", received #" + msg.what));
- } else if (mCount == mLastMessage) {
- success();
- }
- mCount++;
- } else {
- failure(new RuntimeException(
- "Message received after done, #" + msg.what));
- }
- }
- }
-
- @Test
- @MediumTest
- public void testMessageOrder() throws Exception {
- TestHandlerThread tester = new BaseTestHandler() {
- public void go() {
- super.go();
- long now = SystemClock.uptimeMillis() + 200;
- mLastMessage = 4;
- mCount = 0;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(2), now + 1);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now + 2);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(4), now + 2);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(0), now + 0);
- mHandler.sendMessageAtTime(mHandler.obtainMessage(1), now + 0);
- }
- };
-
- tester.doTest(1000);
- }
-
- @Test
- @MediumTest
- public void testAtFrontOfQueue() throws Exception {
- TestHandlerThread tester = new BaseTestHandler() {
- public void go() {
- super.go();
- long now = SystemClock.uptimeMillis() + 200;
- mLastMessage = 3;
- mCount = 0;
- mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now);
- mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(2));
- mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0));
- }
-
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == 0) {
- mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(1));
- }
- }
- };
-
- tester.doTest(1000);
- }
-
- private static class TestFieldIntegrityHandler extends TestHandlerThread {
- Handler mHandler;
- int mLastMessage;
- int mCount;
-
- public TestFieldIntegrityHandler() {
- }
-
- public void go() {
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- TestFieldIntegrityHandler.this.handleMessage(msg);
- }
- };
- }
-
- public void handleMessage(Message msg) {
- if (!msg.isInUse()) {
- failure(new RuntimeException(
- "msg.isInuse is false, should always be true, #" + msg.what));
- }
- if (mCount <= mLastMessage) {
- if (msg.what != mCount) {
- failure(new RuntimeException(
- "Expected message #" + mCount
- + ", received #" + msg.what));
- } else if (mCount == mLastMessage) {
- success();
- }
- mCount++;
- } else {
- failure(new RuntimeException(
- "Message received after done, #" + msg.what));
- }
- }
- }
-
- @Test
- @MediumTest
- public void testFieldIntegrity() throws Exception {
-
- TestHandlerThread tester = new TestFieldIntegrityHandler() {
- Bundle mBundle;
-
- public void go() {
- super.go();
- mLastMessage = 1;
- mCount = 0;
- mHandler.sendMessage(mHandler.obtainMessage(0));
- }
-
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == 0) {
- msg.flags = Message.FLAGS_TO_CLEAR_ON_COPY_FROM;
- msg.what = 1;
- msg.arg1 = 456;
- msg.arg2 = 789;
- msg.obj = this;
- msg.replyTo = null;
- mBundle = new Bundle();
- msg.data = mBundle;
- msg.data.putString("key", "value");
-
- Message newMsg = mHandler.obtainMessage();
- newMsg.copyFrom(msg);
- if (newMsg.isInUse() != false) {
- failure(new RuntimeException(
- "newMsg.isInUse is true should be false after copyFrom"));
- }
- if (newMsg.flags != 0) {
- failure(new RuntimeException(String.format(
- "newMsg.flags is %d should be 0 after copyFrom", newMsg.flags)));
- }
- if (newMsg.what != 1) {
- failure(new RuntimeException(String.format(
- "newMsg.what is %d should be %d after copyFrom", newMsg.what, 1)));
- }
- if (newMsg.arg1 != 456) {
- failure(new RuntimeException(String.format(
- "newMsg.arg1 is %d should be %d after copyFrom", msg.arg1, 456)));
- }
- if (newMsg.arg2 != 789) {
- failure(new RuntimeException(String.format(
- "newMsg.arg2 is %d should be %d after copyFrom", msg.arg2, 789)));
- }
- if (newMsg.obj != this) {
- failure(new RuntimeException(
- "newMsg.obj should be 'this' after copyFrom"));
- }
- if (newMsg.replyTo != null) {
- failure(new RuntimeException(
- "newMsg.replyTo should be null after copyFrom"));
- }
- if (newMsg.data == mBundle) {
- failure(new RuntimeException(
- "newMsg.data should NOT be mBundle after copyFrom"));
- }
- if (!newMsg.data.getString("key").equals(mBundle.getString("key"))) {
- failure(new RuntimeException(String.format(
- "newMsg.data.getString(\"key\") is %s and does not equal" +
- " mBundle.getString(\"key\") which is %s after copyFrom",
- newMsg.data.getString("key"), mBundle.getString("key"))));
- }
- if (newMsg.when != 0) {
- failure(new RuntimeException(String.format(
- "newMsg.when is %d should be 0 after copyFrom", newMsg.when)));
- }
- if (newMsg.target != mHandler) {
- failure(new RuntimeException(
- "newMsg.target is NOT mHandler after copyFrom"));
- }
- if (newMsg.callback != null) {
- failure(new RuntimeException(
- "newMsg.callback is NOT null after copyFrom"));
- }
-
- mHandler.sendMessage(newMsg);
- } else if (msg.what == 1) {
- if (msg.isInUse() != true) {
- failure(new RuntimeException(String.format(
- "msg.isInUse is false should be true after when processing %d",
- msg.what)));
- }
- if (msg.arg1 != 456) {
- failure(new RuntimeException(String.format(
- "msg.arg1 is %d should be %d when processing # %d",
- msg.arg1, 456, msg.what)));
- }
- if (msg.arg2 != 789) {
- failure(new RuntimeException(String.format(
- "msg.arg2 is %d should be %d when processing # %d",
- msg.arg2, 789, msg.what)));
- }
- if (msg.obj != this) {
- failure(new RuntimeException(String.format(
- "msg.obj should be 'this' when processing # %d", msg.what)));
- }
- if (msg.replyTo != null) {
- failure(new RuntimeException(String.format(
- "msg.replyTo should be null when processing # %d", msg.what)));
- }
- if (!msg.data.getString("key").equals(mBundle.getString("key"))) {
- failure(new RuntimeException(String.format(
- "msg.data.getString(\"key\") is %s and does not equal" +
- " mBundle.getString(\"key\") which is %s when processing # %d",
- msg.data.getString("key"), mBundle.getString("key"), msg.what)));
- }
- if (msg.when != 0) {
- failure(new RuntimeException(String.format(
- "msg.when is %d should be 0 when processing # %d",
- msg.when, msg.what)));
- }
- if (msg.target != null) {
- failure(new RuntimeException(String.format(
- "msg.target is NOT null when processing # %d", msg.what)));
- }
- if (msg.callback != null) {
- failure(new RuntimeException(String.format(
- "msg.callback is NOT null when processing # %d", msg.what)));
- }
- } else {
- failure(new RuntimeException(String.format(
- "Unexpected msg.what is %d" + msg.what)));
- }
- }
- };
-
- tester.doTest(1000);
- }
}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index e4e965f..b8d1979 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -30,11 +30,10 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.InstrumentationRegistry;
@@ -54,7 +53,7 @@
import java.util.concurrent.Executors;
@RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = PowerManager.class)
+@DisabledOnRavenwood(blockedBy = PowerManager.class)
public class PowerManagerTest {
private static final String TAG = "PowerManagerTest";
@@ -83,19 +82,14 @@
String[] keys, String[] values);
static {
- if (!RavenwoodRule.isUnderRavenwood()) {
+ if (!RavenwoodRule.isOnRavenwood()) {
System.loadLibrary("powermanagertest_jni");
}
}
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
- ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
- : DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
/**
* Setup any common data for the upcoming tests.
diff --git a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
index 58a434a..a04a662 100644
--- a/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
+++ b/core/tests/coretests/src/android/os/WorkDurationUnitTest.java
@@ -18,12 +18,10 @@
import static org.junit.Assert.assertThrows;
-import android.platform.test.annotations.IgnoreUnderRavenwood;
+import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
-import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -34,16 +32,11 @@
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
-@IgnoreUnderRavenwood(blockedBy = WorkDuration.class)
+@DisabledOnRavenwood(blockedBy = WorkDuration.class)
public class WorkDurationUnitTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
// Required for RequiresFlagsEnabled and RequiresFlagsDisabled annotations to take effect.
@Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
- ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
- : DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setUp() {
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index dcaab8e..897fc54 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -607,8 +607,6 @@
<!-- Permission required for CTS test - IntrusionDetectionManagerTest -->
<permission name="android.permission.READ_INTRUSION_DETECTION_STATE" />
<permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" />
- <!-- Permissions required for CTS test - BugreportManagerTest -->
- <permission name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index 6339a87..087378b 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -4,7 +4,6 @@
# Framework-specific renames.
rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
-rule com.android.server.vcn.util.** com.android.server.vcn.repackaged.util.@1
# for modules-utils-build dependency
rule com.android.modules.utils.build.** android.internal.modules.utils.build.@1
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
index 5a2a723..f9f43bc 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
@@ -85,6 +85,9 @@
public @interface SplitIndex {
}
+ /** Signifies that user is currently not in split screen. */
+ public static final int NOT_IN_SPLIT = -1;
+
/**
* A snap target for two apps, where the split is 33-66. With FLAG_ENABLE_FLEXIBLE_SPLIT,
* only used on tablets.
@@ -152,6 +155,23 @@
public @interface PersistentSnapPosition {}
/**
+ * These are all the valid "states" that split screen can be in. It's the set of
+ * {@link PersistentSnapPosition} + {@link #NOT_IN_SPLIT}.
+ */
+ @IntDef(value = {
+ NOT_IN_SPLIT,
+ SNAP_TO_2_33_66,
+ SNAP_TO_2_50_50,
+ SNAP_TO_2_66_33,
+ SNAP_TO_2_90_10,
+ SNAP_TO_2_10_90,
+ SNAP_TO_3_33_33_33,
+ SNAP_TO_3_45_45_10,
+ SNAP_TO_3_10_45_45,
+ })
+ public @interface SplitScreenState {}
+
+ /**
* Checks if the snapPosition in question is a {@link PersistentSnapPosition}.
*/
public static boolean isPersistentSnapPosition(@SnapPosition int snapPosition) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 12d20bf..f532be6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -96,14 +96,6 @@
}
/**
- * Get all the displays from DisplayManager.
- */
- public Display[] getDisplays() {
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- return displayManager.getDisplays();
- }
-
- /**
* Gets the DisplayLayout associated with a display.
*/
public @Nullable DisplayLayout getDisplayLayout(int displayId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 38087c0..38b8592 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -403,8 +403,11 @@
@Override
// TODO(b/335404678): pass control target
- public void setImeInputTargetRequestedVisibility(boolean visible) {
+ public void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_DISPLAY_IME_CONTROLLER_SET_IME_REQUESTED_VISIBLE);
mImeRequestedVisible = visible;
dispatchImeRequested(mDisplayId, mImeRequestedVisible);
@@ -414,21 +417,21 @@
// therefore have to start the show animation from here
startAnimation(mImeRequestedVisible /* show */, false /* forceRestart */);
- setVisibleDirectly(mImeRequestedVisible || mAnimation != null);
+ setVisibleDirectly(mImeRequestedVisible || mAnimation != null, statsToken);
}
}
/**
* Sends the local visibility state back to window manager. Needed for legacy adjustForIme.
*/
- private void setVisibleDirectly(boolean visible) {
+ private void setVisibleDirectly(boolean visible, @Nullable ImeTracker.Token statsToken) {
mInsetsState.setSourceVisible(InsetsSource.ID_IME, visible);
mRequestedVisibleTypes = visible
? mRequestedVisibleTypes | WindowInsets.Type.ime()
: mRequestedVisibleTypes & ~WindowInsets.Type.ime();
try {
mWmService.updateDisplayWindowRequestedVisibleTypes(mDisplayId,
- mRequestedVisibleTypes);
+ mRequestedVisibleTypes, statsToken);
} catch (RemoteException e) {
}
}
@@ -640,7 +643,7 @@
t.hide(animatingLeash);
removeImeSurface(mDisplayId);
if (android.view.inputmethod.Flags.refactorInsetsController()) {
- setVisibleDirectly(false /* visible */);
+ setVisibleDirectly(false /* visible */, statsToken);
}
ImeTracker.forLogging().onHidden(mStatsToken);
} else if (mAnimationDirection == DIRECTION_SHOW && !mCancelled) {
@@ -669,13 +672,13 @@
if (!android.view.inputmethod.Flags.refactorInsetsController() && !show) {
// When going away, queue up insets change first, otherwise any bounds changes
// can have a "flicker" of ime-provided insets.
- setVisibleDirectly(false /* visible */);
+ setVisibleDirectly(false /* visible */, null /* statsToken */);
}
mAnimation.start();
if (!android.view.inputmethod.Flags.refactorInsetsController() && show) {
// When showing away, queue up insets change last, otherwise any bounds changes
// can have a "flicker" of ime-provided insets.
- setVisibleDirectly(true /* visible */);
+ setVisibleDirectly(true /* visible */, null /* statsToken */);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
index c4c177c..c45f09b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.os.RemoteException;
@@ -223,13 +224,14 @@
}
}
- private void setImeInputTargetRequestedVisibility(boolean visible) {
+ private void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId);
if (listeners == null) {
return;
}
for (OnInsetsChangedListener listener : listeners) {
- listener.setImeInputTargetRequestedVisibility(visible);
+ listener.setImeInputTargetRequestedVisibility(visible, statsToken);
}
}
@@ -276,10 +278,11 @@
}
@Override
- public void setImeInputTargetRequestedVisibility(boolean visible)
+ public void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken)
throws RemoteException {
mMainExecutor.execute(() -> {
- PerDisplay.this.setImeInputTargetRequestedVisibility(visible);
+ PerDisplay.this.setImeInputTargetRequestedVisibility(visible, statsToken);
});
}
}
@@ -345,7 +348,10 @@
* Called to set the requested visibility of the IME in DisplayImeController. Invoked by
* {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget}.
* @param visible requested status of the IME
+ * @param statsToken the token tracking the current IME request
*/
- default void setImeInputTargetRequestedVisibility(boolean visible) {}
+ default void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 6beff19..1852cda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -55,6 +55,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
+import android.util.Log;
import android.view.Display;
import android.view.InsetsController;
import android.view.InsetsSource;
@@ -142,6 +143,9 @@
@ShellMainThread
private final Handler mHandler;
+ /** Singleton source of truth for the current state of split screen on this device. */
+ private final SplitState mSplitState;
+
private int mDividerWindowWidth;
private int mDividerInsets;
private int mDividerSize;
@@ -204,7 +208,8 @@
SplitLayoutHandler splitLayoutHandler,
SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks,
DisplayController displayController, DisplayImeController displayImeController,
- ShellTaskOrganizer taskOrganizer, int parallaxType, @ShellMainThread Handler handler) {
+ ShellTaskOrganizer taskOrganizer, int parallaxType, SplitState splitState,
+ @ShellMainThread Handler handler) {
mHandler = handler;
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
@@ -220,6 +225,7 @@
mTaskOrganizer = taskOrganizer;
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
+ mSplitState = splitState;
final Resources res = mContext.getResources();
mDimNonImeSide = res.getBoolean(R.bool.config_dimNonImeAttachedSide);
@@ -381,6 +387,11 @@
return mDividerSnapAlgorithm.calculateNearestSnapPosition(mDividerPosition);
}
+ /** Updates the {@link SplitState} using the current divider position. */
+ public void updateStateWithCurrentPosition() {
+ mSplitState.set(calculateCurrentSnapPosition());
+ }
+
/**
* Returns the divider position as a fraction from 0 to 1.
*/
@@ -413,7 +424,13 @@
removeTouchZones();
}
- int currentPosition = calculateCurrentSnapPosition();
+ int currentPosition = mSplitState.get();
+ // TODO (b/349828130): Can delete this warning after brief soak time.
+ if (currentPosition != calculateCurrentSnapPosition()) {
+ Log.wtf(TAG, "SplitState is " + mSplitState.get()
+ + ", expected " + calculateCurrentSnapPosition());
+ }
+
switch (currentPosition) {
case SNAP_TO_2_10_90:
case SNAP_TO_3_10_45_45:
@@ -764,7 +781,10 @@
break;
default:
flingDividerPosition(currentPosition, snapTarget.position, duration, interpolator,
- () -> setDividerPosition(snapTarget.position, true /* applyLayoutChange */));
+ () -> {
+ setDividerPosition(snapTarget.position, true /* applyLayoutChange */);
+ mSplitState.set(snapTarget.snapPosition);
+ });
break;
}
}
@@ -836,10 +856,12 @@
/** Fling divider from current position to center position. */
public void flingDividerToCenter(@Nullable Runnable finishCallback) {
- final int pos = mDividerSnapAlgorithm.getMiddleTarget().position;
+ final SnapTarget target = mDividerSnapAlgorithm.getMiddleTarget();
+ final int pos = target.position;
flingDividerPosition(getDividerPosition(), pos, FLING_ENTER_DURATION, FAST_OUT_SLOW_IN,
() -> {
setDividerPosition(pos, true /* applyLayoutChange */);
+ mSplitState.set(target.snapPosition);
if (finishCallback != null) {
finishCallback.run();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
new file mode 100644
index 0000000..71758e0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitState.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.split;
+
+import static com.android.wm.shell.shared.split.SplitScreenConstants.NOT_IN_SPLIT;
+import static com.android.wm.shell.shared.split.SplitScreenConstants.SplitScreenState;
+
+/**
+ * A class that manages the "state" of split screen. See {@link SplitScreenState} for definitions.
+ */
+public class SplitState {
+ private @SplitScreenState int mState = NOT_IN_SPLIT;
+
+ /** Updates the current state of split screen on this device. */
+ public void set(@SplitScreenState int newState) {
+ mState = newState;
+ }
+
+ /** Reports the current state of split screen on this device. */
+ public @SplitScreenState int get() {
+ return mState;
+ }
+
+ /** Sets NOT_IN_SPLIT when user exits split. */
+ public void exit() {
+ set(NOT_IN_SPLIT);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index c99d9ba8..9d4b4bb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -54,6 +54,7 @@
import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.api.CompatUIInfo;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -65,6 +66,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -194,7 +196,7 @@
private final CompatUIStatusManager mCompatUIStatusManager;
@NonNull
- private final IntPredicate mInDesktopModePredicate;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@@ -210,7 +212,7 @@
@NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
@NonNull AccessibilityManager accessibilityManager,
@NonNull CompatUIStatusManager compatUIStatusManager,
- @NonNull IntPredicate isDesktopModeEnablePredicate) {
+ @NonNull Optional<DesktopUserRepositories> desktopUserRepositories) {
mContext = context;
mShellController = shellController;
mDisplayController = displayController;
@@ -226,7 +228,7 @@
mDisappearTimeSupplier = flags -> accessibilityManager.getRecommendedTimeoutMillis(
DISAPPEAR_DELAY_MS, flags);
mCompatUIStatusManager = compatUIStatusManager;
- mInDesktopModePredicate = isDesktopModeEnablePredicate;
+ mDesktopUserRepositories = desktopUserRepositories;
shellInit.addInitCallback(this::onInit, this);
}
@@ -267,7 +269,6 @@
updateActiveTaskInfo(taskInfo);
}
-
// We're showing the first reachability education so we ignore incoming TaskInfo
// until the education flow has completed or we double tap. The double-tap
// basically cancel all the onboarding flow. We don't have to ignore events in case
@@ -865,7 +866,11 @@
}
private boolean isInDesktopMode(@Nullable TaskInfo taskInfo) {
- return taskInfo != null && Flags.skipCompatUiEducationInDesktopMode()
- && mInDesktopModePredicate.test(taskInfo.displayId);
+ if (mDesktopUserRepositories.isEmpty() || taskInfo == null) {
+ return false;
+ }
+ boolean isDesktopModeShowing = mDesktopUserRepositories.get().getCurrent()
+ .getVisibleTaskCount(taskInfo.displayId) > 0;
+ return Flags.skipCompatUiEducationInDesktopMode() && isDesktopModeShowing;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
index 523e2f5..2c52e9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxController.kt
@@ -58,7 +58,8 @@
fun updateLetterboxSurfaceBounds(
key: LetterboxKey,
transaction: Transaction,
- taskBounds: Rect
+ taskBounds: Rect,
+ activityBounds: Rect
)
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
index adb034c..771d618 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxData.kt
@@ -16,5 +16,18 @@
package com.android.wm.shell.compatui.letterbox
+import android.view.SurfaceControl
+
// The key to use for identify the letterbox sessions.
-data class LetterboxKey(val displayId: Int, val taskId: Int)
\ No newline at end of file
+data class LetterboxKey(val displayId: Int, val taskId: Int)
+
+// Encapsulates the surfaces in the multiple surfaces scenario.
+data class LetterboxSurfaces(
+ var leftSurface: SurfaceControl? = null,
+ var topSurface: SurfaceControl? = null,
+ var rightSurface: SurfaceControl? = null,
+ var bottomSurface: SurfaceControl? = null
+) : Iterable<SurfaceControl?> {
+ override fun iterator() =
+ listOf(leftSurface, topSurface, rightSurface, bottomSurface).iterator()
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
index 8b830e7..a48dc8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserver.kt
@@ -41,6 +41,8 @@
companion object {
@JvmStatic
private val TAG = "LetterboxTransitionObserver"
+ @JvmStatic
+ private val EMPTY_BOUNDS = Rect()
}
init {
@@ -86,10 +88,13 @@
startTransaction,
change.leash
)
+ val activityBounds =
+ ti.appCompatTaskInfo.topActivityLetterboxBounds ?: EMPTY_BOUNDS
updateLetterboxSurfaceBounds(
key,
startTransaction,
- taskBounds
+ taskBounds,
+ activityBounds
)
}
updateLetterboxSurfaceVisibility(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
new file mode 100644
index 0000000..5129d03
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/MultiSurfaceLetterboxController.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import android.graphics.Rect
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.dagger.WMSingleton
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_APP_COMPAT
+import javax.inject.Inject
+
+/**
+ * Component responsible for handling the lifecycle of multiple letterbox surfaces when needed.
+ */
+@WMSingleton
+class MultiSurfaceLetterboxController @Inject constructor(
+ private val letterboxBuilder: LetterboxSurfaceBuilder
+) : LetterboxController {
+
+ companion object {
+ @JvmStatic
+ private val TAG = "MultiSurfaceLetterboxController"
+ }
+
+ private val letterboxMap = mutableMapOf<LetterboxKey, LetterboxSurfaces>()
+
+ override fun createLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction,
+ parentLeash: SurfaceControl
+ ) {
+ val surfaceBuilderFn = { position: String ->
+ letterboxBuilder.createSurface(
+ transaction,
+ parentLeash,
+ "ShellLetterboxSurface-$key-$position",
+ "MultiSurfaceLetterboxController#createLetterboxSurface"
+ )
+ }
+ letterboxMap.runOnItem(key, onMissed = { k, m ->
+ m[k] = LetterboxSurfaces(
+ leftSurface = surfaceBuilderFn("Left"),
+ topSurface = surfaceBuilderFn("Top"),
+ rightSurface = surfaceBuilderFn("Right"),
+ bottomSurface = surfaceBuilderFn("Bottom"),
+ )
+ })
+ }
+
+ override fun destroyLetterboxSurface(
+ key: LetterboxKey,
+ transaction: Transaction
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.forEach { s ->
+ s.remove(transaction)
+ }
+ })
+ letterboxMap.remove(key)
+ }
+
+ override fun updateLetterboxSurfaceVisibility(
+ key: LetterboxKey,
+ transaction: Transaction,
+ visible: Boolean
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.forEach { s ->
+ s.setVisibility(transaction, visible)
+ }
+ })
+ }
+
+ override fun updateLetterboxSurfaceBounds(
+ key: LetterboxKey,
+ transaction: Transaction,
+ taskBounds: Rect,
+ activityBounds: Rect
+ ) {
+ letterboxMap.runOnItem(key, onFound = { item ->
+ item.updateSurfacesBounds(transaction, taskBounds, activityBounds)
+ })
+ }
+
+ override fun dump() {
+ ProtoLog.v(WM_SHELL_APP_COMPAT, "%s: %s", TAG, "${letterboxMap.keys}")
+ }
+
+ /*
+ * Executes [onFound] on the [LetterboxItem] if present or [onMissed] if not present.
+ */
+ private fun MutableMap<LetterboxKey, LetterboxSurfaces>.runOnItem(
+ key: LetterboxKey,
+ onFound: (LetterboxSurfaces) -> Unit = { _ -> },
+ onMissed: (
+ LetterboxKey,
+ MutableMap<LetterboxKey, LetterboxSurfaces>
+ ) -> Unit = { _, _ -> }
+ ) {
+ this[key]?.let {
+ return onFound(it)
+ }
+ return onMissed(key, this)
+ }
+
+ private fun SurfaceControl?.remove(
+ tx: Transaction
+ ) = this?.let {
+ tx.remove(this)
+ }
+
+ private fun SurfaceControl?.setVisibility(
+ tx: Transaction,
+ visible: Boolean
+ ) = this?.let {
+ tx.setVisibility(this, visible)
+ }
+
+ private fun Transaction.moveAndCrop(
+ surface: SurfaceControl,
+ rect: Rect
+ ): Transaction =
+ setPosition(surface, rect.left.toFloat(), rect.top.toFloat())
+ .setWindowCrop(
+ surface,
+ rect.width(),
+ rect.height()
+ )
+
+ private fun LetterboxSurfaces.updateSurfacesBounds(
+ tx: Transaction,
+ taskBounds: Rect,
+ activityBounds: Rect
+ ) {
+ // Update the bounds depending on the activity position.
+ leftSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, taskBounds.top, activityBounds.left, taskBounds.bottom)
+ )
+ }
+ rightSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(activityBounds.right, taskBounds.top, taskBounds.right, taskBounds.bottom)
+ )
+ }
+ topSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, taskBounds.top, taskBounds.right, activityBounds.top)
+ )
+ }
+ bottomSurface?.let { s ->
+ tx.moveAndCrop(
+ s,
+ Rect(taskBounds.left, activityBounds.bottom, taskBounds.right, taskBounds.bottom)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
index f21a727..a67f608 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterbox/SingleSurfaceLetterboxController.kt
@@ -34,7 +34,7 @@
companion object {
@JvmStatic
- private val TAG = "LetterboxController"
+ private val TAG = "SingleSurfaceLetterboxController"
}
private val letterboxMap = mutableMapOf<LetterboxKey, SurfaceControl>()
@@ -93,7 +93,8 @@
override fun updateLetterboxSurfaceBounds(
key: LetterboxKey,
transaction: Transaction,
- taskBounds: Rect
+ taskBounds: Rect,
+ activityBounds: Rect
) {
letterboxMap.runOnItem(key, onFound = { item ->
item.run {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index 33e4fd8..aebd94f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -30,6 +30,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.dagger.pip.TvPipModule;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
@@ -89,6 +90,7 @@
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -96,6 +98,6 @@
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
- mainExecutor, mainHandler, systemWindows);
+ splitState, mainExecutor, mainHandler, systemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index cb9c20e..de86b22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -72,6 +72,7 @@
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.compatui.CompatUIConfiguration;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
@@ -87,8 +88,8 @@
import com.android.wm.shell.compatui.impl.DefaultCompatUIRepository;
import com.android.wm.shell.compatui.impl.DefaultComponentIdGenerator;
import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.freeform.FreeformComponents;
@@ -138,7 +139,6 @@
import dagger.Provides;
import java.util.Optional;
-import java.util.function.IntPredicate;
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
@@ -267,7 +267,7 @@
Lazy<CompatUIShellCommandHandler> compatUIShellCommandHandler,
Lazy<AccessibilityManager> accessibilityManager,
CompatUIRepository compatUIRepository,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
@NonNull CompatUIState compatUIState,
@NonNull CompatUIComponentIdGenerator componentIdGenerator,
@NonNull CompatUIComponentFactory compatUIComponentFactory,
@@ -280,10 +280,6 @@
new DefaultCompatUIHandler(compatUIRepository, compatUIState,
componentIdGenerator, compatUIComponentFactory, mainExecutor));
}
- final IntPredicate inDesktopModePredicate =
- desktopRepository.<IntPredicate>map(modeTaskRepository -> displayId ->
- modeTaskRepository.getVisibleTaskCount(displayId) > 0)
- .orElseGet(() -> displayId -> false);
return Optional.of(
new CompatUIController(
context,
@@ -300,7 +296,7 @@
compatUIShellCommandHandler.get(),
accessibilityManager.get(),
compatUIStatusManager,
- inDesktopModePredicate));
+ desktopUserRepositories));
}
@WMSingleton
@@ -704,14 +700,14 @@
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellController,
shellCommandHandler, taskStackListener, activityTaskManager,
- desktopRepository, taskStackTransitionObserver, mainExecutor));
+ desktopUserRepositories, taskStackTransitionObserver, mainExecutor));
}
@BindsOptionalOf
@@ -867,6 +863,12 @@
return Optional.empty();
}
+ @WMSingleton
+ @Provides
+ static SplitState provideSplitState() {
+ return new SplitState();
+ }
+
//
// Starting window
//
@@ -1002,16 +1004,16 @@
@BindsOptionalOf
@DynamicOverride
- abstract DesktopRepository optionalDesktopRepository();
+ abstract DesktopUserRepositories optionalDesktopUserRepositories();
@WMSingleton
@Provides
- static Optional<DesktopRepository> provideDesktopRepository(Context context,
- @DynamicOverride Optional<Lazy<DesktopRepository>> desktopRepository) {
+ static Optional<DesktopUserRepositories> provideDesktopUserRepositories(Context context,
+ @DynamicOverride Optional<Lazy<DesktopUserRepositories>> desktopUserRepositories) {
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- return desktopRepository.flatMap((lazy) -> {
+ return desktopUserRepositories.flatMap((lazy) -> {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.of(lazy.get());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 860431a..2c46860 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.apptoweb.AssistContentRequester;
+import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -68,6 +69,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.transition.TransitionStateHolder;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxController;
@@ -87,11 +89,11 @@
import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
-import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
@@ -151,6 +153,8 @@
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
+import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
+import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
@@ -338,6 +342,13 @@
return new AdditionalSystemViewContainer.Factory();
}
+ @WMSingleton
+ @Provides
+ static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
+ @ShellMainThread @NonNull CoroutineScope mainScope) {
+ return new DefaultWindowDecorViewHostSupplier(mainScope);
+ }
+
//
// Freeform
//
@@ -363,7 +374,7 @@
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorViewModel,
@@ -375,7 +386,7 @@
context,
init,
shellTaskOrganizer,
- desktopRepository,
+ desktopUserRepositories,
desktopTasksController,
launchAdjacentController,
windowDecorViewModel,
@@ -493,6 +504,7 @@
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler) {
return new SplitScreenController(
@@ -516,6 +528,7 @@
desktopTasksController,
null /* stageCoordinator */,
multiInstanceHelper,
+ splitState,
mainExecutor,
mainHandler);
}
@@ -691,7 +704,7 @@
DesktopModeDragAndDropTransitionHandler desktopModeDragAndDropTransitionHandler,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
DragToDesktopTransitionHandler dragToDesktopTransitionHandler,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
Optional<DesktopImmersiveController> desktopImmersiveController,
DesktopModeLoggerTransitionObserver desktopModeLoggerTransitionObserver,
LaunchAdjacentController launchAdjacentController,
@@ -727,7 +740,7 @@
toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler,
desktopImmersiveController.get(),
- desktopRepository,
+ desktopUserRepositories,
recentsTransitionHandler,
multiInstanceHelper,
mainExecutor,
@@ -750,7 +763,7 @@
ShellTaskOrganizer shellTaskOrganizer,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
ReturnToDragStartAnimator returnToDragStartAnimator,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
DesktopModeEventLogger desktopModeEventLogger) {
return new DesktopTilingDecorViewModel(
context,
@@ -761,7 +774,7 @@
shellTaskOrganizer,
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
- desktopRepository,
+ desktopUserRepositories,
desktopModeEventLogger
);
}
@@ -769,10 +782,10 @@
@WMSingleton
@Provides
static Optional<TaskChangeListener> provideDesktopTaskChangeListener(
- Context context, @DynamicOverride DesktopRepository desktopRepository) {
+ Context context, @DynamicOverride DesktopUserRepositories desktopUserRepositories) {
if (ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue()
&& DesktopModeStatus.canEnterDesktopMode(context)) {
- return Optional.of(new DesktopTaskChangeListener(desktopRepository));
+ return Optional.of(new DesktopTaskChangeListener(desktopUserRepositories));
}
return Optional.empty();
}
@@ -782,7 +795,7 @@
static Optional<DesktopTasksLimiter> provideDesktopTasksLimiter(
Context context,
Transitions transitions,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer shellTaskOrganizer,
InteractionJankMonitor interactionJankMonitor,
@ShellMainThread Handler handler) {
@@ -795,7 +808,7 @@
return Optional.of(
new DesktopTasksLimiter(
transitions,
- desktopRepository,
+ desktopUserRepositories,
shellTaskOrganizer,
maxTaskLimit,
interactionJankMonitor,
@@ -809,7 +822,7 @@
Context context,
ShellInit shellInit,
Transitions transitions,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
DisplayController displayController,
ShellTaskOrganizer shellTaskOrganizer,
ShellCommandHandler shellCommandHandler) {
@@ -818,7 +831,7 @@
new DesktopImmersiveController(
shellInit,
transitions,
- desktopRepository,
+ desktopUserRepositories,
displayController,
shellTaskOrganizer,
shellCommandHandler));
@@ -857,14 +870,16 @@
InputManager inputManager,
ShellTaskOrganizer shellTaskOrganizer,
FocusTransitionObserver focusTransitionObserver,
- @ShellMainThread ShellExecutor mainExecutor) {
+ @ShellMainThread ShellExecutor mainExecutor,
+ DisplayController displayController) {
if (DesktopModeStatus.canEnterDesktopMode(context) && useKeyGestureEventHandler()
&& manageKeyGestures()
&& (Flags.enableMoveToNextDisplayShortcut()
|| Flags.enableTaskResizingKeyboardShortcuts())) {
return Optional.of(new DesktopModeKeyGestureHandler(context,
desktopModeWindowDecorViewModel, desktopTasksController,
- inputManager, shellTaskOrganizer, focusTransitionObserver, mainExecutor));
+ inputManager, shellTaskOrganizer, focusTransitionObserver,
+ mainExecutor, displayController));
}
return Optional.empty();
}
@@ -881,7 +896,7 @@
ShellCommandHandler shellCommandHandler,
IWindowManager windowManager,
ShellTaskOrganizer taskOrganizer,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
DisplayController displayController,
ShellController shellController,
DisplayInsetsController displayInsetsController,
@@ -908,7 +923,7 @@
}
return Optional.of(new DesktopModeWindowDecorViewModel(context, shellExecutor, mainHandler,
mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager,
- taskOrganizer, desktopRepository, displayController, shellController,
+ taskOrganizer, desktopUserRepositories, displayController, shellController,
displayInsetsController, syncQueue, transitions, desktopTasksController,
desktopImmersiveController.get(),
rootTaskDisplayAreaOrganizer, interactionJankMonitor, genericLinksParser,
@@ -926,7 +941,7 @@
@ShellAnimationThread ShellExecutor animExecutor,
ShellInit shellInit,
Transitions transitions,
- @DynamicOverride DesktopRepository desktopRepository) {
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories) {
if (!DesktopModeStatus.canEnterDesktopMode(context)
|| !ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
|| !Flags.enableDesktopSystemDialogsTransitions()) {
@@ -935,7 +950,7 @@
return Optional.of(
new SystemModalsTransitionHandler(
context, mainExecutor, animExecutor, shellInit, transitions,
- desktopRepository));
+ desktopUserRepositories));
}
@WMSingleton
@@ -994,16 +1009,17 @@
@WMSingleton
@Provides
@DynamicOverride
- static DesktopRepository provideDesktopRepository(
+ static DesktopUserRepositories provideDesktopUserRepositories(
Context context,
ShellInit shellInit,
DesktopPersistentRepository desktopPersistentRepository,
DesktopRepositoryInitializer desktopRepositoryInitializer,
- @ShellMainThread CoroutineScope mainScope
+ @ShellMainThread CoroutineScope mainScope,
+ UserManager userManager
) {
- return new DesktopRepository(context, shellInit, desktopPersistentRepository,
+ return new DesktopUserRepositories(context, shellInit, desktopPersistentRepository,
desktopRepositoryInitializer,
- mainScope);
+ mainScope, userManager);
}
@WMSingleton
@@ -1014,7 +1030,7 @@
ShellTaskOrganizer shellTaskOrganizer,
TaskStackListenerImpl taskStackListener,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
- @DynamicOverride DesktopRepository desktopRepository) {
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories) {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.of(
new DesktopActivityOrientationChangeHandler(
@@ -1023,7 +1039,7 @@
shellTaskOrganizer,
taskStackListener,
toggleResizeDesktopTaskTransitionHandler,
- desktopRepository));
+ desktopUserRepositories));
}
return Optional.empty();
}
@@ -1032,12 +1048,13 @@
@Provides
static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver(
Context context,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
Transitions transitions,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
+ Optional<BackAnimationController> backAnimationController,
ShellInit shellInit) {
- return desktopRepository.flatMap(
+ return desktopUserRepositories.flatMap(
repository ->
Optional.of(
new DesktopTasksTransitionObserver(
@@ -1046,6 +1063,7 @@
transitions,
shellTaskOrganizer,
desktopMixedTransitionHandler.get(),
+ backAnimationController.get(),
shellInit)));
}
@@ -1054,7 +1072,7 @@
static Optional<DesktopMixedTransitionHandler> provideDesktopMixedTransitionHandler(
Context context,
Transitions transitions,
- @DynamicOverride DesktopRepository desktopRepository,
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
FreeformTaskTransitionHandler freeformTaskTransitionHandler,
CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
Optional<DesktopImmersiveController> desktopImmersiveController,
@@ -1072,7 +1090,7 @@
new DesktopMixedTransitionHandler(
context,
transitions,
- desktopRepository,
+ desktopUserRepositories,
freeformTaskTransitionHandler,
closeDesktopTaskTransitionHandler,
desktopImmersiveController.get(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 3cd5df3..cfdfe3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -42,7 +42,7 @@
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
@@ -171,7 +171,7 @@
PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenControllerOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
- Optional<DesktopRepository> desktopRepositoryOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
DisplayController displayController,
PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
@@ -181,7 +181,7 @@
pipBoundsAlgorithm, menuPhoneController, pipAnimationController,
pipSurfaceTransactionHelper, pipTransitionController, pipParamsChangedForwarder,
splitScreenControllerOptional, pipPerfHintControllerOptional,
- desktopRepositoryOptional, rootTaskDisplayAreaOrganizer, displayController,
+ desktopUserRepositoriesOptional, rootTaskDisplayAreaOrganizer, displayController,
pipUiEventLogger, shellTaskOrganizer, mainExecutor);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 7507e04..3a99619 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -39,7 +39,7 @@
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipMotionHelper;
@@ -131,10 +131,10 @@
PipBoundsState pipBoundsState,
@ShellMainThread ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
- Optional<DesktopRepository> desktopRepositoryOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
- desktopRepositoryOptional, rootTaskDisplayAreaOrganizer);
+ desktopUserRepositoriesOptional, rootTaskDisplayAreaOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
index a16c15df..9b5a289 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
@@ -106,13 +106,13 @@
// Scale the end bounds of the window down with an anchor in the center
inset(
(startBounds.width().toFloat() * (1 - CLOSE_ANIM_SCALE) / 2).toInt(),
- (startBounds.height().toFloat() * (1 - CLOSE_ANIM_SCALE) / 2).toInt()
+ (startBounds.height().toFloat() * (1 - CLOSE_ANIM_SCALE) / 2).toInt(),
)
val offsetY =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
CLOSE_ANIM_OFFSET_Y,
- context.resources.displayMetrics
+ context.resources.displayMetrics,
)
.toInt()
offset(/* dx= */ 0, offsetY)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
index 606aa6c..6104e79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
@@ -39,7 +39,7 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
private val taskStackListener: TaskStackListenerImpl,
private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler,
- private val taskRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
) {
init {
@@ -49,15 +49,17 @@
}
private fun onInit() {
- taskStackListener.addListener(object : TaskStackListenerCallback {
- override fun onActivityRequestedOrientationChanged(
- taskId: Int,
- @ScreenOrientation requestedOrientation: Int
- ) {
- // Handle requested screen orientation changes at runtime.
- handleActivityOrientationChange(taskId, requestedOrientation)
+ taskStackListener.addListener(
+ object : TaskStackListenerCallback {
+ override fun onActivityRequestedOrientationChanged(
+ taskId: Int,
+ @ScreenOrientation requestedOrientation: Int,
+ ) {
+ // Handle requested screen orientation changes at runtime.
+ handleActivityOrientationChange(taskId, requestedOrientation)
+ }
}
- })
+ )
}
/**
@@ -77,11 +79,13 @@
private fun handleActivityOrientationChange(
taskId: Int,
- @ScreenOrientation requestedOrientation: Int
+ @ScreenOrientation requestedOrientation: Int,
) {
if (!Flags.respectOrientationChangeForUnresizeable()) return
val task = shellTaskOrganizer.getRunningTaskInfo(taskId) ?: return
- if (!isDesktopModeShowing(task.displayId) || !task.isFreeform || task.isResizeable) return
+ val taskRepository = desktopUserRepositories.current
+ val isDesktopModeShowing = taskRepository.getVisibleTaskCount(task.displayId) > 0
+ if (!isDesktopModeShowing || !task.isFreeform || task.isResizeable) return
val taskBounds = task.configuration.windowConfiguration.bounds
val taskHeight = taskBounds.height()
@@ -91,10 +95,12 @@
if (taskWidth > taskHeight) ORIENTATION_LANDSCAPE else ORIENTATION_PORTRAIT
// Non-resizeable activity requested opposite orientation.
- if (orientation == ORIENTATION_PORTRAIT
- && ActivityInfo.isFixedOrientationLandscape(requestedOrientation)
- || orientation == ORIENTATION_LANDSCAPE
- && ActivityInfo.isFixedOrientationPortrait(requestedOrientation)) {
+ if (
+ orientation == ORIENTATION_PORTRAIT &&
+ ActivityInfo.isFixedOrientationLandscape(requestedOrientation) ||
+ orientation == ORIENTATION_LANDSCAPE &&
+ ActivityInfo.isFixedOrientationPortrait(requestedOrientation)
+ ) {
val finalSize = Size(taskHeight, taskWidth)
// Use the center x as the resizing anchor point.
@@ -106,7 +112,4 @@
resizeHandler.startTransition(wct)
}
}
-
- private fun isDesktopModeShowing(displayId: Int): Boolean =
- taskRepository.getVisibleTaskCount(displayId) > 0
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt
index 83b0f84..56c50ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandler.kt
@@ -71,8 +71,7 @@
animations +=
info.changes
.filter {
- it.mode == info.type &&
- it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
+ it.mode == info.type && it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
}
.mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) }
if (animations.isEmpty()) return false
@@ -83,7 +82,7 @@
private fun createMinimizeAnimation(
change: TransitionInfo.Change,
finishTransaction: Transaction,
- onAnimFinish: (Animator) -> Unit
+ onAnimFinish: (Animator) -> Unit,
): Animator? {
val t = Transaction()
val sc = change.leash
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index ba383fa..43e8d2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -12,7 +12,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
-*/
+ */
package com.android.wm.shell.desktopmode
@@ -64,16 +64,22 @@
private fun refreshDisplayWindowingMode() {
// TODO: b/375319538 - Replace the check with a DisplayManager API once it's available.
- val isExtendedDisplayEnabled = 0 != Settings.Global.getInt(
- context.contentResolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0
- )
+ val isExtendedDisplayEnabled =
+ 0 !=
+ Settings.Global.getInt(
+ context.contentResolver,
+ DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS,
+ 0,
+ )
if (!isExtendedDisplayEnabled) {
// No action needed in mirror or projected mode.
return
}
- val hasNonDefaultDisplay = rootTaskDisplayAreaOrganizer.getDisplayIds()
- .any { displayId -> displayId != DEFAULT_DISPLAY }
+ val hasNonDefaultDisplay =
+ rootTaskDisplayAreaOrganizer.getDisplayIds().any { displayId ->
+ displayId != DEFAULT_DISPLAY
+ }
val targetDisplayWindowingMode =
if (hasNonDefaultDisplay) {
WINDOWING_MODE_FREEFORM
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index dd95273d..8e2a412 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -43,14 +43,14 @@
import java.io.PrintWriter
/**
- * A controller to move tasks in/out of desktop's full immersive state where the task
- * remains freeform while being able to take fullscreen bounds and have its App Header visibility
- * be transient below the status bar like in fullscreen immersive mode.
+ * A controller to move tasks in/out of desktop's full immersive state where the task remains
+ * freeform while being able to take fullscreen bounds and have its App Header visibility be
+ * transient below the status bar like in fullscreen immersive mode.
*/
class DesktopImmersiveController(
shellInit: ShellInit,
private val transitions: Transitions,
- private val desktopRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val displayController: DisplayController,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val shellCommandHandler: ShellCommandHandler,
@@ -60,25 +60,23 @@
constructor(
shellInit: ShellInit,
transitions: Transitions,
- desktopRepository: DesktopRepository,
+ desktopUserRepositories: DesktopUserRepositories,
displayController: DisplayController,
shellTaskOrganizer: ShellTaskOrganizer,
shellCommandHandler: ShellCommandHandler,
) : this(
shellInit,
transitions,
- desktopRepository,
+ desktopUserRepositories,
displayController,
shellTaskOrganizer,
shellCommandHandler,
- { SurfaceControl.Transaction() }
+ { SurfaceControl.Transaction() },
)
- @VisibleForTesting
- var state: TransitionState? = null
+ @VisibleForTesting var state: TransitionState? = null
- @VisibleForTesting
- val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>()
+ @VisibleForTesting val pendingExternalExitTransitions = mutableListOf<ExternalPendingExit>()
/** Whether there is an immersive transition that hasn't completed yet. */
private val inProgress: Boolean
@@ -103,21 +101,20 @@
if (inProgress) {
logV(
"Cannot start entry because transition(s) already in progress: %s",
- getRunningTransitions()
+ getRunningTransitions(),
)
return
}
- val wct = WindowContainerTransaction().apply {
- setBounds(taskInfo.token, Rect())
- }
+ val wct = WindowContainerTransaction().apply { setBounds(taskInfo.token, Rect()) }
logV("Moving task ${taskInfo.taskId} into immersive mode")
val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
- state = TransitionState(
- transition = transition,
- displayId = taskInfo.displayId,
- taskId = taskInfo.taskId,
- direction = Direction.ENTER
- )
+ state =
+ TransitionState(
+ transition = transition,
+ displayId = taskInfo.displayId,
+ taskId = taskInfo.taskId,
+ direction = Direction.ENTER,
+ )
}
/** Starts a transition to move an immersive task out of immersive. */
@@ -126,22 +123,24 @@
if (inProgress) {
logV(
"Cannot start exit because transition(s) already in progress: %s",
- getRunningTransitions()
+ getRunningTransitions(),
)
return
}
- val wct = WindowContainerTransaction().apply {
- setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
- }
+ val wct =
+ WindowContainerTransaction().apply {
+ setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
+ }
logV("Moving task %d out of immersive mode, reason: %s", taskInfo.taskId, reason)
val transition = transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ this)
- state = TransitionState(
- transition = transition,
- displayId = taskInfo.displayId,
- taskId = taskInfo.taskId,
- direction = Direction.EXIT
- )
+ state =
+ TransitionState(
+ transition = transition,
+ displayId = taskInfo.displayId,
+ taskId = taskInfo.taskId,
+ direction = Direction.EXIT,
+ )
}
/**
@@ -177,23 +176,26 @@
reason: ExitReason,
): ExitResult {
if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit
- val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId)
- ?: return ExitResult.NoExit
+ val immersiveTask =
+ desktopUserRepositories.current.getTaskInFullImmersiveState(displayId)
+ ?: return ExitResult.NoExit
if (immersiveTask == excludeTaskId) {
return ExitResult.NoExit
}
- val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask)
- ?: return ExitResult.NoExit
+ val taskInfo =
+ shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return ExitResult.NoExit
logV(
"Appending immersive exit for task: %d in display: %d for reason: %s",
- immersiveTask, displayId, reason
+ immersiveTask,
+ displayId,
+ reason,
)
wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
return ExitResult.Exit(
exitingTask = immersiveTask,
runOnTransitionStart = { transition ->
addPendingImmersiveExit(immersiveTask, displayId, transition)
- }
+ },
)
}
@@ -210,7 +212,7 @@
reason: ExitReason,
): ExitResult {
if (!Flags.enableFullyImmersiveInDesktop()) return ExitResult.NoExit
- if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) {
+ if (desktopUserRepositories.current.isTaskInFullImmersiveState(taskInfo.taskId)) {
// A full immersive task is being minimized, make sure the immersive state is broken
// (i.e. resize back to max bounds).
wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
@@ -221,20 +223,16 @@
addPendingImmersiveExit(
taskId = taskInfo.taskId,
displayId = taskInfo.displayId,
- transition = transition
+ transition = transition,
)
- }
+ },
)
}
return ExitResult.NoExit
}
-
/** Whether the [change] in the [transition] is a known immersive change. */
- fun isImmersiveChange(
- transition: IBinder,
- change: TransitionInfo.Change,
- ): Boolean {
+ fun isImmersiveChange(transition: IBinder, change: TransitionInfo.Change): Boolean {
return pendingExternalExitTransitions.any {
it.transition == transition && it.taskId == change.taskInfo?.taskId
}
@@ -242,11 +240,7 @@
private fun addPendingImmersiveExit(taskId: Int, displayId: Int, transition: IBinder) {
pendingExternalExitTransitions.add(
- ExternalPendingExit(
- taskId = taskId,
- displayId = displayId,
- transition = transition
- )
+ ExternalPendingExit(taskId = taskId, displayId = displayId, transition = transition)
)
}
@@ -255,7 +249,7 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
val state = requireState()
check(state.transition == transition) {
@@ -283,10 +277,11 @@
finishCallback: Transitions.TransitionFinishCallback,
) {
logD("animateResize for task#%d", targetTaskId)
- val change = info.changes.firstOrNull { c ->
- val taskInfo = c.taskInfo
- return@firstOrNull taskInfo != null && taskInfo.taskId == targetTaskId
- }
+ val change =
+ info.changes.firstOrNull { c ->
+ val taskInfo = c.taskInfo
+ return@firstOrNull taskInfo != null && taskInfo.taskId == targetTaskId
+ }
if (change == null) {
logD("Did not find change for task#%d to animate", targetTaskId)
startTransaction.apply()
@@ -297,9 +292,9 @@
}
/**
- * Animate an immersive change.
+ * Animate an immersive change.
*
- * As of now, both enter and exit transitions have the same animation, a veiled resize.
+ * As of now, both enter and exit transitions have the same animation, a veiled resize.
*/
fun animateResizeChange(
change: TransitionInfo.Change,
@@ -317,8 +312,7 @@
.setPosition(leash, startBounds.left.toFloat(), startBounds.top.toFloat())
.setWindowCrop(leash, startBounds.width(), startBounds.height())
.show(leash)
- onTaskResizeAnimationListener
- ?.onAnimationStart(taskId, startTransaction, startBounds)
+ onTaskResizeAnimationListener?.onAnimationStart(taskId, startTransaction, startBounds)
?: startTransaction.apply()
val updateTransaction = transactionSupplier()
ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds).apply {
@@ -340,8 +334,7 @@
.setPosition(leash, rect.left.toFloat(), rect.top.toFloat())
.setWindowCrop(leash, rect.width(), rect.height())
.apply()
- onTaskResizeAnimationListener
- ?.onBoundsChange(taskId, updateTransaction, rect)
+ onTaskResizeAnimationListener?.onBoundsChange(taskId, updateTransaction, rect)
?: updateTransaction.apply()
}
start()
@@ -350,13 +343,13 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? = null
override fun onTransitionConsumed(
transition: IBinder,
aborted: Boolean,
- finishTransaction: SurfaceControl.Transaction?
+ finishTransaction: SurfaceControl.Transaction?,
) {
val state = this.state ?: return
if (transition == state.transition && aborted) {
@@ -377,9 +370,12 @@
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
) {
+ val desktopRepository: DesktopRepository = desktopUserRepositories.current
// Check if this is a pending external exit transition.
- val pendingExit = pendingExternalExitTransitions
- .firstOrNull { pendingExit -> pendingExit.transition == transition }
+ val pendingExit =
+ pendingExternalExitTransitions.firstOrNull { pendingExit ->
+ pendingExit.transition == transition
+ }
if (pendingExit != null) {
if (info.hasTaskChange(taskId = pendingExit.taskId)) {
if (desktopRepository.isTaskInFullImmersiveState(pendingExit.taskId)) {
@@ -387,7 +383,7 @@
desktopRepository.setTaskInFullImmersiveState(
displayId = pendingExit.displayId,
taskId = pendingExit.taskId,
- immersive = false
+ immersive = false,
)
if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
desktopRepository.removeBoundsBeforeFullImmersive(pendingExit.taskId)
@@ -400,24 +396,25 @@
// Check if this is a direct immersive enter/exit transition.
if (transition == state?.transition) {
val state = requireState()
- val immersiveChange = info.changes.firstOrNull { c ->
- c.taskInfo?.taskId == state.taskId
- }
+ val immersiveChange =
+ info.changes.firstOrNull { c -> c.taskInfo?.taskId == state.taskId }
if (immersiveChange == null) {
logV(
"Direct move for task#%d in %s direction missing immersive change.",
- state.taskId, state.direction
+ state.taskId,
+ state.direction,
)
return
}
val startBounds = immersiveChange.startAbsBounds
logV("Direct move for task#%d in %s direction verified", state.taskId, state.direction)
+
when (state.direction) {
Direction.ENTER -> {
desktopRepository.setTaskInFullImmersiveState(
displayId = state.displayId,
taskId = state.taskId,
- immersive = true
+ immersive = true,
)
if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
desktopRepository.saveBoundsBeforeFullImmersive(state.taskId, startBounds)
@@ -427,7 +424,7 @@
desktopRepository.setTaskInFullImmersiveState(
displayId = state.displayId,
taskId = state.taskId,
- immersive = false
+ immersive = false,
)
if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
desktopRepository.removeBoundsBeforeFullImmersive(state.taskId)
@@ -447,31 +444,34 @@
desktopRepository.setTaskInFullImmersiveState(
displayId = c.taskInfo!!.displayId,
taskId = c.taskInfo!!.taskId,
- immersive = false
+ immersive = false,
)
}
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- val pendingExit = pendingExternalExitTransitions
- .firstOrNull { pendingExit -> pendingExit.transition == merged }
+ val pendingExit =
+ pendingExternalExitTransitions.firstOrNull { pendingExit ->
+ pendingExit.transition == merged
+ }
if (pendingExit != null) {
logV(
"Pending exit transition %s for task#%s merged into %s",
- merged, pendingExit.taskId, playing
+ merged,
+ pendingExit.taskId,
+ playing,
)
pendingExit.transition = playing
}
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- val pendingExit = pendingExternalExitTransitions
- .firstOrNull { pendingExit -> pendingExit.transition == transition }
+ val pendingExit =
+ pendingExternalExitTransitions.firstOrNull { pendingExit ->
+ pendingExit.transition == transition
+ }
if (pendingExit != null) {
- logV(
- "Pending exit transition %s for task#%s finished",
- transition, pendingExit
- )
+ logV("Pending exit transition %s for task#%s finished", transition, pendingExit)
pendingExternalExitTransitions.remove(pendingExit)
}
}
@@ -481,10 +481,11 @@
}
private fun getExitDestinationBounds(taskInfo: RunningTaskInfo): Rect {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
- ?: error("Expected non-null display layout for displayId: ${taskInfo.displayId}")
+ val displayLayout =
+ displayController.getDisplayLayout(taskInfo.displayId)
+ ?: error("Expected non-null display layout for displayId: ${taskInfo.displayId}")
return if (Flags.enableRestoreToPreviousSizeFromDesktopImmersive()) {
- desktopRepository.removeBoundsBeforeFullImmersive(taskInfo.taskId)
+ desktopUserRepositories.current.removeBoundsBeforeFullImmersive(taskInfo.taskId)
?: if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue()) {
calculateInitialBounds(displayLayout, taskInfo)
} else {
@@ -500,12 +501,8 @@
private fun getRunningTransitions(): List<IBinder> {
val running = mutableListOf<IBinder>()
- state?.let {
- running.add(it.transition)
- }
- pendingExternalExitTransitions.forEach {
- running.add(it.transition)
- }
+ state?.let { running.add(it.transition) }
+ pendingExternalExitTransitions.forEach { running.add(it.transition) }
return running
}
@@ -525,28 +522,23 @@
val transition: IBinder,
val displayId: Int,
val taskId: Int,
- val direction: Direction
+ val direction: Direction,
)
/**
* Tracks state of a transition involving an immersive exit that is external to this class' own
- * transitions. This usually means transitions that exit immersive mode as a side-effect and
- * not the primary action (for example, minimizing the immersive task or launching a new task
- * on top of the immersive task).
+ * transitions. This usually means transitions that exit immersive mode as a side-effect and not
+ * the primary action (for example, minimizing the immersive task or launching a new task on top
+ * of the immersive task).
*/
- data class ExternalPendingExit(
- val taskId: Int,
- val displayId: Int,
- var transition: IBinder,
- )
+ data class ExternalPendingExit(val taskId: Int, val displayId: Int, var transition: IBinder)
/** The result of an external exit request. */
sealed class ExitResult {
/** An immersive task exit (meaning, resize) was appended to the request. */
- data class Exit(
- val exitingTask: Int,
- val runOnTransitionStart: ((IBinder) -> Unit)
- ) : ExitResult()
+ data class Exit(val exitingTask: Int, val runOnTransitionStart: ((IBinder) -> Unit)) :
+ ExitResult()
+
/** There was no exit appended to the request. */
data object NoExit : ExitResult()
@@ -556,7 +548,8 @@
@VisibleForTesting
enum class Direction {
- ENTER, EXIT
+ ENTER,
+ EXIT,
}
/** The reason for moving the task out of desktop immersive mode. */
@@ -579,7 +572,6 @@
companion object {
private const val TAG = "DesktopImmersive"
- @VisibleForTesting
- const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L
+ @VisibleForTesting const val FULL_IMMERSIVE_ANIM_DURATION_MS = 336L
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
index 82c2ebc..33d94d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandler.kt
@@ -49,7 +49,7 @@
class DesktopMixedTransitionHandler(
private val context: Context,
private val transitions: Transitions,
- private val desktopRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler,
private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler,
private val desktopImmersiveController: DesktopImmersiveController,
@@ -61,11 +61,10 @@
) : MixedTransitionHandler, FreeformTaskTransitionStarter {
init {
- shellInit.addInitCallback ({ transitions.addHandler(this) }, this)
+ shellInit.addInitCallback({ transitions.addHandler(this) }, this)
}
- @VisibleForTesting
- val pendingMixedTransitions = mutableListOf<PendingMixedTransition>()
+ @VisibleForTesting val pendingMixedTransitions = mutableListOf<PendingMixedTransition>()
/** Delegates starting transition to [FreeformTaskTransitionHandler]. */
override fun startWindowingModeTransition(
@@ -83,7 +82,8 @@
return freeformTaskTransitionHandler.startRemoveTransition(wct)
}
requireNotNull(wct)
- return transitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this)
+ return transitions
+ .startTransition(WindowManager.TRANSIT_CLOSE, wct, /* handler= */ this)
.also { transition ->
pendingMixedTransitions.add(PendingMixedTransition.Close(transition))
}
@@ -100,8 +100,10 @@
minimizingTaskId: Int? = null,
exitingImmersiveTask: Int? = null,
): IBinder {
- if (!Flags.enableFullyImmersiveInDesktop() &&
- !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
+ if (
+ !Flags.enableFullyImmersiveInDesktop() &&
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue
+ ) {
return transitions.startTransition(transitionType, wct, /* handler= */ null)
}
if (exitingImmersiveTask == null) {
@@ -109,18 +111,21 @@
} else {
logV(
"Starting mixed launch transition for task#%d with immersive exit of task#%d",
- taskId, exitingImmersiveTask
+ taskId,
+ exitingImmersiveTask,
)
}
- return transitions.startTransition(transitionType, wct, /* handler= */ this)
- .also { transition ->
- pendingMixedTransitions.add(PendingMixedTransition.Launch(
+ return transitions.startTransition(transitionType, wct, /* handler= */ this).also {
+ transition ->
+ pendingMixedTransitions.add(
+ PendingMixedTransition.Launch(
transition = transition,
launchingTask = taskId,
minimizingTask = minimizingTaskId,
exitingImmersiveTask = exitingImmersiveTask,
- ))
- }
+ )
+ )
+ }
}
/** Notifies this handler that there is a pending transition for it to handle. */
@@ -141,36 +146,38 @@
finishTransaction: SurfaceControl.Transaction,
finishCallback: TransitionFinishCallback,
): Boolean {
- val pending = pendingMixedTransitions.find { pending -> pending.transition == transition }
- ?: return false.also {
- logV("No pending desktop transition")
- }
+ val pending =
+ pendingMixedTransitions.find { pending -> pending.transition == transition }
+ ?: return false.also { logV("No pending desktop transition") }
pendingMixedTransitions.remove(pending)
logV("Animating pending mixed transition: %s", pending)
return when (pending) {
- is PendingMixedTransition.Close -> animateCloseTransition(
- transition,
- info,
- startTransaction,
- finishTransaction,
- finishCallback
- )
- is PendingMixedTransition.Launch -> animateLaunchTransition(
- pending,
- transition,
- info,
- startTransaction,
- finishTransaction,
- finishCallback
- )
- is PendingMixedTransition.Minimize -> animateMinimizeTransition(
- pending,
- transition,
- info,
- startTransaction,
- finishTransaction,
- finishCallback
- )
+ is PendingMixedTransition.Close ->
+ animateCloseTransition(
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback,
+ )
+ is PendingMixedTransition.Launch ->
+ animateLaunchTransition(
+ pending,
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback,
+ )
+ is PendingMixedTransition.Minimize ->
+ animateMinimizeTransition(
+ pending,
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback,
+ )
}
}
@@ -216,12 +223,10 @@
finishCallback: TransitionFinishCallback,
): Boolean {
// Check if there's also an immersive change during this launch.
- val immersiveExitChange = pending.exitingImmersiveTask?.let { exitingTask ->
- findTaskChange(info, exitingTask)
- }
- val minimizeChange = pending.minimizingTask?.let { minimizingTask ->
- findTaskChange(info, minimizingTask)
- }
+ val immersiveExitChange =
+ pending.exitingImmersiveTask?.let { exitingTask -> findTaskChange(info, exitingTask) }
+ val minimizeChange =
+ pending.minimizingTask?.let { minimizingTask -> findTaskChange(info, minimizingTask) }
val launchChange = findDesktopTaskLaunchChange(info, pending.launchingTask)
if (launchChange == null) {
check(minimizeChange == null)
@@ -241,8 +246,9 @@
logV(
"Animating mixed launch transition task#%d, minimizingTask#%s immersiveExitTask#%s",
- launchChange.taskInfo!!.taskId, minimizeChange?.taskInfo?.taskId,
- immersiveExitChange?.taskInfo?.taskId
+ launchChange.taskInfo!!.taskId,
+ minimizeChange?.taskInfo?.taskId,
+ immersiveExitChange?.taskInfo?.taskId,
)
if (DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue) {
// Only apply minimize change reparenting here if we implement the new app launch
@@ -259,7 +265,7 @@
immersiveExitChange,
startTransaction,
finishTransaction,
- finishCb
+ finishCb,
)
// Let the leftover/default handler animate the remaining changes.
return dispatchToLeftoverHandler(
@@ -267,7 +273,7 @@
info,
startTransaction,
finishTransaction,
- finishCb
+ finishCb,
)
}
// There's nothing to animate separately, so let the left over handler animate
@@ -278,7 +284,7 @@
info,
startTransaction,
finishTransaction,
- finishCb
+ finishCb,
)
}
@@ -304,7 +310,7 @@
info,
startTransaction,
finishTransaction,
- finishCallback
+ finishCallback,
)
}
@@ -321,7 +327,7 @@
override fun onTransitionConsumed(
transition: IBinder,
aborted: Boolean,
- finishTransaction: SurfaceControl.Transaction?
+ finishTransaction: SurfaceControl.Transaction?,
) {
pendingMixedTransitions.removeAll { pending -> pending.transition == transition }
super.onTransitionConsumed(transition, aborted, finishTransaction)
@@ -356,7 +362,7 @@
doOnFinishCallback = {
// Finish the jank trace when closing the last window in desktop mode.
interactionJankMonitor.end(CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE)
- }
+ },
)
}
@@ -379,7 +385,10 @@
require(taskInfo.isFreeform)
logV("Reparenting minimizing task#%d", taskInfo.taskId)
rootTaskDisplayAreaOrganizer.reparentToDisplayArea(
- taskInfo.displayId, minimizeChange.leash, startTransaction)
+ taskInfo.displayId,
+ minimizeChange.leash,
+ startTransaction,
+ )
}
private fun dispatchToLeftoverHandler(
@@ -399,13 +408,13 @@
doOnFinishCallback?.invoke()
finishCallback.onTransitionFinished(wct)
},
- /* skip= */ this
+ /* skip= */ this,
) != null
}
private fun isLastDesktopTask(change: TransitionInfo.Change): Boolean =
change.taskInfo?.let {
- desktopRepository.getExpandedTaskCount(it.displayId) == 1
+ desktopUserRepositories.getProfile(it.userId).getExpandedTaskCount(it.displayId) == 1
} ?: false
private fun findCloseDesktopTaskChange(info: TransitionInfo): TransitionInfo.Change? {
@@ -423,7 +432,7 @@
private fun findDesktopTaskLaunchChange(
info: TransitionInfo,
- launchTaskId: Int?
+ launchTaskId: Int?,
): TransitionInfo.Change? {
return if (launchTaskId != null) {
// Launching a known task (probably from background or moving to front), so
@@ -432,8 +441,9 @@
} else {
// Launching a new task, so the first opening freeform task.
info.changes.firstOrNull { change ->
- change.mode == TRANSIT_OPEN
- && change.taskInfo != null && change.taskInfo!!.isFreeform
+ change.mode == TRANSIT_OPEN &&
+ change.taskInfo != null &&
+ change.taskInfo!!.isFreeform
}
}
}
@@ -451,9 +461,7 @@
abstract val transition: IBinder
/** A task is closing. */
- data class Close(
- override val transition: IBinder,
- ) : PendingMixedTransition()
+ data class Close(override val transition: IBinder) : PendingMixedTransition()
/** A task is opening or moving to front. */
data class Launch(
@@ -463,8 +471,10 @@
val exitingImmersiveTask: Int?,
) : PendingMixedTransition()
- /** A task is minimizing. This should be used for task going to back and some closing cases
- * with back navigation. */
+ /**
+ * A task is minimizing. This should be used for task going to back and some closing cases
+ * with back navigation.
+ */
data class Minimize(
override val transition: IBinder,
val minimizingTask: Int,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt
index a7a4a10..ca02c72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeDragAndDropTransitionHandler.kt
@@ -27,16 +27,14 @@
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
-/**
- * Transition handler for drag-and-drop (i.e., tab tear) transitions that occur in desktop mode.
- */
+/** Transition handler for drag-and-drop (i.e., tab tear) transitions that occur in desktop mode. */
class DesktopModeDragAndDropTransitionHandler(private val transitions: Transitions) :
Transitions.TransitionHandler {
private val pendingTransitionTokens: MutableList<IBinder> = mutableListOf()
/**
- * Begin a transition when a [android.app.PendingIntent] is dropped without a window to
- * accept it.
+ * Begin a transition when a [android.app.PendingIntent] is dropped without a window to accept
+ * it.
*/
fun handleDropEvent(wct: WindowContainerTransaction): IBinder {
val token = transitions.startTransition(TRANSIT_OPEN, wct, this)
@@ -49,29 +47,32 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: TransitionFinishCallback
+ finishCallback: TransitionFinishCallback,
): Boolean {
if (!pendingTransitionTokens.contains(transition)) return false
val change = findRelevantChange(info)
val leash = change.leash
val endBounds = change.endAbsBounds
- startTransaction.hide(leash)
+ startTransaction
+ .hide(leash)
.setWindowCrop(leash, endBounds.width(), endBounds.height())
.apply()
val animator = ValueAnimator()
animator.setFloatValues(0f, 1f)
animator.setDuration(FADE_IN_ANIMATION_DURATION)
val t = SurfaceControl.Transaction()
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator) {
- t.show(leash)
- t.apply()
- }
+ animator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator) {
+ t.show(leash)
+ t.apply()
+ }
- override fun onAnimationEnd(animation: Animator) {
- finishCallback.onTransitionFinished(null)
+ override fun onAnimationEnd(animation: Animator) {
+ finishCallback.onTransitionFinished(null)
+ }
}
- })
+ )
animator.addUpdateListener { animation: ValueAnimator ->
t.setAlpha(leash, animation.animatedFraction)
t.apply()
@@ -83,9 +84,7 @@
private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
val matchingChanges =
- info.changes.filter { c ->
- isValidTaskChange(c) && c.mode == TRANSIT_OPEN
- }
+ info.changes.filter { c -> isValidTaskChange(c) && c.mode == TRANSIT_OPEN }
if (matchingChanges.size != 1) {
throw IllegalStateException(
"Expected 1 relevant change but found: ${matchingChanges.size}"
@@ -100,7 +99,7 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? {
return null
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
index dc23128..ff6fb59 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt
@@ -35,29 +35,25 @@
import java.util.Random
import java.util.concurrent.atomic.AtomicInteger
-
/** Event logger for logging desktop mode session events */
class DesktopModeEventLogger {
private val random: Random = SecureRandom()
/** The session id for the current desktop mode session */
- @VisibleForTesting
- val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID)
+ @VisibleForTesting val currentSessionId: AtomicInteger = AtomicInteger(NO_SESSION_ID)
private fun generateSessionId() = 1 + random.nextInt(1 shl 20)
- /**
- * Logs enter into desktop mode with [enterReason]
- */
+ /** Logs enter into desktop mode with [enterReason] */
fun logSessionEnter(enterReason: EnterReason) {
val sessionId = generateSessionId()
val previousSessionId = currentSessionId.getAndSet(sessionId)
if (previousSessionId != NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: Existing desktop mode session id: %s found on desktop "
- + "mode enter",
- previousSessionId
+ "DesktopModeLogger: Existing desktop mode session id: %s found on desktop " +
+ "mode enter",
+ previousSessionId,
)
}
@@ -65,27 +61,25 @@
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session enter, session: %s reason: %s",
sessionId,
- enterReason.name
+ enterReason.name,
)
FrameworkStatsLog.write(
DESKTOP_MODE_ATOM_ID,
/* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER,
/* enterReason */ enterReason.reason,
/* exitReason */ 0,
- /* session_id */ sessionId
+ /* session_id */ sessionId,
)
EventLogTags.writeWmShellEnterDesktopMode(enterReason.reason, sessionId)
}
- /**
- * Logs exit from desktop mode session with [exitReason]
- */
+ /** Logs exit from desktop mode session with [exitReason] */
fun logSessionExit(exitReason: ExitReason) {
val sessionId = currentSessionId.getAndSet(NO_SESSION_ID)
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging exit from desktop mode"
+ "DesktopModeLogger: No session id found for logging exit from desktop mode",
)
return
}
@@ -94,27 +88,25 @@
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging session exit, session: %s reason: %s",
sessionId,
- exitReason.name
+ exitReason.name,
)
FrameworkStatsLog.write(
DESKTOP_MODE_ATOM_ID,
/* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT,
/* enterReason */ 0,
/* exitReason */ exitReason.reason,
- /* session_id */ sessionId
+ /* session_id */ sessionId,
)
EventLogTags.writeWmShellExitDesktopMode(exitReason.reason, sessionId)
}
- /**
- * Logs that a task with [taskUpdate] was added in a desktop mode session
- */
+ /** Logs that a task with [taskUpdate] was added in a desktop mode session */
fun logTaskAdded(taskUpdate: TaskUpdate) {
val sessionId = currentSessionId.get()
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging task added"
+ "DesktopModeLogger: No session id found for logging task added",
)
return
}
@@ -123,23 +115,22 @@
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task added, session: %s taskId: %s",
sessionId,
- taskUpdate.instanceId
+ taskUpdate.instanceId,
)
logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED,
- sessionId, taskUpdate
+ sessionId,
+ taskUpdate,
)
}
- /**
- * Logs that a task with [taskUpdate] was removed from a desktop mode session
- */
+ /** Logs that a task with [taskUpdate] was removed from a desktop mode session */
fun logTaskRemoved(taskUpdate: TaskUpdate) {
val sessionId = currentSessionId.get()
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging task removed"
+ "DesktopModeLogger: No session id found for logging task removed",
)
return
}
@@ -148,23 +139,22 @@
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task remove, session: %s taskId: %s",
sessionId,
- taskUpdate.instanceId
+ taskUpdate.instanceId,
)
logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED,
- sessionId, taskUpdate
+ sessionId,
+ taskUpdate,
)
}
- /**
- * Logs that a task with [taskUpdate] had it's info changed in a desktop mode session
- */
+ /** Logs that a task with [taskUpdate] had it's info changed in a desktop mode session */
fun logTaskInfoChanged(taskUpdate: TaskUpdate) {
val sessionId = currentSessionId.get()
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging task info changed"
+ "DesktopModeLogger: No session id found for logging task info changed",
)
return
}
@@ -173,11 +163,12 @@
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task info changed, session: %s taskId: %s",
sessionId,
- taskUpdate.instanceId
+ taskUpdate.instanceId,
)
logTaskUpdate(
FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED,
- sessionId, taskUpdate
+ sessionId,
+ taskUpdate,
)
}
@@ -200,30 +191,32 @@
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging start of task resizing"
+ "DesktopModeLogger: No session id found for logging start of task resizing",
)
return
}
- val taskSizeUpdate = createTaskSizeUpdate(
- resizeTrigger,
- inputMethod,
- taskInfo,
- taskWidth,
- taskHeight,
- displayController = displayController,
- displayLayoutSize = displayLayoutSize,
- )
+ val taskSizeUpdate =
+ createTaskSizeUpdate(
+ resizeTrigger,
+ inputMethod,
+ taskInfo,
+ taskWidth,
+ taskHeight,
+ displayController = displayController,
+ displayLayoutSize = displayLayoutSize,
+ )
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task resize is starting, session: %s, taskSizeUpdate: %s",
sessionId,
- taskSizeUpdate
+ taskSizeUpdate,
)
logTaskSizeUpdated(
FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__START_RESIZING_STAGE,
- sessionId, taskSizeUpdate
+ sessionId,
+ taskSizeUpdate,
)
}
@@ -245,31 +238,33 @@
if (sessionId == NO_SESSION_ID) {
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: No session id found for logging end of task resizing"
+ "DesktopModeLogger: No session id found for logging end of task resizing",
)
return
}
- val taskSizeUpdate = createTaskSizeUpdate(
- resizeTrigger,
- inputMethod,
- taskInfo,
- taskWidth,
- taskHeight,
- displayController,
- displayLayoutSize,
- )
+ val taskSizeUpdate =
+ createTaskSizeUpdate(
+ resizeTrigger,
+ inputMethod,
+ taskInfo,
+ taskWidth,
+ taskHeight,
+ displayController,
+ displayLayoutSize,
+ )
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: Logging task resize is ending, session: %s, taskSizeUpdate: %s",
sessionId,
- taskSizeUpdate
+ taskSizeUpdate,
)
logTaskSizeUpdated(
FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__RESIZING_STAGE__END_RESIZING_STAGE,
- sessionId, taskSizeUpdate
+ sessionId,
+ taskSizeUpdate,
)
}
@@ -287,12 +282,15 @@
val height = taskHeight ?: taskBounds.height()
val width = taskWidth ?: taskBounds.width()
- val displaySize = when {
- displayLayoutSize != null -> displayLayoutSize.height * displayLayoutSize.width
- displayController != null -> displayController.getDisplayLayout(taskInfo.displayId)
- ?.let { it.height() * it.width() }
- else -> null
- }
+ val displaySize =
+ when {
+ displayLayoutSize != null -> displayLayoutSize.height * displayLayoutSize.width
+ displayController != null ->
+ displayController.getDisplayLayout(taskInfo.displayId)?.let {
+ it.height() * it.width()
+ }
+ else -> null
+ }
return TaskSizeUpdate(
resizeTrigger,
@@ -316,8 +314,8 @@
taskHeight = 0,
taskWidth = 0,
taskX = 0,
- taskY = 0
- )
+ taskY = 0,
+ ),
)
}
@@ -343,7 +341,7 @@
taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON,
taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON,
/* visible_task_count */
- taskUpdate.visibleTaskCount
+ taskUpdate.visibleTaskCount,
)
EventLogTags.writeWmShellDesktopModeTaskUpdate(
/* task_event */
@@ -365,14 +363,14 @@
taskUpdate.minimizeReason?.reason ?: UNSET_MINIMIZE_REASON,
taskUpdate.unminimizeReason?.reason ?: UNSET_UNMINIMIZE_REASON,
/* visible_task_count */
- taskUpdate.visibleTaskCount
+ taskUpdate.visibleTaskCount,
)
}
private fun logTaskSizeUpdated(
resizingStage: Int,
sessionId: Int,
- taskSizeUpdate: TaskSizeUpdate
+ taskSizeUpdate: TaskSizeUpdate,
) {
FrameworkStatsLog.write(
DESKTOP_MODE_TASK_SIZE_UPDATED_ATOM_ID,
@@ -393,7 +391,7 @@
/* task_width */
taskSizeUpdate.taskWidth,
/* display_area */
- taskSizeUpdate.displayArea ?: -1
+ taskSizeUpdate.displayArea ?: -1,
)
}
@@ -410,7 +408,6 @@
* @property taskY y-coordinate of the top-left corner
* @property minimizeReason the reason the task was minimized
* @property unminimizeEvent the reason the task was unminimized
- *
*/
data class TaskUpdate(
val instanceId: Int,
@@ -425,8 +422,7 @@
)
/**
- * Describes a task size update (resizing, snapping or maximizing to
- * stable bounds).
+ * Describes a task size update (resizing, snapping or maximizing to stable bounds).
*
* @property resizeTrigger the trigger for task resize
* @property inputMethod the input method for resizing this task
@@ -450,9 +446,7 @@
fun getInputMethodFromMotionEvent(e: MotionEvent?): InputMethod {
if (e == null) return InputMethod.UNKNOWN_INPUT_METHOD
- val toolType = e.getToolType(
- e.findPointerIndex(e.getPointerId(0))
- )
+ val toolType = e.getToolType(e.findPointerIndex(e.getPointerId(0)))
return when {
toolType == TOOL_TYPE_STYLUS -> InputMethod.STYLUS
toolType == TOOL_TYPE_MOUSE -> InputMethod.MOUSE
@@ -474,8 +468,7 @@
.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT
),
MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value
- FrameworkStatsLog
- .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
+ FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON
),
}
@@ -611,20 +604,16 @@
*/
enum class InputMethod(val method: Int) {
UNKNOWN_INPUT_METHOD(
- FrameworkStatsLog
- .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
+ FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__UNKNOWN_INPUT_METHOD
),
TOUCH(
- FrameworkStatsLog
- .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__TOUCH_INPUT_METHOD
+ FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__TOUCH_INPUT_METHOD
),
STYLUS(
- FrameworkStatsLog
- .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__STYLUS_INPUT_METHOD
+ FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__STYLUS_INPUT_METHOD
),
MOUSE(
- FrameworkStatsLog
- .DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__MOUSE_INPUT_METHOD
+ FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED__INPUT_METHOD__MOUSE_INPUT_METHOD
),
TOUCHPAD(
FrameworkStatsLog
@@ -643,4 +632,4 @@
FrameworkStatsLog.DESKTOP_MODE_TASK_SIZE_UPDATED
@VisibleForTesting const val NO_SESSION_ID = 0
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 43544f6..71318cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -16,30 +16,28 @@
package com.android.wm.shell.desktopmode
-import android.hardware.input.KeyGestureEvent
-
-import android.hardware.input.InputManager
-import android.hardware.input.InputManager.KeyGestureEventHandler
-import android.os.IBinder
-import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
-import com.android.wm.shell.ShellTaskOrganizer
import android.app.ActivityManager.RunningTaskInfo
-import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
-import com.android.internal.protolog.ProtoLog
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.KeyGestureEventHandler
+import android.hardware.input.KeyGestureEvent
+import android.os.IBinder
import com.android.hardware.input.Flags.manageKeyGestures
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
-import com.android.wm.shell.transition.FocusTransitionObserver
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.FocusTransitionObserver
+import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
import java.util.Optional
-/**
- * Handles key gesture events (keyboard shortcuts) in Desktop Mode.
- */
+/** Handles key gesture events (keyboard shortcuts) in Desktop Mode. */
class DesktopModeKeyGestureHandler(
private val context: Context,
private val desktopModeWindowDecorViewModel: Optional<DesktopModeWindowDecorViewModel>,
@@ -48,24 +46,26 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
private val focusTransitionObserver: FocusTransitionObserver,
@ShellMainThread private val mainExecutor: ShellExecutor,
- ) : KeyGestureEventHandler {
+ private val displayController: DisplayController,
+) : KeyGestureEventHandler {
init {
inputManager.registerKeyGestureEventHandler(this)
}
override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean {
- if (!isKeyGestureSupported(event.keyGestureType) || !desktopTasksController.isPresent
- || !desktopModeWindowDecorViewModel.isPresent) {
+ if (
+ !isKeyGestureSupported(event.keyGestureType) ||
+ !desktopTasksController.isPresent ||
+ !desktopModeWindowDecorViewModel.isPresent
+ ) {
return false
}
when (event.keyGestureType) {
KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
getGloballyFocusedFreeformTask()?.let {
- desktopTasksController.get().moveToNextDisplay(
- it.taskId
- )
+ desktopTasksController.get().moveToNextDisplay(it.taskId)
}
return true
}
@@ -73,12 +73,14 @@
logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
mainExecutor.execute {
- desktopModeWindowDecorViewModel.get().onSnapResize(
- it.taskId,
- true,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ desktopModeWindowDecorViewModel
+ .get()
+ .onSnapResize(
+ it.taskId,
+ true,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false,
+ )
}
}
return true
@@ -87,25 +89,33 @@
logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
mainExecutor.execute {
- desktopModeWindowDecorViewModel.get().onSnapResize(
- it.taskId,
- false,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- /* fromMenu= */ false
- )
+ desktopModeWindowDecorViewModel
+ .get()
+ .onSnapResize(
+ it.taskId,
+ false,
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ /* fromMenu= */ false,
+ )
}
}
return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled")
- getGloballyFocusedFreeformTask()?.let {
+ getGloballyFocusedFreeformTask()?.let { taskInfo ->
mainExecutor.execute {
- desktopTasksController.get().toggleDesktopTaskSize(
- it,
- ResizeTrigger.MAXIMIZE_MENU,
- DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
- )
+ desktopTasksController
+ .get()
+ .toggleDesktopTaskSize(
+ taskInfo,
+ ToggleTaskSizeInteraction(
+ isMaximized = isTaskMaximized(taskInfo, displayController),
+ source = ToggleTaskSizeInteraction.Source.KEYBOARD_SHORTCUT,
+ inputMethod =
+ DesktopModeEventLogger.Companion.InputMethod.KEYBOARD,
+ ),
+ )
}
}
return true
@@ -113,11 +123,7 @@
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
getGloballyFocusedFreeformTask()?.let {
- mainExecutor.execute {
- desktopTasksController.get().minimizeTask(
- it,
- )
- }
+ mainExecutor.execute { desktopTasksController.get().minimizeTask(it) }
}
return true
}
@@ -125,16 +131,17 @@
}
}
- override fun isKeyGestureSupported(gestureType: Int): Boolean = when (gestureType) {
- KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
- -> enableMoveToNextDisplayShortcut()
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
- KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
- KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW
- -> enableTaskResizingKeyboardShortcuts() && manageKeyGestures()
- else -> false
- }
+ override fun isKeyGestureSupported(gestureType: Int): Boolean =
+ when (gestureType) {
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY ->
+ enableMoveToNextDisplayShortcut()
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW ->
+ enableTaskResizingKeyboardShortcuts() && manageKeyGestures()
+ else -> false
+ }
// TODO: b/364154795 - wait for the completion of moveToNextDisplay transition, otherwise it
// will pick a wrong task when a user quickly perform other actions with keyboard shortcuts
@@ -142,7 +149,7 @@
private fun getGloballyFocusedFreeformTask(): RunningTaskInfo? =
shellTaskOrganizer.getRunningTasks().find { taskInfo ->
taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
- focusTransitionObserver.hasGlobalFocus(taskInfo)
+ focusTransitionObserver.hasGlobalFocus(taskInfo)
}
private fun logV(msg: String, vararg arguments: Any?) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 41febdf..dfa2d9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -60,7 +60,7 @@
context: Context,
shellInit: ShellInit,
private val transitions: Transitions,
- private val desktopModeEventLogger: DesktopModeEventLogger
+ private val desktopModeEventLogger: DesktopModeEventLogger,
) : Transitions.TransitionObserver {
init {
@@ -89,7 +89,8 @@
transitions.registerObserver(this)
SystemProperties.set(
VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
- VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE)
+ VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY_DEFAULT_VALUE,
+ )
desktopModeEventLogger.logTaskInfoStateInit()
}
@@ -97,13 +98,13 @@
transition: IBinder,
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction
+ finishTransaction: SurfaceControl.Transaction,
) {
// this was a new recents animation
if (info.isExitToRecentsTransition() && tasksSavedForRecents.isEmpty()) {
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: Recents animation running, saving tasks for later"
+ "DesktopModeLogger: Recents animation running, saving tasks for later",
)
// TODO (b/326391303) - avoid logging session exit if we can identify a cancelled
// recents animation
@@ -129,7 +130,7 @@
) {
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
- "DesktopModeLogger: Canceled recents animation, restoring tasks"
+ "DesktopModeLogger: Canceled recents animation, restoring tasks",
)
// restore saved tasks in the updated set and clear for next use
postTransitionVisibleFreeformTasks += tasksSavedForRecents
@@ -140,7 +141,7 @@
identifyLogEventAndUpdateState(
transitionInfo = info,
preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos,
- postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks
+ postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks,
)
wasPreviousTransitionExitToOverview = info.isExitToRecentsTransition()
}
@@ -200,7 +201,7 @@
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopModeLogger: taskInfo map after processing changes %s",
- postTransitionFreeformTasks.size()
+ postTransitionFreeformTasks.size(),
)
return postTransitionFreeformTasks
@@ -226,7 +227,7 @@
private fun identifyLogEventAndUpdateState(
transitionInfo: TransitionInfo,
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
- postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
+ postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
) {
if (
postTransitionVisibleFreeformTasks.isEmpty() &&
@@ -236,12 +237,10 @@
// Sessions is finishing, log task updates followed by an exit event
identifyAndLogTaskUpdates(
preTransitionVisibleFreeformTasks,
- postTransitionVisibleFreeformTasks
+ postTransitionVisibleFreeformTasks,
)
- desktopModeEventLogger.logSessionExit(
- getExitReason(transitionInfo)
- )
+ desktopModeEventLogger.logSessionExit(getExitReason(transitionInfo))
isSessionActive = false
} else if (
postTransitionVisibleFreeformTasks.isNotEmpty() &&
@@ -250,19 +249,17 @@
) {
// Session is starting, log enter event followed by task updates
isSessionActive = true
- desktopModeEventLogger.logSessionEnter(
- getEnterReason(transitionInfo)
- )
+ desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo))
identifyAndLogTaskUpdates(
preTransitionVisibleFreeformTasks,
- postTransitionVisibleFreeformTasks
+ postTransitionVisibleFreeformTasks,
)
} else if (isSessionActive) {
// Session is neither starting, nor finishing, log task updates if there are any
identifyAndLogTaskUpdates(
preTransitionVisibleFreeformTasks,
- postTransitionVisibleFreeformTasks
+ postTransitionVisibleFreeformTasks,
)
}
@@ -274,11 +271,11 @@
/** Compare the old and new state of taskInfos and identify and log the changes */
private fun identifyAndLogTaskUpdates(
preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
- postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>
+ postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>,
) {
postTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
- val currentTaskUpdate = buildTaskUpdateForTask(taskInfo,
- postTransitionVisibleFreeformTasks.size())
+ val currentTaskUpdate =
+ buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())
val previousTaskInfo = preTransitionVisibleFreeformTasks[taskId]
when {
// new tasks added
@@ -287,16 +284,20 @@
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
VISIBLE_TASKS_COUNTER_NAME,
- postTransitionVisibleFreeformTasks.size().toLong()
+ postTransitionVisibleFreeformTasks.size().toLong(),
)
- SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
- postTransitionVisibleFreeformTasks.size().toString())
+ SystemProperties.set(
+ VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+ postTransitionVisibleFreeformTasks.size().toString(),
+ )
}
// old tasks that were resized or repositioned
// TODO(b/347935387): Log changes only once they are stable.
- buildTaskUpdateForTask(previousTaskInfo, postTransitionVisibleFreeformTasks.size())
- != currentTaskUpdate ->
- desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate)
+ buildTaskUpdateForTask(
+ previousTaskInfo,
+ postTransitionVisibleFreeformTasks.size(),
+ ) != currentTaskUpdate ->
+ desktopModeEventLogger.logTaskInfoChanged(currentTaskUpdate)
}
}
@@ -304,14 +305,17 @@
preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo ->
if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) {
desktopModeEventLogger.logTaskRemoved(
- buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size()))
+ buildTaskUpdateForTask(taskInfo, postTransitionVisibleFreeformTasks.size())
+ )
Trace.setCounter(
Trace.TRACE_TAG_WINDOW_MANAGER,
VISIBLE_TASKS_COUNTER_NAME,
- postTransitionVisibleFreeformTasks.size().toLong()
+ postTransitionVisibleFreeformTasks.size().toLong(),
)
- SystemProperties.set(VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
- postTransitionVisibleFreeformTasks.size().toString())
+ SystemProperties.set(
+ VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY,
+ postTransitionVisibleFreeformTasks.size().toString(),
+ )
}
}
}
@@ -332,45 +336,50 @@
/** Get [EnterReason] for this session enter */
private fun getEnterReason(transitionInfo: TransitionInfo): EnterReason {
- val enterReason = when {
- transitionInfo.type == WindowManager.TRANSIT_WAKE
- // If there is a screen lock, desktop window entry is after dismissing keyguard
- || (transitionInfo.type == WindowManager.TRANSIT_TO_BACK
- && wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON
- transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
- EnterReason.APP_HANDLE_DRAG
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
- EnterReason.APP_HANDLE_MENU_BUTTON
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
- EnterReason.APP_FROM_OVERVIEW
- transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
- EnterReason.KEYBOARD_SHORTCUT_ENTER
- // NOTE: the below condition also applies for EnterReason quickswitch
- transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
- // Enter desktop mode from cancelled recents has no transition. Enter is detected on the
- // next transition involving freeform windows.
- // TODO(b/346564416): Modify logging for cancelled recents once it transition is
- // changed. Also see how to account to time difference between actual enter time and
- // time of this log. Also account for the missed session when exit happens just after
- // a cancelled recents.
- wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
- transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
- else -> {
- ProtoLog.w(
- WM_SHELL_DESKTOP_MODE,
- "Unknown enter reason for transition type: %s",
- transitionInfo.type
- )
- EnterReason.UNKNOWN_ENTER
+ val enterReason =
+ when {
+ transitionInfo.type == WindowManager.TRANSIT_WAKE
+ // If there is a screen lock, desktop window entry is after dismissing keyguard
+ ||
+ (transitionInfo.type == WindowManager.TRANSIT_TO_BACK &&
+ wasPreviousTransitionExitByScreenOff) -> EnterReason.SCREEN_ON
+ transitionInfo.type == Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP ->
+ EnterReason.APP_HANDLE_DRAG
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON ->
+ EnterReason.APP_HANDLE_MENU_BUTTON
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW ->
+ EnterReason.APP_FROM_OVERVIEW
+ transitionInfo.type == TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT ->
+ EnterReason.KEYBOARD_SHORTCUT_ENTER
+ // NOTE: the below condition also applies for EnterReason quickswitch
+ transitionInfo.type == WindowManager.TRANSIT_TO_FRONT -> EnterReason.OVERVIEW
+ // Enter desktop mode from cancelled recents has no transition. Enter is detected on
+ // the
+ // next transition involving freeform windows.
+ // TODO(b/346564416): Modify logging for cancelled recents once it transition is
+ // changed. Also see how to account to time difference between actual enter time
+ // and
+ // time of this log. Also account for the missed session when exit happens just
+ // after
+ // a cancelled recents.
+ wasPreviousTransitionExitToOverview -> EnterReason.OVERVIEW
+ transitionInfo.type == WindowManager.TRANSIT_OPEN -> EnterReason.APP_FREEFORM_INTENT
+ else -> {
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "Unknown enter reason for transition type: %s",
+ transitionInfo.type,
+ )
+ EnterReason.UNKNOWN_ENTER
+ }
}
- }
wasPreviousTransitionExitByScreenOff = false
return enterReason
}
/** Get [ExitReason] for this session exit */
private fun getExitReason(transitionInfo: TransitionInfo): ExitReason =
- when {
+ when {
transitionInfo.type == WindowManager.TRANSIT_SLEEP -> {
wasPreviousTransitionExitByScreenOff = true
ExitReason.SCREEN_OFF
@@ -387,7 +396,7 @@
ProtoLog.w(
WM_SHELL_DESKTOP_MODE,
"Unknown exit reason for transition type: %s",
- transitionInfo.type
+ transitionInfo.type,
)
ExitReason.UNKNOWN_EXIT
}
@@ -413,8 +422,7 @@
}
companion object {
- @VisibleForTesting
- const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks"
+ @VisibleForTesting const val VISIBLE_TASKS_COUNTER_NAME = "desktop_mode_visible_tasks"
@VisibleForTesting
const val VISIBLE_TASKS_COUNTER_SYSTEM_PROPERTY =
"debug.tracing." + VISIBLE_TASKS_COUNTER_NAME
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
index d6fccd1..9b3caca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTransitionTypes.kt
@@ -43,7 +43,7 @@
TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON,
TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW,
TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT,
- TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN
+ TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN,
)
}
@@ -73,7 +73,7 @@
TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG,
TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON,
TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT,
- TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN
+ TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index 2c432bc..301ba9e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -75,7 +75,7 @@
instanceId: InstanceId,
uid: Int,
packageName: String,
- event: DesktopUiEventEnum
+ event: DesktopUiEventEnum,
) {
if (packageName.isEmpty() || uid < 0) {
logD("Skip logging since package name is empty or bad uid")
@@ -84,11 +84,12 @@
uiEventLogger.logWithInstanceId(event, uid, packageName, instanceId)
}
- private fun getUid(packageName: String, userId: Int): Int = try {
- packageManager.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId).uid
- } catch (e: PackageManager.NameNotFoundException) {
- INVALID_PACKAGE_UID
- }
+ private fun getUid(packageName: String, userId: Int): Int =
+ try {
+ packageManager.getApplicationInfoAsUser(packageName, /* flags= */ 0, userId).uid
+ } catch (e: PackageManager.NameNotFoundException) {
+ INVALID_PACKAGE_UID
+ }
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
@@ -103,8 +104,12 @@
DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722),
@UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode")
DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723),
+ @UiEvent(doc = "Tap on the window header restore button in desktop windowing mode")
+ DESKTOP_WINDOW_RESTORE_BUTTON_TAP(2017),
@UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode")
DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724),
+ @UiEvent(doc = "Double tap on window header to restore from maximize in desktop windowing")
+ DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_RESTORE(2018),
@UiEvent(doc = "Tap on the window Handle to open the Handle Menu")
DESKTOP_WINDOW_APP_HANDLE_TAP(1998),
@UiEvent(doc = "Tap on the desktop mode option under app handle menu")
@@ -136,7 +141,11 @@
@UiEvent(doc = "Tap on the tile to left option in the maximize button menu")
DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_LEFT(2012),
@UiEvent(doc = "Tap on the tile to right option in the maximize button menu")
- DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT(2013);
+ DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_TILE_TO_RIGHT(2013),
+ @UiEvent(doc = "Moving the desktop window by dragging the header")
+ DESKTOP_WINDOW_MOVE_BY_HEADER_DRAG(2021),
+ @UiEvent(doc = "Double tap on the window header to refocus a desktop window")
+ DESKTOP_WINDOW_HEADER_TAP_TO_REFOCUS(2022);
override fun getId(): Int = mId
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index c7cf310..14623cf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -28,6 +28,7 @@
import android.graphics.Rect
import android.os.SystemProperties
import android.util.Size
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
val DESKTOP_MODE_INITIAL_BOUNDS_SCALE: Float =
@@ -36,21 +37,14 @@
val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int =
SystemProperties.getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
-/**
- * Calculates the initial bounds to enter desktop, centered on the display.
- */
+/** Calculates the initial bounds to enter desktop, centered on the display. */
fun calculateDefaultDesktopTaskBounds(displayLayout: DisplayLayout): Rect {
// TODO(b/319819547): Account for app constraints so apps do not become letterboxed
val desiredWidth = (displayLayout.width() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
val desiredHeight = (displayLayout.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE).toInt()
val heightOffset = (displayLayout.height() - desiredHeight) / 2
val widthOffset = (displayLayout.width() - desiredWidth) / 2
- return Rect(
- widthOffset,
- heightOffset,
- desiredWidth + widthOffset,
- desiredHeight + heightOffset
- )
+ return Rect(widthOffset, heightOffset, desiredWidth + widthOffset, desiredHeight + heightOffset)
}
/**
@@ -62,7 +56,7 @@
fun calculateInitialBounds(
displayLayout: DisplayLayout,
taskInfo: RunningTaskInfo,
- scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE
+ scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE,
): Rect {
val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
val appAspectRatio = calculateAspectRatio(taskInfo)
@@ -87,8 +81,10 @@
if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
- Size(taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
- idealSize.height)
+ Size(
+ taskInfo.appCompatTaskInfo.topActivityLetterboxAppWidth,
+ idealSize.height,
+ )
} else {
// For landscape resizeable activities, simply apply ideal size.
idealSize
@@ -108,7 +104,7 @@
// apply custom app width.
Size(
customPortraitWidthForLandscapeApp,
- taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight
+ taskInfo.appCompatTaskInfo.topActivityLetterboxAppHeight,
)
} else {
// For portrait resizeable activities, simply apply ideal size.
@@ -122,7 +118,7 @@
maximizeSizeGivenAspectRatio(
taskInfo,
Size(customPortraitWidthForLandscapeApp, idealSize.height),
- appAspectRatio
+ appAspectRatio,
)
} else {
// For portrait unresizeable activities, calculate maximum size (within the
@@ -140,13 +136,10 @@
}
/**
- * Calculates the maximized bounds of a task given in the given [DisplayLayout], taking
- * resizability into consideration.
+ * Calculates the maximized bounds of a task given in the given [DisplayLayout], taking resizability
+ * into consideration.
*/
-fun calculateMaximizeBounds(
- displayLayout: DisplayLayout,
- taskInfo: RunningTaskInfo,
-): Rect {
+fun calculateMaximizeBounds(displayLayout: DisplayLayout, taskInfo: RunningTaskInfo): Rect {
val stableBounds = Rect()
displayLayout.getStableBounds(stableBounds)
if (taskInfo.isResizeable) {
@@ -155,10 +148,13 @@
} else {
// if non-resizable then calculate max bounds according to aspect ratio
val activityAspectRatio = calculateAspectRatio(taskInfo)
- val newSize = maximizeSizeGivenAspectRatio(taskInfo,
- Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
- return centerInArea(
- newSize, stableBounds, stableBounds.left, stableBounds.top)
+ val newSize =
+ maximizeSizeGivenAspectRatio(
+ taskInfo,
+ Size(stableBounds.width(), stableBounds.height()),
+ activityAspectRatio,
+ )
+ return centerInArea(newSize, stableBounds, stableBounds.left, stableBounds.top)
}
}
@@ -169,7 +165,7 @@
fun maximizeSizeGivenAspectRatio(
taskInfo: RunningTaskInfo,
targetArea: Size,
- aspectRatio: Float
+ aspectRatio: Float,
): Size {
val targetHeight = targetArea.height
val targetWidth = targetArea.width
@@ -211,16 +207,36 @@
minOf(appBounds.height(), appBounds.width()).toFloat()
}
+/** Returns whether the task is maximized. */
+fun isTaskMaximized(taskInfo: RunningTaskInfo, displayController: DisplayController): Boolean {
+ val displayLayout =
+ displayController.getDisplayLayout(taskInfo.displayId)
+ ?: error("Could not get display layout for display=${taskInfo.displayId}")
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+ return isTaskMaximized(taskInfo, stableBounds)
+}
+
+/** Returns whether the task is maximized. */
+fun isTaskMaximized(taskInfo: RunningTaskInfo, stableBounds: Rect): Boolean {
+ val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+ return if (taskInfo.isResizeable) {
+ isTaskBoundsEqual(currentTaskBounds, stableBounds)
+ } else {
+ isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
+ }
+}
+
/** Returns true if task's width or height is maximized else returns false. */
fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
return taskBounds.width() == stableBounds.width() ||
- taskBounds.height() == stableBounds.height()
+ taskBounds.height() == stableBounds.height()
}
/** Returns true if task bound is equal to stable bounds else returns false. */
fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
return taskBounds.width() == stableBounds.width() &&
- taskBounds.height() == stableBounds.height()
+ taskBounds.height() == stableBounds.height()
}
/**
@@ -248,8 +264,8 @@
get() = isResizeable && !appCompatTaskInfo.hasMinAspectRatioOverride()
/**
- * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the
- * entire screen, as area can be offset by left and top start.
+ * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the entire
+ * screen, as area can be offset by left and top start.
*/
fun centerInArea(desiredSize: Size, areaBounds: Rect, leftStart: Int, topStart: Int): Rect {
val heightOffset = (areaBounds.height() - desiredSize.height) / 2
@@ -286,6 +302,6 @@
}
private fun hasFullscreenOverride(taskInfo: RunningTaskInfo): Boolean {
- return taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled
- || taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
+ return taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled ||
+ taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 7fcb767..e187d2c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.desktopmode
-import android.content.Context
import android.graphics.Rect
import android.graphics.Region
import android.util.ArrayMap
@@ -30,11 +29,8 @@
import androidx.core.util.valueIterator
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
-import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
-import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
-import com.android.wm.shell.sysui.ShellInit
import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -42,26 +38,23 @@
import kotlinx.coroutines.launch
/** Tracks desktop data for Android Desktop Windowing. */
-class DesktopRepository (
- private val context: Context,
- shellInit: ShellInit,
+class DesktopRepository(
private val persistentRepository: DesktopPersistentRepository,
- private val repositoryInitializer: DesktopRepositoryInitializer,
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
-){
-
+ val userId: Int,
+) {
/**
* Task data tracked per desktop.
*
* @property activeTasks task ids of active tasks currently or previously visible in Desktop
- * mode session. Tasks become inactive when task closes or when desktop mode session ends.
+ * mode session. Tasks become inactive when task closes or when desktop mode session ends.
* @property visibleTasks task ids for active freeform tasks that are currently visible. There
- * might be other active tasks in desktop mode that are not visible.
+ * might be other active tasks in desktop mode that are not visible.
* @property minimizedTasks task ids for active freeform tasks that are currently minimized.
* @property closingTasks task ids for tasks that are going to close, but are currently visible.
* @property freeformTasksInZOrder list of current freeform task ids ordered from top to bottom
* @property fullImmersiveTaskId the task id of the desktop task that is in full-immersive mode.
- * (top is at index 0).
+ * (top is at index 0).
*/
private data class DesktopTaskData(
val activeTasks: ArraySet<Int> = ArraySet(),
@@ -72,14 +65,16 @@
val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
var fullImmersiveTaskId: Int? = null,
) {
- fun deepCopy(): DesktopTaskData = DesktopTaskData(
- activeTasks = ArraySet(activeTasks),
- visibleTasks = ArraySet(visibleTasks),
- minimizedTasks = ArraySet(minimizedTasks),
- closingTasks = ArraySet(closingTasks),
- freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
- fullImmersiveTaskId = fullImmersiveTaskId
- )
+ fun deepCopy(): DesktopTaskData =
+ DesktopTaskData(
+ activeTasks = ArraySet(activeTasks),
+ visibleTasks = ArraySet(visibleTasks),
+ minimizedTasks = ArraySet(minimizedTasks),
+ closingTasks = ArraySet(closingTasks),
+ freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
+ fullImmersiveTaskId = fullImmersiveTaskId,
+ )
+
fun clear() {
activeTasks.clear()
visibleTasks.clear()
@@ -111,21 +106,12 @@
private var desktopGestureExclusionListener: Consumer<Region>? = null
private var desktopGestureExclusionExecutor: Executor? = null
- private val desktopTaskDataByDisplayId = object : SparseArray<DesktopTaskData>() {
- /** Gets [DesktopTaskData] for existing [displayId] or creates a new one. */
- fun getOrCreate(displayId: Int): DesktopTaskData =
- this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
- }
-
- init {
- if (DesktopModeStatus.canEnterDesktopMode(context)) {
- shellInit.addInitCallback(::initRepoFromPersistentStorage, this)
+ private val desktopTaskDataByDisplayId =
+ object : SparseArray<DesktopTaskData>() {
+ /** Gets [DesktopTaskData] for existing [displayId] or creates a new one. */
+ fun getOrCreate(displayId: Int): DesktopTaskData =
+ this[displayId] ?: DesktopTaskData().also { this[displayId] = it }
}
- }
-
- private fun initRepoFromPersistentStorage() {
- repositoryInitializer.initialize(this)
- }
/** Adds [activeTasksListener] to be notified of updates to active tasks. */
fun addActiveTaskListener(activeTasksListener: ActiveTasksListener) {
@@ -137,9 +123,7 @@
visibleTasksListeners[visibleTasksListener] = executor
desktopTaskDataByDisplayId.keyIterator().forEach {
val visibleTaskCount = getVisibleTaskCount(it)
- executor.execute {
- visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount)
- }
+ executor.execute { visibleTasksListener.onTasksVisibilityChanged(it, visibleTaskCount) }
}
}
@@ -201,8 +185,7 @@
/** Removes task from active task list of displays excluding the [excludedDisplayId]. */
fun removeActiveTask(taskId: Int, excludedDisplayId: Int? = null) {
desktopTaskDataByDisplayId.forEach { displayId, desktopTaskData ->
- if ((displayId != excludedDisplayId)
- && desktopTaskData.activeTasks.remove(taskId)) {
+ if ((displayId != excludedDisplayId) && desktopTaskData.activeTasks.remove(taskId)) {
logD("Removed active task=%d displayId=%d", taskId, displayId)
updateActiveTasksListeners(displayId)
}
@@ -229,16 +212,18 @@
}
fun isActiveTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.activeTasks }
+
fun isClosingTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.closingTasks }
+
fun isVisibleTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.visibleTasks }
+
fun isMinimizedTask(taskId: Int) = desktopTaskDataSequence().any { taskId in it.minimizedTasks }
/** Checks if a task is the only visible, non-closing, non-minimized task on its display. */
fun isOnlyVisibleNonClosingTask(taskId: Int): Boolean =
- desktopTaskDataSequence().any { it.visibleTasks
- .subtract(it.closingTasks)
- .subtract(it.minimizedTasks)
- .singleOrNull() == taskId
+ desktopTaskDataSequence().any {
+ it.visibleTasks.subtract(it.closingTasks).subtract(it.minimizedTasks).singleOrNull() ==
+ taskId
}
fun getActiveTasks(displayId: Int): ArraySet<Int> =
@@ -272,10 +257,12 @@
/**
* Updates visibility of a freeform task with [taskId] on [displayId] and notifies listeners.
*
- * If task was visible on a different display with a different [displayId], removes from
- * the set of visible tasks on that display and notifies listeners.
+ * If task was visible on a different display with a different [displayId], removes from the set
+ * of visible tasks on that display and notifies listeners.
*/
fun updateTask(displayId: Int, taskId: Int, isVisible: Boolean) {
+ logD("updateTask taskId=%d, displayId=%d, isVisible=%b", taskId, displayId, isVisible)
+
if (isVisible) {
// If task is visible, remove it from any other display besides [displayId].
removeVisibleTask(taskId, excludedDisplayId = displayId)
@@ -293,8 +280,12 @@
}
val newCount = getVisibleTaskCount(displayId)
if (prevCount != newCount) {
- logD("Update task visibility taskId=%d visible=%b displayId=%d",
- taskId, isVisible, displayId)
+ logD(
+ "Update task visibility taskId=%d visible=%b displayId=%d",
+ taskId,
+ isVisible,
+ displayId,
+ )
logD("VisibleTaskCount has changed from %d to %d", prevCount, newCount)
notifyVisibleTaskListeners(displayId, newCount)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -332,9 +323,8 @@
/** Gets number of visible tasks on given [displayId] */
fun getVisibleTaskCount(displayId: Int): Int =
- desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size ?: 0.also {
- logD("getVisibleTaskCount=$it")
- }
+ desktopTaskDataByDisplayId[displayId]?.visibleTasks?.size
+ ?: 0.also { logD("getVisibleTaskCount=$it") }
/**
* Adds task (or moves if it already exists) to the top of the ordered list.
@@ -357,9 +347,8 @@
if (displayId == INVALID_DISPLAY) {
// When a task vanishes it doesn't have a displayId. Find the display of the task and
// mark it as minimized.
- getDisplayIdForTask(taskId)?.let {
- minimizeTask(it, taskId)
- } ?: logW("Minimize task: No display id found for task: taskId=%d", taskId)
+ getDisplayIdForTask(taskId)?.let { minimizeTask(it, taskId) }
+ ?: logW("Minimize task: No display id found for task: taskId=%d", taskId)
} else {
logD("Minimize Task: display=%d, task=%d", displayId, taskId)
desktopTaskDataByDisplayId.getOrCreate(displayId).minimizedTasks.add(taskId)
@@ -373,8 +362,8 @@
/** Unminimizes the task for [taskId] and [displayId] */
fun unminimizeTask(displayId: Int, taskId: Int) {
logD("Unminimize Task: display=%d, task=%d", displayId, taskId)
- desktopTaskDataByDisplayId[displayId]?.minimizedTasks?.remove(taskId) ?:
- logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
+ desktopTaskDataByDisplayId[displayId]?.minimizedTasks?.remove(taskId)
+ ?: logW("Unminimize Task: display=%d, task=%d, no task data", displayId, taskId)
}
private fun getDisplayIdForTask(taskId: Int): Int? {
@@ -407,16 +396,14 @@
desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.remove(taskId)
boundsBeforeMaximizeByTaskId.remove(taskId)
boundsBeforeFullImmersiveByTaskId.remove(taskId)
- logD("Remaining freeform tasks: %s",
- desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString())
+ logD(
+ "Remaining freeform tasks: %s",
+ desktopTaskDataByDisplayId[displayId]?.freeformTasksInZOrder?.toDumpString(),
+ )
// Remove task from unminimized task if it is minimized.
unminimizeTask(displayId, taskId)
// Mark task as not in immersive if it was immersive.
- setTaskInFullImmersiveState(
- displayId = displayId,
- taskId = taskId,
- immersive = false
- )
+ setTaskInFullImmersiveState(displayId = displayId, taskId = taskId, immersive = false)
removeActiveTask(taskId)
removeVisibleTask(taskId)
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
@@ -495,15 +482,16 @@
persistentRepository.addOrUpdateDesktop(
// Use display id as desktop id for now since only once desktop per display
// is supported.
+ userId = userId,
desktopId = displayId,
visibleTasks = desktopTaskDataByDisplayIdCopy.visibleTasks,
minimizedTasks = desktopTaskDataByDisplayIdCopy.minimizedTasks,
- freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder
+ freeformTasksInZOrder = desktopTaskDataByDisplayIdCopy.freeformTasksInZOrder,
)
} catch (exception: Exception) {
logE(
"An exception occurred while updating the persistent repository \n%s",
- exception.stackTrace
+ exception.stackTrace,
)
}
}
@@ -529,6 +517,7 @@
)
pw.println("${innerPrefix}minimizedTasks=${data.minimizedTasks.toDumpString()}")
pw.println("${innerPrefix}fullImmersiveTaskId=${data.fullImmersiveTaskId}")
+ pw.println("${innerPrefix}wallpaperActivityToken=$wallpaperActivityToken")
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
index 94ac2e6..947a8dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListener.kt
@@ -22,72 +22,81 @@
import com.android.wm.shell.freeform.TaskChangeListener
/** Manages tasks handling specific to Android Desktop Mode. */
-class DesktopTaskChangeListener(
- private val desktopRepository: DesktopRepository,
-) : TaskChangeListener {
+class DesktopTaskChangeListener(private val desktopUserRepositories: DesktopUserRepositories) :
+ TaskChangeListener {
- override fun onTaskOpening(taskInfo: RunningTaskInfo) {
- if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
- return
+ override fun onTaskOpening(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!isFreeformTask(taskInfo) && desktopRepository.isActiveTask(taskInfo.taskId)) {
+ desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ return
+ }
+ if (isFreeformTask(taskInfo)) {
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ }
}
- if (isFreeformTask(taskInfo)) {
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+
+ override fun onTaskChanging(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+
+ // Case 1: Freeform task is changed in Desktop Mode.
+ if (isFreeformTask(taskInfo)) {
+ if (taskInfo.isVisible) {
+ desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ }
+ desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
+ } else {
+ // Case 2: Freeform task is changed outside Desktop Mode.
+ desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ }
}
- }
- override fun onTaskChanging(taskInfo: RunningTaskInfo) {
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
-
- // Case 1: Freeform task is changed in Desktop Mode.
- if (isFreeformTask(taskInfo)) {
- if (taskInfo.isVisible) {
- desktopRepository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
- }
- desktopRepository.updateTask(
- taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible)
- } else {
- // Case 2: Freeform task is changed outside Desktop Mode.
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ // This method should only be used for scenarios where the task info changes are not propagated
+ // to
+ // [DesktopTaskChangeListener#onTaskChanging] via [TransitionsObserver].
+ // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
+ // of race conditions and possible duplications with [onTaskChanging].
+ override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
+ // TODO: b/367268953 - Propapagate usages from FreeformTaskListener to this method.
}
- }
- // This method should only be used for scenarios where the task info changes are not propagated to
- // [DesktopTaskChangeListener#onTaskChanging] via [TransitionsObserver].
- // Any changes to [DesktopRepository] from this method should be made carefully to minimize risk
- // of race conditions and possible duplications with [onTaskChanging].
- override fun onNonTransitionTaskChanging(taskInfo: RunningTaskInfo) {
- // TODO: b/367268953 - Propapagate usages from FreeformTaskListener to this method.
- }
-
- override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
- if (!isFreeformTask(taskInfo)) {
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ override fun onTaskMovingToFront(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+ if (!isFreeformTask(taskInfo)) {
+ desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ }
+ // TODO: b/367268953 - Connect this with DesktopRepository for handling
+ // task moving to front for tasks in windowing mode.
}
- // TODO: b/367268953 - Connect this with DesktopRepository for handling
- // task moving to front for tasks in windowing mode.
- }
- override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
- // TODO: b/367268953 - Connect this with DesktopRepository.
- }
-
- override fun onTaskClosing(taskInfo: RunningTaskInfo) {
- if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
- // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
- desktopRepository.isClosingTask(taskInfo.taskId)) {
- // A task that's vanishing should be removed:
- // - If it's closed by the X button which means it's marked as a closing task.
- desktopRepository.removeClosingTask(taskInfo.taskId)
- desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
- } else {
- desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, isVisible = false)
- desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+ override fun onTaskMovingToBack(taskInfo: RunningTaskInfo) {
+ // TODO: b/367268953 - Connect this with DesktopRepository.
}
- }
- private fun isFreeformTask(taskInfo: RunningTaskInfo): Boolean =
- taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+ override fun onTaskClosing(taskInfo: RunningTaskInfo) {
+ val desktopRepository: DesktopRepository =
+ desktopUserRepositories.getProfile(taskInfo.userId)
+ if (!desktopRepository.isActiveTask(taskInfo.taskId)) return
+ // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
+ if (
+ !DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() ||
+ desktopRepository.isClosingTask(taskInfo.taskId)
+ ) {
+ // A task that's vanishing should be removed:
+ // - If it's closed by the X button which means it's marked as a closing task.
+ desktopRepository.removeClosingTask(taskInfo.taskId)
+ desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
+ } else {
+ desktopRepository.updateTask(taskInfo.displayId, taskInfo.taskId, isVisible = false)
+ desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+ }
+ }
+
+ private fun isFreeformTask(taskInfo: RunningTaskInfo): Boolean =
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt
index 65f12cf..848d80f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTaskPosition.kt
@@ -22,16 +22,14 @@
import android.graphics.Rect
import android.view.Gravity
import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomLeft
import com.android.wm.shell.desktopmode.DesktopTaskPosition.BottomRight
import com.android.wm.shell.desktopmode.DesktopTaskPosition.Center
import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopLeft
import com.android.wm.shell.desktopmode.DesktopTaskPosition.TopRight
-import com.android.wm.shell.R
-/**
- * The position of a task window in desktop mode.
- */
+/** The position of a task window in desktop mode. */
sealed class DesktopTaskPosition {
data object Center : DesktopTaskPosition() {
private const val WINDOW_HEIGHT_PROPORTION = 0.375
@@ -89,8 +87,8 @@
}
/**
- * Returns the top left coordinates for the window to be placed in the given
- * DesktopTaskPosition in the frame.
+ * Returns the top left coordinates for the window to be placed in the given DesktopTaskPosition
+ * in the frame.
*/
abstract fun getTopLeftCoordinates(frame: Rect, window: Rect): Point
@@ -98,8 +96,8 @@
}
/**
- * If the app has specified horizontal or vertical gravity layout, don't change the
- * task position for cascading effect.
+ * If the app has specified horizontal or vertical gravity layout, don't change the task position
+ * for cascading effect.
*/
fun canChangeTaskPosition(taskInfo: TaskInfo): Boolean {
taskInfo.topActivityInfo?.windowLayout?.let {
@@ -110,9 +108,7 @@
return true
}
-/**
- * Returns the current DesktopTaskPosition for a given window in the frame.
- */
+/** Returns the current DesktopTaskPosition for a given window in the frame. */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
fun Rect.getDesktopTaskPosition(bounds: Rect): DesktopTaskPosition {
return when {
@@ -140,8 +136,8 @@
internal fun prevBoundsMovedAboveThreshold(res: Resources, prev: Rect, newBounds: Rect): Boolean {
// This is the required minimum dp for a task to be touchable.
- val moveThresholdPx = res.getDimensionPixelSize(
- R.dimen.freeform_required_visible_empty_space_in_header)
+ val moveThresholdPx =
+ res.getDimensionPixelSize(R.dimen.freeform_required_visible_empty_space_in_header)
val leftFar = newBounds.left - prev.left > moveThresholdPx
val topFar = newBounds.top - prev.top > moveThresholdPx
val rightFar = prev.right - newBounds.right > moveThresholdPx
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index c479ab3..a94a40b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -84,10 +84,17 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
import com.android.wm.shell.desktopmode.DesktopRepository.VisibleTasksListener
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
+import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.minimize.DesktopWindowLimitRemoteHandler
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
@@ -95,6 +102,8 @@
import com.android.wm.shell.recents.RecentTasksController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState
+import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
import com.android.wm.shell.shared.ShellSharedConstants
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.annotations.ExternalThread
@@ -128,14 +137,6 @@
import java.util.Optional
import java.util.concurrent.Executor
import java.util.function.Consumer
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
-import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
-import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
-import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
-import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION
-import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler.FULLSCREEN_ANIMATION_DURATION
-import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState
-import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
/** Handles moving tasks in and out of desktop */
class DesktopTasksController(
@@ -158,7 +159,7 @@
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val dragToDesktopTransitionHandler: DragToDesktopTransitionHandler,
private val desktopImmersiveController: DesktopImmersiveController,
- private val taskRepository: DesktopRepository,
+ private val userRepositories: DesktopUserRepositories,
private val recentsTransitionHandler: RecentsTransitionHandler,
private val multiInstanceHelper: MultiInstanceHelper,
@ShellMainThread private val mainExecutor: ShellExecutor,
@@ -176,6 +177,7 @@
UserChangeListener {
private val desktopMode: DesktopModeImpl
+ private var taskRepository: DesktopRepository
private var visualIndicator: DesktopModeVisualIndicator? = null
private var userId: Int
private val desktopModeShellCommandHandler: DesktopModeShellCommandHandler =
@@ -203,8 +205,7 @@
}
}
- @VisibleForTesting
- var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
+ @VisibleForTesting var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
@VisibleForTesting
var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener? = null
@@ -213,8 +214,7 @@
val draggingTaskId
get() = dragToDesktopTransitionHandler.draggingTaskId
- @RecentsTransitionState
- private var recentsTransitionState = TRANSITION_STATE_NOT_RUNNING
+ @RecentsTransitionState private var recentsTransitionState = TRANSITION_STATE_NOT_RUNNING
private lateinit var splitScreenController: SplitScreenController
lateinit var freeformTaskTransitionStarter: FreeformTaskTransitionStarter
@@ -228,6 +228,7 @@
shellInit.addInitCallback({ onInit() }, this)
}
userId = ActivityManager.getCurrentUser()
+ taskRepository = userRepositories.getProfile(userId)
}
private fun onInit() {
@@ -237,7 +238,7 @@
shellController.addExternalInterface(
ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE,
{ createExternalInterface() },
- this
+ this,
)
shellController.addUserChangeListener(this)
transitions.addHandler(this)
@@ -247,7 +248,7 @@
override fun onTransitionStateChanged(@RecentsTransitionState state: Int) {
logV(
"Recents transition state changed: %s",
- RecentsTransitionStateListener.stateToString(state)
+ RecentsTransitionStateListener.stateToString(state),
)
recentsTransitionState = state
desktopTilingDecorViewModel.onOverviewAnimationStateChange(
@@ -297,17 +298,17 @@
bringDesktopAppsToFront(displayId, wct)
val transitionType = transitionType(remoteTransition)
- val handler = remoteTransition?.let {
- OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
- }
+ val handler =
+ remoteTransition?.let {
+ OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
+ }
transitions.startTransition(transitionType, wct, handler).also { t ->
handler?.setTransition(t)
}
}
/** Gets number of visible tasks in [displayId]. */
- fun visibleTaskCount(displayId: Int): Int =
- taskRepository.getVisibleTaskCount(displayId)
+ fun visibleTaskCount(displayId: Int): Int = taskRepository.getVisibleTaskCount(displayId)
/** Returns true if any tasks are visible in Desktop Mode. */
fun isDesktopModeShowing(displayId: Int): Boolean = visibleTaskCount(displayId) > 0
@@ -318,29 +319,37 @@
when (allFocusedTasks.size) {
0 -> return
// Full screen case
- 1 -> moveRunningTaskToDesktop(
- allFocusedTasks.single(), transitionSource = transitionSource)
+ 1 ->
+ moveRunningTaskToDesktop(
+ allFocusedTasks.single(),
+ transitionSource = transitionSource,
+ )
// Split-screen case where there are two focused tasks, then we find the child
// task to move to desktop.
- 2 -> moveRunningTaskToDesktop(
- getSplitFocusedTask(allFocusedTasks[0], allFocusedTasks[1]),
- transitionSource = transitionSource)
- else -> logW(
- "DesktopTasksController: Cannot enter desktop, expected less " +
- "than 3 focused tasks but found %d", allFocusedTasks.size)
+ 2 ->
+ moveRunningTaskToDesktop(
+ getSplitFocusedTask(allFocusedTasks[0], allFocusedTasks[1]),
+ transitionSource = transitionSource,
+ )
+ else ->
+ logW(
+ "DesktopTasksController: Cannot enter desktop, expected less " +
+ "than 3 focused tasks but found %d",
+ allFocusedTasks.size,
+ )
}
}
/**
- * Returns all focused tasks in full screen or split screen mode in [displayId] when
- * it is not the home activity.
+ * Returns all focused tasks in full screen or split screen mode in [displayId] when it is not
+ * the home activity.
*/
private fun getAllFocusedTasks(displayId: Int): List<RunningTaskInfo> =
shellTaskOrganizer.getRunningTasks(displayId).filter {
it.isFocused &&
- (it.windowingMode == WINDOWING_MODE_FULLSCREEN ||
- it.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
- it.activityType != ACTIVITY_TYPE_HOME
+ (it.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+ it.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
+ it.activityType != ACTIVITY_TYPE_HOME
}
/** Returns child task from two focused tasks in split screen mode. */
@@ -376,9 +385,9 @@
}
private fun moveBackgroundTaskToDesktop(
- taskId: Int,
- wct: WindowContainerTransaction,
- transitionSource: DesktopModeTransitionSource,
+ taskId: Int,
+ wct: WindowContainerTransaction,
+ transitionSource: DesktopModeTransitionSource,
): Boolean {
if (recentTasksController?.findTaskInBackground(taskId) == null) {
logW("moveBackgroundTaskToDesktop taskId=%d not found", taskId)
@@ -386,54 +395,62 @@
}
logV("moveBackgroundTaskToDesktop with taskId=%d", taskId)
// TODO(342378842): Instead of using default display, support multiple displays
- val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
- DEFAULT_DISPLAY, wct, taskId)
- val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = DEFAULT_DISPLAY,
- excludeTaskId = taskId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
- )
+ val taskIdToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ displayId = DEFAULT_DISPLAY,
+ excludeTaskId = taskId,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
+ )
wct.startTask(
taskId,
- ActivityOptions.makeBasic().apply {
- launchWindowingMode = WINDOWING_MODE_FREEFORM
- }.toBundle(),
+ ActivityOptions.makeBasic()
+ .apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
+ .toBundle(),
)
// TODO(343149901): Add DPI changes for task launch
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
return true
}
- /** Moves a running task to desktop. */
+ /** Moves a running task to desktop. */
fun moveRunningTaskToDesktop(
task: RunningTaskInfo,
wct: WindowContainerTransaction = WindowContainerTransaction(),
transitionSource: DesktopModeTransitionSource,
) {
- if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
- && isTopActivityExemptFromDesktopWindowing(context, task)) {
+ if (
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
+ isTopActivityExemptFromDesktopWindowing(context, task)
+ ) {
logW("Cannot enter desktop for taskId %d, ineligible top activity found", task.taskId)
return
}
logV("moveRunningTaskToDesktop taskId=%d", task.taskId)
exitSplitIfApplicable(wct, task)
- val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = task.displayId,
- excludeTaskId = task.taskId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
- )
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ displayId = task.displayId,
+ excludeTaskId = task.taskId,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
+ )
// Bring other apps to front first
val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
addMoveToDesktopChanges(wct, task)
val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct, transitionSource)
- desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(FREEFORM_ANIMATION_DURATION)
+ desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+ FREEFORM_ANIMATION_DURATION
+ )
taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) }
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
}
@@ -448,11 +465,15 @@
taskSurface: SurfaceControl,
) {
logV("startDragToDesktop taskId=%d", taskInfo.taskId)
- interactionJankMonitor.begin(taskSurface, context, handler,
- CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
+ interactionJankMonitor.begin(
+ taskSurface,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD,
+ )
dragToDesktopTransitionHandler.startDragToDesktopTransition(
taskInfo.taskId,
- dragToDesktopValueAnimator
+ dragToDesktopValueAnimator,
)
}
@@ -464,7 +485,7 @@
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"DesktopTasksController: finalizeDragToDesktop taskId=%d",
- taskInfo.taskId
+ taskInfo.taskId,
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
@@ -472,12 +493,13 @@
val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
- val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = taskInfo.displayId,
- excludeTaskId = null,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH
- )
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ displayId = taskInfo.displayId,
+ excludeTaskId = null,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
+ )
val transition = dragToDesktopTransitionHandler.finishDragToDesktopTransition(wct)
desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt()
@@ -520,16 +542,16 @@
performDesktopExitCleanupIfNeeded(taskId, wct)
taskRepository.addClosingTask(displayId, taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
- doesAnyTaskRequireTaskbarRounding(
- displayId,
- taskId
- )
+ doesAnyTaskRequireTaskbarRounding(displayId, taskId)
)
- return desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- taskInfo = taskInfo,
- reason = DesktopImmersiveController.ExitReason.CLOSED
- ).asExit()?.runOnTransitionStart
+ return desktopImmersiveController
+ .exitImmersiveIfApplicable(
+ wct = wct,
+ taskInfo = taskInfo,
+ reason = DesktopImmersiveController.ExitReason.CLOSED,
+ )
+ .asExit()
+ ?.runOnTransitionStart
}
fun minimizeTask(taskInfo: RunningTaskInfo) {
@@ -538,11 +560,12 @@
val wct = WindowContainerTransaction()
performDesktopExitCleanupIfNeeded(taskId, wct)
// Notify immersive handler as it might need to exit immersive state.
- val exitResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- taskInfo = taskInfo,
- reason = DesktopImmersiveController.ExitReason.MINIMIZED
- )
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
+ wct = wct,
+ taskInfo = taskInfo,
+ reason = DesktopImmersiveController.ExitReason.MINIMIZED,
+ )
wct.reorder(taskInfo.token, false)
val transition = freeformTaskTransitionStarter.startMinimizedModeTransition(wct)
@@ -550,7 +573,7 @@
it.addPendingMinimizeChange(
transition = transition,
displayId = displayId,
- taskId = taskId
+ taskId = taskId,
)
}
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
@@ -577,7 +600,7 @@
splitScreenController.prepareExitSplitScreen(
wct,
splitScreenController.getStageOfTask(taskInfo.taskId),
- EXIT_REASON_DESKTOP_MODE
+ EXIT_REASON_DESKTOP_MODE,
)
splitScreenController.transitionHandler?.onSplitToDesktop()
}
@@ -597,22 +620,24 @@
private fun moveToFullscreenWithAnimation(
task: RunningTaskInfo,
position: Point,
- transitionSource: DesktopModeTransitionSource
+ transitionSource: DesktopModeTransitionSource,
) {
logV("moveToFullscreenWithAnimation taskId=%d", task.taskId)
val wct = WindowContainerTransaction()
addMoveToFullscreenChanges(wct, task)
exitDesktopTaskTransitionHandler.startTransition(
- transitionSource,
- wct,
- position,
- mOnAnimationFinishedCallback
- )
+ transitionSource,
+ wct,
+ position,
+ mOnAnimationFinishedCallback,
+ )
// handles case where we are moving to full screen without closing all DW tasks.
if (!taskRepository.isOnlyVisibleNonClosingTask(task.taskId)) {
- desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
+ FULLSCREEN_ANIMATION_DURATION
+ )
}
}
@@ -642,16 +667,11 @@
val wct = WindowContainerTransaction()
wct.startTask(
taskId,
- ActivityOptions.makeBasic().apply {
- launchWindowingMode = WINDOWING_MODE_FREEFORM
- }.toBundle(),
+ ActivityOptions.makeBasic()
+ .apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
+ .toBundle(),
)
- startLaunchTransition(
- TRANSIT_OPEN,
- wct,
- taskId,
- remoteTransition = remoteTransition
- )
+ startLaunchTransition(TRANSIT_OPEN, wct, taskId, remoteTransition = remoteTransition)
}
/**
@@ -686,29 +706,32 @@
remoteTransition: RemoteTransition? = null,
displayId: Int = DEFAULT_DISPLAY,
): IBinder {
- val taskIdToMinimize = if (launchingTaskId != null) {
- addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
- } else {
- logW("Starting desktop task launch without checking the task-limit")
- // TODO(b/378920066): This currently does not respect the desktop window limit.
- // It's possible that |launchingTaskId| is null when launching using an intent, and
- // the task-limit should be respected then too.
- null
- }
- val exitImmersiveResult = desktopImmersiveController.exitImmersiveIfApplicable(
- wct = wct,
- displayId = displayId,
- excludeTaskId = launchingTaskId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
- )
- if (remoteTransition == null) {
- val t = desktopMixedTransitionHandler.startLaunchTransition(
- transitionType = transitionType,
+ val taskIdToMinimize =
+ if (launchingTaskId != null) {
+ addAndGetMinimizeChanges(displayId, wct, newTaskId = launchingTaskId)
+ } else {
+ logW("Starting desktop task launch without checking the task-limit")
+ // TODO(b/378920066): This currently does not respect the desktop window limit.
+ // It's possible that |launchingTaskId| is null when launching using an intent, and
+ // the task-limit should be respected then too.
+ null
+ }
+ val exitImmersiveResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
- taskId = launchingTaskId,
- minimizingTaskId = taskIdToMinimize,
- exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
+ displayId = displayId,
+ excludeTaskId = launchingTaskId,
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
+ if (remoteTransition == null) {
+ val t =
+ desktopMixedTransitionHandler.startLaunchTransition(
+ transitionType = transitionType,
+ wct = wct,
+ taskId = launchingTaskId,
+ minimizingTaskId = taskIdToMinimize,
+ exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask,
+ )
taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) }
exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t)
return t
@@ -722,7 +745,11 @@
}
val remoteTransitionHandler =
DesktopWindowLimitRemoteHandler(
- mainExecutor, rootTaskDisplayAreaOrganizer, remoteTransition, taskIdToMinimize)
+ mainExecutor,
+ rootTaskDisplayAreaOrganizer,
+ remoteTransition,
+ taskIdToMinimize,
+ )
val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler)
remoteTransitionHandler.setTransition(t)
taskIdToMinimize.let { addPendingMinimizeTransition(t, it) }
@@ -793,34 +820,23 @@
* bounds) and a free floating state (either the last saved bounds if available or the default
* bounds otherwise).
*/
- fun toggleDesktopTaskSize(
- taskInfo: RunningTaskInfo,
- resizeTrigger: ResizeTrigger,
- inputMethod: InputMethod,
- maximizeCujRecorder: (() -> Unit)? = null,
- unmaximizeCujRecorder: (() -> Unit)? = null,
- ) {
+ fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo, interaction: ToggleTaskSizeInteraction) {
val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
desktopModeEventLogger.logTaskResizingStarted(
- resizeTrigger,
- inputMethod,
+ interaction.resizeTrigger,
+ interaction.inputMethod,
taskInfo,
currentTaskBounds.width(),
currentTaskBounds.height(),
- displayController
+ displayController,
)
-
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
-
- val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
val destinationBounds = Rect()
-
- val isMaximized = isTaskMaximized(taskInfo, stableBounds)
+ val isMaximized = interaction.direction == ToggleTaskSizeInteraction.Direction.RESTORE
// If the task is currently maximized, we will toggle it not to be and vice versa. This is
// helpful to eliminate the current task from logic to calculate taskbar corner rounding.
- val willMaximize = !isMaximized
+ val willMaximize = interaction.direction == ToggleTaskSizeInteraction.Direction.MAXIMIZE
if (isMaximized) {
- unmaximizeCujRecorder?.invoke()
// The desktop task is at the maximized width and/or height of the stable bounds.
// If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
// Otherwise, toggle to the default bounds.
@@ -836,7 +852,6 @@
}
}
} else {
- maximizeCujRecorder?.invoke()
// Save current bounds so that task can be restored back to original bounds if necessary
// and toggle to the stable bounds.
desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
@@ -845,22 +860,27 @@
destinationBounds.set(calculateMaximizeBounds(displayLayout, taskInfo))
}
-
val shouldRestoreToSnap =
isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)
logD("willMaximize = %s", willMaximize)
logD("shouldRestoreToSnap = %s", shouldRestoreToSnap)
- val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap ||
+ val doesAnyTaskRequireTaskbarRounding =
+ willMaximize ||
+ shouldRestoreToSnap ||
doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId)
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+ interaction.uiEvent?.let { uiEvent -> desktopModeUiEventLogger.log(taskInfo, uiEvent) }
desktopModeEventLogger.logTaskResizingEnded(
- resizeTrigger, inputMethod,
- taskInfo, destinationBounds.width(),
- destinationBounds.height(), displayController
+ interaction.resizeTrigger,
+ interaction.inputMethod,
+ taskInfo,
+ destinationBounds.width(),
+ destinationBounds.height(),
+ displayController,
)
toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
}
@@ -869,12 +889,9 @@
taskInfo: RunningTaskInfo,
taskSurface: SurfaceControl,
currentDragBounds: Rect,
- motionEvent: MotionEvent
+ motionEvent: MotionEvent,
) {
- val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
- val stableBounds = Rect()
- displayLayout.getStableBounds(stableBounds)
- if (isTaskMaximized(taskInfo, stableBounds)) {
+ if (isTaskMaximized(taskInfo, displayController)) {
// Handle the case where we attempt to drag-to-maximize when already maximized: the task
// position won't need to change but we want to animate the surface going back to the
// maximized position.
@@ -892,8 +909,11 @@
toggleDesktopTaskSize(
taskInfo,
- ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER,
- DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent),
+ ToggleTaskSizeInteraction(
+ direction = ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ source = ToggleTaskSizeInteraction.Source.HEADER_DRAG_TO_TOP,
+ inputMethod = DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent),
+ ),
)
}
@@ -904,29 +924,19 @@
} else {
// if non-resizable then calculate max bounds according to aspect ratio
val activityAspectRatio = calculateAspectRatio(taskInfo)
- val newSize = maximizeSizeGivenAspectRatio(taskInfo,
- Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
- return centerInArea(
- newSize, stableBounds, stableBounds.left, stableBounds.top)
- }
- }
-
- private fun isTaskMaximized(
- taskInfo: RunningTaskInfo,
- stableBounds: Rect
- ): Boolean {
- val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
-
- return if (taskInfo.isResizeable) {
- isTaskBoundsEqual(currentTaskBounds, stableBounds)
- } else {
- isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
+ val newSize =
+ maximizeSizeGivenAspectRatio(
+ taskInfo,
+ Size(stableBounds.width(), stableBounds.height()),
+ activityAspectRatio,
+ )
+ return centerInArea(newSize, stableBounds, stableBounds.left, stableBounds.top)
}
}
private fun isMaximizedToStableBoundsEdges(
taskInfo: RunningTaskInfo,
- stableBounds: Rect
+ stableBounds: Rect,
): Boolean {
val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
return isTaskBoundsEqual(currentTaskBounds, stableBounds)
@@ -935,18 +945,16 @@
/** Returns if current task bound is snapped to half screen */
private fun isTaskSnappedToHalfScreen(
taskInfo: RunningTaskInfo,
- taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds
+ taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds,
): Boolean =
getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds ||
- getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds
+ getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds
@VisibleForTesting
- fun doesAnyTaskRequireTaskbarRounding(
- displayId: Int,
- excludeTaskId: Int? = null,
- ): Boolean {
+ fun doesAnyTaskRequireTaskbarRounding(displayId: Int, excludeTaskId: Int? = null): Boolean {
val doesAnyTaskRequireTaskbarRounding =
- taskRepository.getExpandedTasksOrdered(displayId)
+ taskRepository
+ .getExpandedTasksOrdered(displayId)
// exclude current task since maximize/restore transition has not taken place yet.
.filterNot { taskId -> taskId == excludeTaskId }
.any { taskId ->
@@ -956,14 +964,14 @@
logD("taskInfo = %s", taskInfo)
logD(
"isTaskSnappedToHalfScreen(taskInfo) = %s",
- isTaskSnappedToHalfScreen(taskInfo)
+ isTaskSnappedToHalfScreen(taskInfo),
)
logD(
"isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s",
- isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ isMaximizedToStableBoundsEdges(taskInfo, stableBounds),
)
- isTaskSnappedToHalfScreen(taskInfo)
- || isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ isTaskSnappedToHalfScreen(taskInfo) ||
+ isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
}
logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding)
@@ -976,7 +984,7 @@
* @param taskInfo current task that is being snap-resized via dragging or maximize menu button
* @param taskSurface the leash of the task being dragged
* @param currentDragBounds current position of the task leash being dragged (or current task
- * bounds if being snapped resize via maximize menu button)
+ * bounds if being snapped resize via maximize menu button)
* @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
*/
fun snapToHalfScreen(
@@ -994,7 +1002,7 @@
taskInfo,
currentDragBounds.width(),
currentDragBounds.height(),
- displayController
+ displayController,
)
val destinationBounds = getSnapBounds(taskInfo, position)
@@ -1008,12 +1016,13 @@
)
if (DesktopModeFlags.ENABLE_TILE_RESIZING.isTrue()) {
- val isTiled = desktopTilingDecorViewModel.snapToHalfScreen(
- taskInfo,
- desktopWindowDecoration,
- position,
- currentDragBounds,
- )
+ val isTiled =
+ desktopTilingDecorViewModel.snapToHalfScreen(
+ taskInfo,
+ desktopWindowDecoration,
+ position,
+ currentDragBounds,
+ )
if (isTiled) {
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
}
@@ -1054,10 +1063,11 @@
) {
if (!isSnapResizingAllowed(taskInfo)) {
Toast.makeText(
- getContext(),
- R.string.desktop_mode_non_resizable_snap_text,
- Toast.LENGTH_SHORT
- ).show()
+ getContext(),
+ R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT,
+ )
+ .show()
return
}
@@ -1068,11 +1078,10 @@
position,
resizeTrigger,
inputMethod,
- desktopModeWindowDecoration
+ desktopModeWindowDecoration,
)
}
-
@VisibleForTesting
fun handleSnapResizingTaskOnDrag(
taskInfo: RunningTaskInfo,
@@ -1086,7 +1095,11 @@
releaseVisualIndicator()
if (!isSnapResizingAllowed(taskInfo)) {
interactionJankMonitor.begin(
- taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_non_resizable"
+ taskSurface,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_SNAP_RESIZE,
+ "drag_non_resizable",
)
// reposition non-resizable app back to its original position before being dragged
@@ -1097,20 +1110,26 @@
endBounds = dragStartBounds,
doOnEnd = {
Toast.makeText(
- context,
- com.android.wm.shell.R.string.desktop_mode_non_resizable_snap_text,
- Toast.LENGTH_SHORT
- ).show()
+ context,
+ com.android.wm.shell.R.string.desktop_mode_non_resizable_snap_text,
+ Toast.LENGTH_SHORT,
+ )
+ .show()
},
)
} else {
- val resizeTrigger = if (position == SnapPosition.LEFT) {
- ResizeTrigger.DRAG_LEFT
- } else {
- ResizeTrigger.DRAG_RIGHT
- }
+ val resizeTrigger =
+ if (position == SnapPosition.LEFT) {
+ ResizeTrigger.DRAG_LEFT
+ } else {
+ ResizeTrigger.DRAG_RIGHT
+ }
interactionJankMonitor.begin(
- taskSurface, context, handler, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"
+ taskSurface,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_SNAP_RESIZE,
+ "drag_resizable",
)
snapToHalfScreen(
taskInfo,
@@ -1140,7 +1159,7 @@
stableBounds.left,
stableBounds.top,
stableBounds.left + destinationWidth,
- stableBounds.bottom
+ stableBounds.bottom,
)
}
SnapPosition.RIGHT -> {
@@ -1148,7 +1167,7 @@
stableBounds.right - destinationWidth,
stableBounds.top,
stableBounds.right,
- stableBounds.bottom
+ stableBounds.bottom,
)
}
}
@@ -1168,36 +1187,32 @@
private fun bringDesktopAppsToFrontBeforeShowingNewTask(
displayId: Int,
wct: WindowContainerTransaction,
- newTaskIdInFront: Int
+ newTaskIdInFront: Int,
): Int? = bringDesktopAppsToFront(displayId, wct, newTaskIdInFront)
private fun bringDesktopAppsToFront(
displayId: Int,
wct: WindowContainerTransaction,
- newTaskIdInFront: Int? = null
+ newTaskIdInFront: Int? = null,
): Int? {
logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
// Move home to front, ensures that we go back home when all desktop windows are closed
moveHomeTask(wct, toTop = true)
// Currently, we only handle the desktop on the default display really.
- if (displayId == DEFAULT_DISPLAY
- && ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
+ if (displayId == DEFAULT_DISPLAY && ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()) {
// Add translucent wallpaper activity to show the wallpaper underneath
addWallpaperActivity(wct)
}
- val expandedTasksOrderedFrontToBack =
- taskRepository.getExpandedTasksOrdered(displayId)
+ val expandedTasksOrderedFrontToBack = taskRepository.getExpandedTasksOrdered(displayId)
// If we're adding a new Task we might need to minimize an old one
// TODO(b/365725441): Handle non running task minimization
val taskIdToMinimize: Int? =
if (newTaskIdInFront != null && desktopTasksLimiter.isPresent) {
- desktopTasksLimiter.get()
- .getTaskIdToMinimize(
- expandedTasksOrderedFrontToBack,
- newTaskIdInFront
- )
+ desktopTasksLimiter
+ .get()
+ .getTaskIdToMinimize(expandedTasksOrderedFrontToBack, newTaskIdInFront)
} else {
null
}
@@ -1215,15 +1230,16 @@
// Task is not running, start it
wct.startTask(
taskId,
- ActivityOptions.makeBasic().apply {
- launchWindowingMode = WINDOWING_MODE_FREEFORM
- }.toBundle(),
+ ActivityOptions.makeBasic()
+ .apply { launchWindowingMode = WINDOWING_MODE_FREEFORM }
+ .toBundle(),
)
}
}
- taskbarDesktopTaskListener?.
- onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+ doesAnyTaskRequireTaskbarRounding(displayId)
+ )
return taskIdToMinimize
}
@@ -1238,8 +1254,7 @@
private fun addWallpaperActivity(wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
val userHandle = UserHandle.of(userId)
- val userContext =
- context.createContextAsUser(userHandle, /* flags= */ 0)
+ val userContext = context.createContextAsUser(userHandle, /* flags= */ 0)
val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId)
val options =
@@ -1254,8 +1269,8 @@
/* requestCode= */ 0,
intent,
PendingIntent.FLAG_IMMUTABLE,
- /* bundle= */ null,
- userHandle
+ /* options= */ null,
+ userHandle,
)
wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
}
@@ -1275,13 +1290,14 @@
if (!taskRepository.isOnlyVisibleNonClosingTask(taskId)) {
return
}
- desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(FULLSCREEN_ANIMATION_DURATION)
+ desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
+ FULLSCREEN_ANIMATION_DURATION
+ )
if (taskRepository.wallpaperActivityToken != null) {
removeWallpaperActivity(wct)
}
}
-
fun releaseVisualIndicator() {
val t = SurfaceControl.Transaction()
visualIndicator?.releaseVisualIndicator(t)
@@ -1305,7 +1321,7 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
// This handler should never be the sole handler, so should not animate anything.
return false
@@ -1313,7 +1329,7 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? {
logV("handleRequest request=%s", request)
// Check if we should skip handling this transition
@@ -1376,11 +1392,8 @@
// Check if freeform task launch during recents should be handled
shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
// Check if the closing task needs to be handled
- TransitionUtil.isClosingType(request.type) -> handleTaskClosing(
- task,
- transition,
- request.type
- )
+ TransitionUtil.isClosingType(request.type) ->
+ handleTaskClosing(task, transition, request.type)
// Check if the top task shouldn't be allowed to enter desktop mode
isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
// Check if fullscreen task should be updated
@@ -1397,19 +1410,16 @@
}
/** Whether the given [change] in the [transition] is a known desktop change. */
- fun isDesktopChange(
- transition: IBinder,
- change: TransitionInfo.Change,
- ): Boolean {
+ fun isDesktopChange(transition: IBinder, change: TransitionInfo.Change): Boolean {
// Only the immersive controller is currently involved in mixed transitions.
- return Flags.enableFullyImmersiveInDesktop()
- && desktopImmersiveController.isImmersiveChange(transition, change)
+ return Flags.enableFullyImmersiveInDesktop() &&
+ desktopImmersiveController.isImmersiveChange(transition, change)
}
/**
- * Whether the given transition [info] will potentially include a desktop change, in which
- * case the transition should be treated as mixed so that the change is in part animated by
- * one of the desktop transition handlers.
+ * Whether the given transition [info] will potentially include a desktop change, in which case
+ * the transition should be treated as mixed so that the change is in part animated by one of
+ * the desktop transition handlers.
*/
fun shouldPlayDesktopAnimation(info: TransitionRequestInfo): Boolean {
// Only immersive mixed transition are currently supported.
@@ -1453,7 +1463,7 @@
change,
startTransaction,
finishTransaction,
- finishCallback
+ finishCallback,
)
}
@@ -1478,13 +1488,14 @@
/** Returns whether an existing desktop task is being relaunched in freeform or not. */
private fun isFreeformRelaunch(triggerTask: RunningTaskInfo?, request: TransitionRequestInfo) =
- (triggerTask != null && triggerTask.windowingMode == WINDOWING_MODE_FREEFORM
- && TransitionUtil.isOpeningType(request.type)
- && taskRepository.isActiveTask(triggerTask.taskId))
+ (triggerTask != null &&
+ triggerTask.windowingMode == WINDOWING_MODE_FREEFORM &&
+ TransitionUtil.isOpeningType(request.type) &&
+ taskRepository.isActiveTask(triggerTask.taskId))
private fun isIncompatibleTask(task: TaskInfo) =
- DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue()
- && isTopActivityExemptFromDesktopWindowing(context, task)
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODALS_POLICY.isTrue() &&
+ isTopActivityExemptFromDesktopWindowing(context, task)
private fun shouldHandleTaskClosing(request: TransitionRequestInfo): Boolean {
return ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue() &&
@@ -1493,18 +1504,19 @@
}
/** Open an existing instance of an app. */
- fun openInstance(
- callingTask: RunningTaskInfo,
- requestedTaskId: Int
- ) {
+ fun openInstance(callingTask: RunningTaskInfo, requestedTaskId: Int) {
val wct = WindowContainerTransaction()
val options = createNewWindowOptions(callingTask)
if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
wct.startTask(requestedTaskId, options.toBundle())
- val taskIdToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
- callingTask.displayId, wct, requestedTaskId)
- val exitResult = desktopImmersiveController
- .exitImmersiveIfApplicable(
+ val taskIdToMinimize =
+ bringDesktopAppsToFrontBeforeShowingNewTask(
+ callingTask.displayId,
+ wct,
+ requestedTaskId,
+ )
+ val exitResult =
+ desktopImmersiveController.exitImmersiveIfApplicable(
wct = wct,
displayId = callingTask.displayId,
excludeTaskId = requestedTaskId,
@@ -1516,44 +1528,51 @@
exitResult.asExit()?.runOnTransitionStart?.invoke(transition)
} else {
val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
- splitScreenController.startTask(requestedTaskId, splitPosition,
- options.toBundle(), null /* hideTaskToken */)
+ splitScreenController.startTask(
+ requestedTaskId,
+ splitPosition,
+ options.toBundle(),
+ null, /* hideTaskToken */
+ )
}
}
/** Create an Intent to open a new window of a task. */
- fun openNewWindow(
- callingTaskInfo: RunningTaskInfo
- ) {
+ fun openNewWindow(callingTaskInfo: RunningTaskInfo) {
// TODO(b/337915660): Add a transition handler for these; animations
// need updates in some cases.
val baseActivity = callingTaskInfo.baseActivity ?: return
- val fillIn: Intent = context.packageManager
- .getLaunchIntentForPackage(
- baseActivity.packageName
- ) ?: return
- fillIn
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
- val launchIntent = PendingIntent.getActivity(
- context,
- /* requestCode= */ 0,
- fillIn,
- PendingIntent.FLAG_IMMUTABLE
- )
+ val fillIn: Intent =
+ context.packageManager.getLaunchIntentForPackage(baseActivity.packageName) ?: return
+ fillIn.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ val launchIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode= */ 0,
+ fillIn,
+ PendingIntent.FLAG_IMMUTABLE,
+ )
val options = createNewWindowOptions(callingTaskInfo)
when (options.launchWindowingMode) {
WINDOWING_MODE_MULTI_WINDOW -> {
- val splitPosition = splitScreenController
- .determineNewInstancePosition(callingTaskInfo)
+ val splitPosition =
+ splitScreenController.determineNewInstancePosition(callingTaskInfo)
// TODO(b/349828130) currently pass in index_undefined until we can revisit these
// specific cases in the future.
- val splitIndex = if (enableFlexibleSplit())
- splitScreenController.determineNewInstanceIndex(callingTaskInfo) else
- SPLIT_INDEX_UNDEFINED
+ val splitIndex =
+ if (enableFlexibleSplit())
+ splitScreenController.determineNewInstanceIndex(callingTaskInfo)
+ else SPLIT_INDEX_UNDEFINED
splitScreenController.startIntent(
- launchIntent, context.userId, fillIn, splitPosition,
- options.toBundle(), null /* hideTaskToken */,
- true /* forceLaunchNewTask */, splitIndex)
+ launchIntent,
+ context.userId,
+ fillIn,
+ splitPosition,
+ options.toBundle(),
+ null /* hideTaskToken */,
+ true /* forceLaunchNewTask */,
+ splitIndex,
+ )
}
WINDOWING_MODE_FREEFORM -> {
val wct = WindowContainerTransaction()
@@ -1562,36 +1581,39 @@
transitionType = TRANSIT_OPEN,
wct = wct,
launchingTaskId = null,
- displayId = callingTaskInfo.displayId
+ displayId = callingTaskInfo.displayId,
)
}
}
}
private fun createNewWindowOptions(callingTask: RunningTaskInfo): ActivityOptions {
- val newTaskWindowingMode = when {
- callingTask.isFreeform -> {
- WINDOWING_MODE_FREEFORM
+ val newTaskWindowingMode =
+ when {
+ callingTask.isFreeform -> {
+ WINDOWING_MODE_FREEFORM
+ }
+ callingTask.isFullscreen || callingTask.isMultiWindow -> {
+ WINDOWING_MODE_MULTI_WINDOW
+ }
+ else -> {
+ error("Invalid windowing mode: ${callingTask.windowingMode}")
+ }
}
- callingTask.isFullscreen || callingTask.isMultiWindow -> {
- WINDOWING_MODE_MULTI_WINDOW
+ val bounds =
+ when (newTaskWindowingMode) {
+ WINDOWING_MODE_FREEFORM -> {
+ displayController.getDisplayLayout(callingTask.displayId)?.let {
+ getInitialBounds(it, callingTask, callingTask.displayId)
+ }
+ }
+ WINDOWING_MODE_MULTI_WINDOW -> {
+ Rect()
+ }
+ else -> {
+ error("Invalid windowing mode: $newTaskWindowingMode")
+ }
}
- else -> {
- error("Invalid windowing mode: ${callingTask.windowingMode}")
- }
- }
- val bounds = when (newTaskWindowingMode) {
- WINDOWING_MODE_FREEFORM -> {
- displayController.getDisplayLayout(callingTask.displayId)
- ?.let { getInitialBounds(it, callingTask, callingTask.displayId) }
- }
- WINDOWING_MODE_MULTI_WINDOW -> {
- Rect()
- }
- else -> {
- error("Invalid windowing mode: $newTaskWindowingMode")
- }
- }
return ActivityOptions.makeBasic().apply {
launchWindowingMode = newTaskWindowingMode
pendingIntentBackgroundActivityStartMode =
@@ -1617,7 +1639,7 @@
private fun handleFreeformTaskLaunch(
task: RunningTaskInfo,
- transition: IBinder
+ transition: IBinder,
): WindowContainerTransaction? {
logV("handleFreeformTaskLaunch")
if (keyguardManager.isKeyguardLocked) {
@@ -1644,8 +1666,10 @@
// TODO(b/365723620): Handle non running tasks that were launched after reboot.
// If task is already visible, it must have been handled already and added to desktop mode.
// Cascade task only if it's not visible yet.
- if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue()
- && !taskRepository.isVisibleTask(task.taskId)) {
+ if (
+ DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue() &&
+ !taskRepository.isVisibleTask(task.taskId)
+ ) {
val displayLayout = displayController.getDisplayLayout(task.displayId)
if (displayLayout != null) {
val initialBounds = Rect(task.configuration.windowConfiguration.bounds)
@@ -1680,7 +1704,7 @@
private fun handleFullscreenTaskLaunch(
task: RunningTaskInfo,
- transition: IBinder
+ transition: IBinder,
): WindowContainerTransaction? {
logV("handleFullscreenTaskLaunch")
if (shouldFullscreenTaskLaunchSwitchToDesktop(task)) {
@@ -1691,8 +1715,10 @@
// that's not the case for launches in desktop. Also, if this launch is the first
// one to trigger the desktop mode (e.g., when [forceEnterDesktop()]), activate the
// desktop mode here.
- if (task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0
- || !isDesktopModeShowing(task.displayId)) {
+ if (
+ task.baseIntent.flags.and(Intent.FLAG_ACTIVITY_TASK_ON_HOME) != 0 ||
+ !isDesktopModeShowing(task.displayId)
+ ) {
bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
wct.reorder(task.token, true)
}
@@ -1706,7 +1732,7 @@
transition,
wct,
task.displayId,
- reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH
+ reason = DesktopImmersiveController.ExitReason.TASK_LAUNCH,
)
}
} else if (taskRepository.isActiveTask(task.taskId)) {
@@ -1737,10 +1763,13 @@
}
/** Handle task closing by removing wallpaper activity if it's the last active task */
- private fun handleTaskClosing(task: RunningTaskInfo, transition: IBinder, requestType: Int): WindowContainerTransaction? {
+ private fun handleTaskClosing(
+ task: RunningTaskInfo,
+ transition: IBinder,
+ requestType: Int,
+ ): WindowContainerTransaction? {
logV("handleTaskClosing")
- if (!isDesktopModeShowing(task.displayId))
- return null
+ if (!isDesktopModeShowing(task.displayId)) return null
val wct = WindowContainerTransaction()
performDesktopExitCleanupIfNeeded(task.taskId, wct)
@@ -1748,28 +1777,18 @@
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
taskRepository.addClosingTask(task.displayId, task.taskId)
desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
- } else if (requestType == TRANSIT_CLOSE) {
- // Handle closing tasks, tasks that are going to back are handled in
- // [DesktopTasksTransitionObserver].
- desktopMixedTransitionHandler.addPendingMixedTransition(
- DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
- transition, task.taskId, taskRepository.getVisibleTaskCount(task.displayId) == 1
- )
- )
}
+
taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
- doesAnyTaskRequireTaskbarRounding(
- task.displayId,
- task.taskId
- )
+ doesAnyTaskRequireTaskbarRounding(task.displayId, task.taskId)
)
return if (wct.isEmpty) null else wct
}
/**
* Apply all changes required when task is first added to desktop. Uses the task's current
- * display by default to apply initial bounds and placement relative to the display.
- * Use a different [displayId] if the task should be moved to a different display.
+ * display by default to apply initial bounds and placement relative to the display. Use a
+ * different [displayId] if the task should be moved to a different display.
*/
@VisibleForTesting
fun addMoveToDesktopChanges(
@@ -1804,11 +1823,12 @@
taskInfo: RunningTaskInfo,
displayId: Int,
): Rect {
- val bounds = if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
- calculateInitialBounds(displayLayout, taskInfo)
- } else {
- calculateDefaultDesktopTaskBounds(displayLayout)
- }
+ val bounds =
+ if (ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS.isTrue) {
+ calculateInitialBounds(displayLayout, taskInfo)
+ } else {
+ calculateDefaultDesktopTaskBounds(displayLayout)
+ }
if (DesktopModeFlags.ENABLE_CASCADING_WINDOWS.isTrue) {
cascadeWindow(bounds, displayLayout, displayId)
@@ -1818,7 +1838,7 @@
private fun addMoveToFullscreenChanges(
wct: WindowContainerTransaction,
- taskInfo: RunningTaskInfo
+ taskInfo: RunningTaskInfo,
) {
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
@@ -1845,8 +1865,12 @@
val activeTasks = taskRepository.getExpandedTasksOrdered(displayId)
activeTasks.firstOrNull()?.let { activeTask ->
shellTaskOrganizer.getRunningTaskInfo(activeTask)?.let {
- cascadeWindow(context.resources, stableBounds,
- it.configuration.windowConfiguration.bounds, bounds)
+ cascadeWindow(
+ context.resources,
+ stableBounds,
+ it.configuration.windowConfiguration.bounds,
+ bounds,
+ )
}
}
}
@@ -1872,24 +1896,19 @@
private fun addAndGetMinimizeChanges(
displayId: Int,
wct: WindowContainerTransaction,
- newTaskId: Int
+ newTaskId: Int,
): Int? {
if (!desktopTasksLimiter.isPresent) return null
- return desktopTasksLimiter
- .get()
- .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId)
+ return desktopTasksLimiter.get().addAndGetMinimizeTaskChanges(displayId, wct, newTaskId)
}
- private fun addPendingMinimizeTransition(
- transition: IBinder,
- taskIdToMinimize: Int,
- ) {
+ private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) {
val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize)
desktopTasksLimiter.ifPresent {
it.addPendingMinimizeChange(
transition = transition,
displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY,
- taskId = taskIdToMinimize
+ taskId = taskIdToMinimize,
)
}
}
@@ -1905,7 +1924,12 @@
// TODO b/359523924: pass immersive task here?
desktopMixedTransitionHandler.addPendingMixedTransition(
DesktopMixedTransitionHandler.PendingMixedTransition.Launch(
- transition, launchTaskId, minimizeTaskId, /* exitingImmersiveTask= */ null))
+ transition,
+ launchTaskId,
+ minimizeTaskId,
+ /* exitingImmersiveTask= */ null,
+ )
+ )
}
fun removeDesktop(displayId: Int) {
@@ -1940,10 +1964,7 @@
* changes if this transition is enabled.
*/
@JvmOverloads
- fun requestSplit(
- taskInfo: RunningTaskInfo,
- leftOrTop: Boolean = false
- ) {
+ fun requestSplit(taskInfo: RunningTaskInfo, leftOrTop: Boolean = false) {
// If a drag to desktop is in progress, we want to enter split select
// even if the requesting task is already in split.
val isDragging = dragToDesktopTransitionHandler.inProgress
@@ -1951,11 +1972,12 @@
if (shouldRequestSplit) {
if (isDragging) {
releaseVisualIndicator()
- val cancelState = if (leftOrTop) {
- DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT
- } else {
- DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT
- }
+ val cancelState =
+ if (leftOrTop) {
+ DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT
+ } else {
+ DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT
+ }
dragToDesktopTransitionHandler.cancelDragToDesktopTransition(cancelState)
} else {
val wct = WindowContainerTransaction()
@@ -1964,7 +1986,7 @@
taskInfo,
wct,
if (leftOrTop) SPLIT_POSITION_TOP_OR_LEFT else SPLIT_POSITION_BOTTOM_OR_RIGHT,
- taskInfo.configuration.windowConfiguration.bounds
+ taskInfo.configuration.windowConfiguration.bounds,
)
}
}
@@ -1999,12 +2021,17 @@
taskInfo: RunningTaskInfo,
taskSurface: SurfaceControl,
inputX: Float,
- taskBounds: Rect
+ taskBounds: Rect,
) {
if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
desktopTilingDecorViewModel.removeTaskIfTiled(taskInfo.displayId, taskInfo.taskId)
- updateVisualIndicator(taskInfo, taskSurface, inputX, taskBounds.top.toFloat(),
- DragStartState.FROM_FREEFORM)
+ updateVisualIndicator(
+ taskInfo,
+ taskSurface,
+ inputX,
+ taskBounds.top.toFloat(),
+ DragStartState.FROM_FREEFORM,
+ )
}
fun updateVisualIndicator(
@@ -2012,7 +2039,7 @@
taskSurface: SurfaceControl?,
inputX: Float,
taskTop: Float,
- dragStartState: DragStartState
+ dragStartState: DragStartState,
): DesktopModeVisualIndicator.IndicatorType {
// If the visual indicator does not exist, create it.
val indicator =
@@ -2024,7 +2051,7 @@
context,
taskSurface,
rootTaskDisplayAreaOrganizer,
- dragStartState
+ dragStartState,
)
if (visualIndicator == null) visualIndicator = indicator
return indicator.updateIndicatorType(PointF(inputX, taskTop))
@@ -2039,7 +2066,7 @@
* @param position position of surface when drag ends.
* @param inputCoordinate the coordinates of the motion event
* @param currentDragBounds the current bounds of where the visible task is (might be actual
- * task bounds or just task leash)
+ * task bounds or just task leash)
* @param validDragArea the bounds of where the task can be dragged within the display.
* @param dragStartBounds the bounds of the task before starting dragging.
*/
@@ -2061,7 +2088,7 @@
val indicator = getVisualIndicator() ?: return
val indicatorType =
indicator.updateIndicatorType(
- PointF(inputCoordinate.x, currentDragBounds.top.toFloat()),
+ PointF(inputCoordinate.x, currentDragBounds.top.toFloat())
)
when (indicatorType) {
IndicatorType.TO_FULLSCREEN_INDICATOR -> {
@@ -2070,19 +2097,19 @@
} else {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_FULL_SCREEN
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_FULL_SCREEN,
)
moveToFullscreenWithAnimation(
taskInfo,
position,
- DesktopModeTransitionSource.TASK_DRAG
+ DesktopModeTransitionSource.TASK_DRAG,
)
}
}
IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_LEFT
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_LEFT,
)
handleSnapResizingTaskOnDrag(
taskInfo,
@@ -2097,7 +2124,7 @@
IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HEADER_DRAG_TO_TILE_TO_RIGHT,
)
handleSnapResizingTaskOnDrag(
taskInfo,
@@ -2116,7 +2143,7 @@
// If task bounds are outside valid drag area, snap them inward
DragPositioningCallbackUtility.snapTaskBoundsIfNecessary(
destinationBounds,
- validDragArea
+ validDragArea,
)
if (destinationBounds == dragStartBounds) {
@@ -2148,8 +2175,9 @@
}
// A freeform drag-move ended, remove the indicator immediately.
releaseVisualIndicator()
- taskbarDesktopTaskListener
- ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId))
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+ doesAnyTaskRequireTaskbarRounding(taskInfo.displayId)
+ )
}
/**
@@ -2157,9 +2185,7 @@
*
* @param taskInfo the task being dragged.
*/
- fun onDragPositioningCancelThroughStatusBar(
- taskInfo: RunningTaskInfo,
- ) {
+ fun onDragPositioningCancelThroughStatusBar(taskInfo: RunningTaskInfo) {
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
cancelDragToDesktop(taskInfo)
}
@@ -2183,11 +2209,16 @@
when (indicatorType) {
IndicatorType.TO_DESKTOP_INDICATOR -> {
// Start a new jank interaction for the drag release to desktop window animation.
- interactionJankMonitor.begin(taskSurface, context, handler,
- CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE, "to_desktop")
+ interactionJankMonitor.begin(
+ taskSurface,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE,
+ "to_desktop",
+ )
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_DESKTOP_MODE
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_DESKTOP_MODE,
)
finalizeDragToDesktop(taskInfo)
}
@@ -2195,21 +2226,21 @@
IndicatorType.TO_FULLSCREEN_INDICATOR -> {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_FULL_SCREEN
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_FULL_SCREEN,
)
cancelDragToDesktop(taskInfo)
}
IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN,
)
requestSplit(taskInfo, leftOrTop = true)
}
IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
desktopModeUiEventLogger.log(
taskInfo,
- DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN
+ DesktopUiEventEnum.DESKTOP_WINDOW_APP_HANDLE_DRAG_TO_SPLIT_SCREEN,
)
requestSplit(taskInfo, leftOrTop = false)
}
@@ -2251,7 +2282,7 @@
override fun onUnhandledDrag(
launchIntent: PendingIntent,
dragEvent: DragEvent,
- onFinishCallback: Consumer<Boolean>
+ onFinishCallback: Consumer<Boolean>,
): Boolean {
// TODO(b/320797628): Pass through which display we are dropping onto
if (!isDesktopModeShowing(DEFAULT_DISPLAY)) {
@@ -2270,22 +2301,27 @@
// window will accept a drag event. This way, we can hide the indicator when we won't
// be handling the transition here, allowing us to display the indicator accurately.
// For now, we create the indicator only on drag end and immediately dispose it.
- val indicatorType = updateVisualIndicator(taskInfo, dragEvent.dragSurface,
- dragEvent.x, dragEvent.y,
- DragStartState.DRAGGED_INTENT)
+ val indicatorType =
+ updateVisualIndicator(
+ taskInfo,
+ dragEvent.dragSurface,
+ dragEvent.x,
+ dragEvent.y,
+ DragStartState.DRAGGED_INTENT,
+ )
releaseVisualIndicator()
- val windowingMode = when (indicatorType) {
- IndicatorType.TO_FULLSCREEN_INDICATOR -> {
- WINDOWING_MODE_FULLSCREEN
+ val windowingMode =
+ when (indicatorType) {
+ IndicatorType.TO_FULLSCREEN_INDICATOR -> {
+ WINDOWING_MODE_FULLSCREEN
+ }
+ IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+ IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+ IndicatorType.TO_DESKTOP_INDICATOR -> {
+ WINDOWING_MODE_FREEFORM
+ }
+ else -> error("Invalid indicator type: $indicatorType")
}
- IndicatorType.TO_SPLIT_LEFT_INDICATOR,
- IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
- IndicatorType.TO_DESKTOP_INDICATOR
- -> {
- WINDOWING_MODE_FREEFORM
- }
- else -> error("Invalid indicator type: $indicatorType")
- }
val displayLayout = displayController.getDisplayLayout(DEFAULT_DISPLAY) ?: return false
val newWindowBounds = Rect()
when (indicatorType) {
@@ -2294,7 +2330,7 @@
newWindowBounds.set(calculateDefaultDesktopTaskBounds(displayLayout))
newWindowBounds.offsetTo(
dragEvent.x.toInt() - (newWindowBounds.width() / 2),
- dragEvent.y.toInt()
+ dragEvent.y.toInt(),
)
}
IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
@@ -2342,7 +2378,9 @@
// TODO(b/366397912): Support full multi-user mode in Windowing.
override fun onUserChanged(newUserId: Int, userContext: Context) {
+ logV("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
userId = newUserId
+ taskRepository = userRepositories.getProfile(userId)
desktopTilingDecorViewModel.onUserChange()
}
@@ -2351,12 +2389,15 @@
if (!Flags.enableFullyImmersiveInDesktop()) return
val inImmersive = taskRepository.isTaskInFullImmersiveState(taskInfo.taskId)
val requestingImmersive = taskInfo.requestingImmersive
- if (inImmersive && !requestingImmersive
- && !RecentsTransitionStateListener.isRunning(recentsTransitionState)) {
+ if (
+ inImmersive &&
+ !requestingImmersive &&
+ !RecentsTransitionStateListener.isRunning(recentsTransitionState)
+ ) {
// Exit immersive if the app is no longer requesting it.
desktopImmersiveController.moveTaskToNonImmersive(
taskInfo,
- DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE
+ DesktopImmersiveController.ExitReason.APP_NOT_IMMERSIVE,
)
}
}
@@ -2365,6 +2406,7 @@
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopTasksController")
DesktopModeStatus.dump(pw, innerPrefix, context)
+ pw.println("${prefix}userId=$userId")
taskRepository.dump(pw, innerPrefix)
}
@@ -2373,7 +2415,7 @@
private inner class DesktopModeImpl : DesktopMode {
override fun addVisibleTasksListener(
listener: VisibleTasksListener,
- callbackExecutor: Executor
+ callbackExecutor: Executor,
) {
mainExecutor.execute {
this@DesktopTasksController.addVisibleTasksListener(listener, callbackExecutor)
@@ -2382,7 +2424,7 @@
override fun addDesktopGestureExclusionRegionListener(
listener: Consumer<Region>,
- callbackExecutor: Executor
+ callbackExecutor: Executor,
) {
mainExecutor.execute {
this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)
@@ -2391,8 +2433,9 @@
override fun moveFocusedTaskToDesktop(
displayId: Int,
- transitionSource: DesktopModeTransitionSource
+ transitionSource: DesktopModeTransitionSource,
) {
+ logV("moveFocusedTaskToDesktop")
mainExecutor.execute {
this@DesktopTasksController.moveFocusedTaskToDesktop(displayId, transitionSource)
}
@@ -2400,14 +2443,16 @@
override fun moveFocusedTaskToFullscreen(
displayId: Int,
- transitionSource: DesktopModeTransitionSource
+ transitionSource: DesktopModeTransitionSource,
) {
+ logV("moveFocusedTaskToFullscreen")
mainExecutor.execute {
this@DesktopTasksController.enterFullscreen(displayId, transitionSource)
}
}
override fun moveFocusedTaskToStageSplit(displayId: Int, leftOrTop: Boolean) {
+ logV("moveFocusedTaskToStageSplit")
mainExecutor.execute { this@DesktopTasksController.enterSplit(displayId, leftOrTop) }
}
}
@@ -2427,7 +2472,7 @@
WM_SHELL_DESKTOP_MODE,
"IDesktopModeImpl: onVisibilityChanged display=%d visible=%d",
displayId,
- visibleTasksCount
+ visibleTasksCount,
)
remoteListener.call { l ->
l.onTasksVisibilityChanged(displayId, visibleTasksCount)
@@ -2436,21 +2481,22 @@
}
private val taskbarDesktopTaskListener: TaskbarDesktopTaskListener =
- object : TaskbarDesktopTaskListener {
- override fun onTaskbarCornerRoundingUpdate(
- hasTasksRequiringTaskbarRounding: Boolean) {
- ProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
- "doesAnyTaskRequireTaskbarRounding=%s",
- hasTasksRequiringTaskbarRounding
- )
+ object : TaskbarDesktopTaskListener {
+ override fun onTaskbarCornerRoundingUpdate(
+ hasTasksRequiringTaskbarRounding: Boolean
+ ) {
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
+ "doesAnyTaskRequireTaskbarRounding=%s",
+ hasTasksRequiringTaskbarRounding,
+ )
- remoteListener.call { l ->
- l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
- }
+ remoteListener.call { l ->
+ l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
}
}
+ }
private val desktopModeEntryExitTransitionListener: DesktopModeEntryExitTransitionListener =
object : DesktopModeEntryExitTransitionListener {
@@ -2458,18 +2504,22 @@
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"IDesktopModeImpl: onEnterDesktopModeTransitionStarted transitionTime=%s",
- transitionDuration
+ transitionDuration,
)
- remoteListener.call { l -> l.onEnterDesktopModeTransitionStarted(transitionDuration) }
+ remoteListener.call { l ->
+ l.onEnterDesktopModeTransitionStarted(transitionDuration)
+ }
}
override fun onExitDesktopModeTransitionStarted(transitionDuration: Int) {
ProtoLog.v(
WM_SHELL_DESKTOP_MODE,
"IDesktopModeImpl: onExitDesktopModeTransitionStarted transitionTime=%s",
- transitionDuration
+ transitionDuration,
)
- remoteListener.call { l -> l.onExitDesktopModeTransitionStarted(transitionDuration) }
+ remoteListener.call { l ->
+ l.onExitDesktopModeTransitionStarted(transitionDuration)
+ }
}
}
@@ -2491,7 +2541,7 @@
c.taskbarDesktopTaskListener = null
c.desktopModeEnterExitTransitionListener = null
}
- }
+ },
)
}
@@ -2518,8 +2568,10 @@
}
override fun hideStashedDesktopApps(displayId: Int) {
- ProtoLog.w(WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: hideStashedDesktopApps is deprecated")
+ ProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: hideStashedDesktopApps is deprecated",
+ )
}
override fun getVisibleTaskCount(displayId: Int): Int {
@@ -2528,16 +2580,14 @@
controller,
"visibleTaskCount",
{ controller -> result[0] = controller.visibleTaskCount(displayId) },
- true /* blocking */
+ true, /* blocking */
)
return result[0]
}
override fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
- executeRemoteCallWithTaskPermission(
- controller,
- "onDesktopSplitSelectAnimComplete"
- ) { c ->
+ executeRemoteCallWithTaskPermission(controller, "onDesktopSplitSelectAnimComplete") { c
+ ->
c.onDesktopSplitSelectAnimComplete(taskInfo)
}
}
@@ -2571,9 +2621,11 @@
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+
private fun logW(msg: String, vararg arguments: Any?) {
ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
@@ -2608,6 +2660,6 @@
/** The positions on a screen that a task can snap to. */
enum class SnapPosition {
RIGHT,
- LEFT
+ LEFT,
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 77af627..635078e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -16,14 +16,15 @@
package com.android.wm.shell.desktopmode
+import android.app.ActivityManager
import android.content.Context
import android.os.Handler
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_TO_BACK
+import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
-import android.window.DesktopModeFlags
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
@@ -31,6 +32,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.UserChangeListener
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionObserver
@@ -38,35 +40,37 @@
* Limits the number of tasks shown in Desktop Mode.
*
* This class should only be used if
- * [android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT]
- * is enabled and [maxTasksLimit] is strictly greater than 0.
+ * [android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT] is enabled and
+ * [maxTasksLimit] is strictly greater than 0.
*/
-class DesktopTasksLimiter (
- transitions: Transitions,
- private val taskRepository: DesktopRepository,
- private val shellTaskOrganizer: ShellTaskOrganizer,
- private val maxTasksLimit: Int,
- private val interactionJankMonitor: InteractionJankMonitor,
- private val context: Context,
- @ShellMainThread private val handler: Handler,
+class DesktopTasksLimiter(
+ transitions: Transitions,
+ private val desktopUserRepositories: DesktopUserRepositories,
+ private val shellTaskOrganizer: ShellTaskOrganizer,
+ private val maxTasksLimit: Int,
+ private val interactionJankMonitor: InteractionJankMonitor,
+ private val context: Context,
+ @ShellMainThread private val handler: Handler,
) {
private val minimizeTransitionObserver = MinimizeTransitionObserver()
- @VisibleForTesting
- val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover()
+ @VisibleForTesting val leftoverMinimizedTasksRemover = LeftoverMinimizedTasksRemover()
+
+ private var userId: Int
init {
require(maxTasksLimit > 0) {
"DesktopTasksLimiter: maxTasksLimit should be greater than 0. Current value: $maxTasksLimit."
}
transitions.registerObserver(minimizeTransitionObserver)
- taskRepository.addActiveTaskListener(leftoverMinimizedTasksRemover)
+ userId = ActivityManager.getCurrentUser()
+ desktopUserRepositories.current.addActiveTaskListener(leftoverMinimizedTasksRemover)
logV("Starting limiter with a maximum of %d tasks", maxTasksLimit)
}
private data class TaskDetails(
val displayId: Int,
val taskId: Int,
- var transitionInfo: TransitionInfo?
+ var transitionInfo: TransitionInfo?,
)
// TODO(b/333018485): replace this observer when implementing the minimize-animation
@@ -82,8 +86,9 @@
transition: IBinder,
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction
+ finishTransaction: SurfaceControl.Transaction,
) {
+ val taskRepository = desktopUserRepositories.current
val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return
if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return
if (!isTaskReadyForMinimize(info, taskToMinimize)) {
@@ -94,12 +99,13 @@
activeTransitionTokensAndTasks[transition] = taskToMinimize
// Save current bounds before minimizing in case we need to restore to it later.
- val boundsBeforeMinimize = info.changes.find { change ->
- change.taskInfo?.taskId == taskToMinimize.taskId }?.startAbsBounds
+ val boundsBeforeMinimize =
+ info.changes
+ .find { change -> change.taskInfo?.taskId == taskToMinimize.taskId }
+ ?.startAbsBounds
taskRepository.saveBoundsBeforeMinimize(taskToMinimize.taskId, boundsBeforeMinimize)
- this@DesktopTasksLimiter.minimizeTask(
- taskToMinimize.displayId, taskToMinimize.taskId)
+ this@DesktopTasksLimiter.minimizeTask(taskToMinimize.displayId, taskToMinimize.taskId)
}
/**
@@ -110,10 +116,11 @@
*/
private fun isTaskReadyForMinimize(
info: TransitionInfo,
- taskDetails: TaskDetails
+ taskDetails: TaskDetails,
): Boolean {
- val taskChange = info.changes.find { change ->
- change.taskInfo?.taskId == taskDetails.taskId }
+ val taskChange =
+ info.changes.find { change -> change.taskInfo?.taskId == taskDetails.taskId }
+ val taskRepository = desktopUserRepositories.current
if (taskChange == null) return !taskRepository.isVisibleTask(taskDetails.taskId)
return taskChange.mode == TRANSIT_TO_BACK
}
@@ -123,8 +130,10 @@
if (mActiveTaskDetails != null && mActiveTaskDetails.transitionInfo != null) {
// Begin minimize window CUJ instrumentation.
interactionJankMonitor.begin(
- mActiveTaskDetails.transitionInfo?.rootLeash, context, handler,
- CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+ mActiveTaskDetails.transitionInfo?.rootLeash,
+ context,
+ handler,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
)
}
}
@@ -151,7 +160,8 @@
}
@VisibleForTesting
- inner class LeftoverMinimizedTasksRemover : DesktopRepository.ActiveTasksListener {
+ inner class LeftoverMinimizedTasksRemover :
+ DesktopRepository.ActiveTasksListener, UserChangeListener {
override fun onActiveTasksChanged(displayId: Int) {
// If back navigation is enabled, we shouldn't remove the leftover tasks
if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) return
@@ -161,6 +171,7 @@
}
fun removeLeftoverMinimizedTasks(displayId: Int, wct: WindowContainerTransaction) {
+ val taskRepository = desktopUserRepositories.current
if (taskRepository.getExpandedTasksOrdered(displayId).isNotEmpty()) return
val remainingMinimizedTasks = taskRepository.getMinimizedTasks(displayId)
if (remainingMinimizedTasks.isEmpty()) return
@@ -173,39 +184,46 @@
}
}
}
+
+ override fun onUserChanged(newUserId: Int, userContext: Context) {
+ // Removes active task listener for the previous repository
+ desktopUserRepositories.getProfile(userId).removeActiveTasksListener(this)
+
+ // Sets active listener for the current repository.
+ userId = newUserId
+ desktopUserRepositories.getProfile(newUserId).addActiveTaskListener(this)
+ }
}
/**
* Mark task with [taskId] on [displayId] as minimized.
*
- * This should be after the corresponding transition has finished so we don't
- * minimize the task if the transition fails.
+ * This should be after the corresponding transition has finished so we don't minimize the task
+ * if the transition fails.
*/
private fun minimizeTask(displayId: Int, taskId: Int) {
logV("Minimize taskId=%d, displayId=%d", taskId, displayId)
+ val taskRepository = desktopUserRepositories.current
taskRepository.minimizeTask(displayId, taskId)
}
/**
- * Adds a minimize-transition to [wct] if adding [newFrontTaskInfo] crosses task
- * limit, returning the task to minimize.
+ * Adds a minimize-transition to [wct] if adding [newFrontTaskInfo] crosses task limit,
+ * returning the task to minimize.
*/
fun addAndGetMinimizeTaskChanges(
- displayId: Int,
- wct: WindowContainerTransaction,
- newFrontTaskId: Int,
+ displayId: Int,
+ wct: WindowContainerTransaction,
+ newFrontTaskId: Int,
): Int? {
logV("addAndGetMinimizeTaskChanges, newFrontTask=%d", newFrontTaskId)
-
+ val taskRepository = desktopUserRepositories.current
val taskIdToMinimize =
- getTaskIdToMinimize(
- taskRepository.getExpandedTasksOrdered(displayId),
- newFrontTaskId
- )
+ getTaskIdToMinimize(taskRepository.getExpandedTasksOrdered(displayId), newFrontTaskId)
// If it's a running task, reorder it to back.
- taskIdToMinimize?.let { shellTaskOrganizer.getRunningTaskInfo(it) }?.let {
- wct.reorder(it.token, false /* onTop */)
- }
+ taskIdToMinimize
+ ?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
+ ?.let { wct.reorder(it.token, false /* onTop */) }
return taskIdToMinimize
}
@@ -215,20 +233,19 @@
*/
fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) {
minimizeTransitionObserver.addPendingTransitionToken(
- transition, TaskDetails(displayId, taskId, transitionInfo = null))
+ transition,
+ TaskDetails(displayId, taskId, transitionInfo = null),
+ )
}
/**
- * Returns the minimized task from the list of visible tasks ordered from front to back with
- * the new task placed in front of other tasks.
+ * Returns the minimized task from the list of visible tasks ordered from front to back with the
+ * new task placed in front of other tasks.
*/
- fun getTaskIdToMinimize(
- visibleOrderedTasks: List<Int>,
- newTaskIdInFront: Int? = null
- ): Int? {
+ fun getTaskIdToMinimize(visibleOrderedTasks: List<Int>, newTaskIdInFront: Int? = null): Int? {
return getTaskIdToMinimize(
- createOrderedTaskListWithGivenTaskInFront(
- visibleOrderedTasks, newTaskIdInFront))
+ createOrderedTaskListWithGivenTaskInFront(visibleOrderedTasks, newTaskIdInFront)
+ )
}
/** Returns the Task to minimize given a list of visible tasks ordered from front to back. */
@@ -242,16 +259,16 @@
}
private fun createOrderedTaskListWithGivenTaskInFront(
- existingTaskIdsOrderedFrontToBack: List<Int>,
- newTaskId: Int?
+ existingTaskIdsOrderedFrontToBack: List<Int>,
+ newTaskId: Int?,
): List<Int> {
return if (newTaskId == null) existingTaskIdsOrderedFrontToBack
- else listOf(newTaskId) +
+ else
+ listOf(newTaskId) +
existingTaskIdsOrderedFrontToBack.filter { taskId -> taskId != newTaskId }
}
- @VisibleForTesting
- fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
+ @VisibleForTesting fun getTransitionObserver(): TransitionObserver = minimizeTransitionObserver
private fun logV(msg: String, vararg arguments: Any?) {
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index c39c715..9625b71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import android.app.ActivityManager
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.os.IBinder
@@ -23,12 +24,13 @@
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_TO_BACK
-import android.window.TransitionInfo
-import android.window.WindowContainerTransaction
import android.window.DesktopModeFlags
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
+import android.window.TransitionInfo
+import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
@@ -43,19 +45,22 @@
*/
class DesktopTasksTransitionObserver(
private val context: Context,
- private val desktopRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val transitions: Transitions,
private val shellTaskOrganizer: ShellTaskOrganizer,
private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
- shellInit: ShellInit
+ private val backAnimationController: BackAnimationController,
+ shellInit: ShellInit,
) : Transitions.TransitionObserver {
private var transitionToCloseWallpaper: IBinder? = null
+ private var currentProfileId: Int
init {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(::onInit, this)
}
+ currentProfileId = ActivityManager.getCurrentUser()
}
fun onInit() {
@@ -67,7 +72,7 @@
transition: IBinder,
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction
+ finishTransaction: SurfaceControl.Transaction,
) {
// TODO: b/332682201 Update repository state
updateWallpaperToken(info)
@@ -89,8 +94,11 @@
val taskInfo = change.taskInfo
if (taskInfo == null || taskInfo.taskId == -1) continue
- if (desktopRepository.isActiveTask(taskInfo.taskId) &&
- taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) {
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ if (
+ desktopRepository.isActiveTask(taskInfo.taskId) &&
+ taskInfo.windowingMode != WINDOWING_MODE_FREEFORM
+ ) {
desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId)
}
}
@@ -105,35 +113,103 @@
if (taskInfo == null || taskInfo.taskId == -1) {
continue
}
-
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
- if (visibleTaskCount > 0 &&
- change.mode == TRANSIT_TO_BACK &&
- taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (
+ visibleTaskCount > 0 &&
+ change.mode == TRANSIT_TO_BACK &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
+ ) {
desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
desktopMixedTransitionHandler.addPendingMixedTransition(
DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
- transition, taskInfo.taskId, visibleTaskCount == 1))
+ transition,
+ taskInfo.taskId,
+ visibleTaskCount == 1,
+ )
+ )
}
}
+ } else if (info.type == TRANSIT_CLOSE) {
+ // In some cases app will be closing as a result of back navigation but we would like
+ // to minimize. Mark the task closing as minimized.
+ var hasWallpaperClosing = false
+ var minimizingTask: Int? = null
+ for (change in info.changes) {
+ val taskInfo = change.taskInfo
+ if (taskInfo == null || taskInfo.taskId == -1) continue
+ if (change.mode != TRANSIT_CLOSE) continue
+
+ if (minimizingTask == null) {
+ minimizingTask = getMinimizingTaskForClosingTransition(taskInfo)
+ }
+
+ if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
+ hasWallpaperClosing = true
+ }
+ }
+
+ if (minimizingTask == null) return
+ // If the transition has wallpaper closing, it means we are moving out of desktop.
+ desktopMixedTransitionHandler.addPendingMixedTransition(
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition,
+ minimizingTask,
+ isLastTask = hasWallpaperClosing,
+ )
+ )
}
}
+ /**
+ * Given this a closing task in a closing transition, a task is assumed to be closed by back
+ * navigation if:
+ * 1) Desktop mode is visible.
+ * 2) Task is in freeform.
+ * 3) Task is the latest task that the back gesture is triggered on.
+ * 4) It's not marked as a closing task as a result of closing it by the app header.
+ *
+ * This doesn't necessarily mean all the cases are because of back navigation but those cases
+ * will be rare. E.g. triggering back navigation on an app that pops up a close dialog, and
+ * closing it will minimize it here.
+ */
+ private fun getMinimizingTaskForClosingTransition(
+ taskInfo: ActivityManager.RunningTaskInfo
+ ): Int? {
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
+ if (
+ visibleTaskCount > 0 &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
+ backAnimationController.latestTriggerBackTask == taskInfo.taskId &&
+ !desktopRepository.isClosingTask(taskInfo.taskId)
+ ) {
+ desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
+ return taskInfo.taskId
+ }
+ return null
+ }
+
private fun removeWallpaperOnLastTaskClosingIfNeeded(
transition: IBinder,
- info: TransitionInfo
+ info: TransitionInfo,
) {
+ // TODO: 380868195 - Smooth animation for wallpaper activity closing just by itself
for (change in info.changes) {
val taskInfo = change.taskInfo
if (taskInfo == null || taskInfo.taskId == -1) {
continue
}
- if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 1 &&
- change.mode == TRANSIT_CLOSE &&
- taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
- desktopRepository.wallpaperActivityToken != null) {
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
+ if (
+ desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 0 &&
+ change.mode == TRANSIT_CLOSE &&
+ taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
+ desktopRepository.wallpaperActivityToken != null
+ ) {
transitionToCloseWallpaper = transition
+ currentProfileId = taskInfo.userId
}
}
}
@@ -150,11 +226,13 @@
// TODO: b/332682201 Update repository state
if (transitionToCloseWallpaper == transition) {
// TODO: b/362469671 - Handle merging the animation when desktop is also closing.
+ val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
desktopRepository.wallpaperActivityToken?.let { wallpaperActivityToken ->
transitions.startTransition(
TRANSIT_CLOSE,
WindowContainerTransaction().removeTask(wallpaperActivityToken),
- null)
+ null,
+ )
}
transitionToCloseWallpaper = null
}
@@ -167,6 +245,7 @@
info.changes.forEach { change ->
change.taskInfo?.let { taskInfo ->
if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
+ val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
when (change.mode) {
WindowManager.TRANSIT_OPEN -> {
desktopRepository.wallpaperActivityToken = taskInfo.token
@@ -175,10 +254,10 @@
// task.
shellTaskOrganizer.applyTransaction(
WindowContainerTransaction()
- .setTaskTrimmableFromRecents(taskInfo.token, false))
+ .setTaskTrimmableFromRecents(taskInfo.token, false)
+ )
}
- TRANSIT_CLOSE ->
- desktopRepository.wallpaperActivityToken = null
+ TRANSIT_CLOSE -> desktopRepository.wallpaperActivityToken = null
else -> {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
new file mode 100644
index 0000000..e5f5283
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserManager
+import android.util.SparseArray
+import com.android.internal.protolog.ProtoLog
+import com.android.window.flags.Flags
+import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
+import kotlinx.coroutines.CoroutineScope
+
+/** Manages per-user DesktopRepository instances. */
+class DesktopUserRepositories(
+ context: Context,
+ shellInit: ShellInit,
+ private val persistentRepository: DesktopPersistentRepository,
+ private val repositoryInitializer: DesktopRepositoryInitializer,
+ @ShellMainThread private val mainCoroutineScope: CoroutineScope,
+ userManager: UserManager,
+) : UserChangeListener {
+ private var userId: Int
+ private var userIdToProfileIdsMap: MutableMap<Int, List<Int>> = mutableMapOf()
+
+ // TODO(b/357060209): Add caching for this logic to improve efficiency.
+ val current: DesktopRepository
+ get() = desktopRepoByUserId.getOrCreate(userId)
+
+ private val desktopRepoByUserId =
+ object : SparseArray<DesktopRepository>() {
+ /** Gets [DesktopRepository] for existing [userId] or creates a new one. */
+ fun getOrCreate(userId: Int): DesktopRepository =
+ this[userId]
+ ?: DesktopRepository(persistentRepository, mainCoroutineScope, userId).also {
+ this[userId] = it
+ }
+ }
+
+ init {
+ userId = ActivityManager.getCurrentUser()
+ if (DesktopModeStatus.canEnterDesktopMode(context)) {
+ shellInit.addInitCallback(::initRepoFromPersistentStorage, this)
+ }
+ if (Flags.enableDesktopWindowingHsum()) {
+ userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
+ }
+ }
+
+ private fun initRepoFromPersistentStorage() {
+ repositoryInitializer.initialize(this)
+ }
+
+ /** Returns [DesktopRepository] for the parent user id. */
+ fun getProfile(profileId: Int): DesktopRepository {
+ if (Flags.enableDesktopWindowingHsum()) {
+ for ((uid, profileIds) in userIdToProfileIdsMap) {
+ if (profileId in profileIds) {
+ return desktopRepoByUserId.getOrCreate(uid)
+ }
+ }
+ }
+ return desktopRepoByUserId.getOrCreate(profileId)
+ }
+
+ override fun onUserChanged(newUserId: Int, userContext: Context) {
+ logD("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
+ userId = newUserId
+ }
+
+ override fun onUserProfilesChanged(profiles: MutableList<UserInfo>) {
+ logD("onUserProfilesChanged profiles=%s", profiles.toString())
+ if (Flags.enableDesktopWindowingHsum()) {
+ // TODO(b/366397912): Remove all persisted profile data when the profile changes.
+ userIdToProfileIdsMap[userId] = profiles.map { it.id }
+ }
+ }
+
+ private fun logD(msg: String, vararg arguments: Any?) {
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ companion object {
+ private const val TAG = "DesktopUserRepositories"
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
index 909a066..fd39becc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt
@@ -49,7 +49,6 @@
taskInfo.baseIntent.component?.let(::isWallpaperComponent) ?: false
@JvmStatic
- fun isWallpaperComponent(component: ComponentName) =
- component == wallpaperActivityComponent
+ fun isWallpaperComponent(component: ComponentName) = component == wallpaperActivityComponent
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index d7d5519..d76eef5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -108,14 +108,11 @@
* Note that the transition handler for this transition doesn't call the finish callback until
* after one of the "end" or "cancel" transitions is merged into this transition.
*/
- fun startDragToDesktopTransition(
- taskId: Int,
- dragToDesktopAnimator: MoveToDesktopAnimator,
- ) {
+ fun startDragToDesktopTransition(taskId: Int, dragToDesktopAnimator: MoveToDesktopAnimator) {
if (inProgress) {
ProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
- "DragToDesktop: Drag to desktop transition already in progress."
+ "DragToDesktop: Drag to desktop transition already in progress.",
)
return
}
@@ -133,7 +130,7 @@
0 /* requestCode */,
launchHomeIntent,
FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
- options.toBundle()
+ options.toBundle(),
)
val wct = WindowContainerTransaction()
wct.sendPendingIntent(pendingIntent, launchHomeIntent, Bundle())
@@ -149,13 +146,13 @@
draggedTaskId = taskId,
dragAnimator = dragToDesktopAnimator,
startTransitionToken = startTransitionToken,
- otherSplitTask = otherTask
+ otherSplitTask = otherTask,
)
} else {
TransitionState.FromFullscreen(
draggedTaskId = taskId,
dragAnimator = dragToDesktopAnimator,
- startTransitionToken = startTransitionToken
+ startTransitionToken = startTransitionToken,
)
}
}
@@ -244,7 +241,7 @@
/** Calculate the bounds of a scaled task, then use those bounds to request split select. */
private fun requestSplitFromScaledTask(
@SplitPosition splitPosition: Int,
- wct: WindowContainerTransaction
+ wct: WindowContainerTransaction,
) {
val state = requireTransitionState()
val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
@@ -259,7 +256,7 @@
dragPosition.x.toInt(),
dragPosition.y.toInt(),
(dragPosition.x + scaledWidth).toInt(),
- (dragPosition.y + scaledHeight).toInt()
+ (dragPosition.y + scaledHeight).toInt(),
)
requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds)
}
@@ -268,14 +265,14 @@
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo,
@SplitPosition splitPosition: Int,
- taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds)
+ taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds),
) {
// Prepare to exit split in order to enter split select.
if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
splitScreenController.prepareExitSplitScreen(
wct,
splitScreenController.getStageOfTask(taskInfo.taskId),
- SplitScreenController.EXIT_REASON_DESKTOP_MODE
+ SplitScreenController.EXIT_REASON_DESKTOP_MODE,
)
splitScreenController.transitionHandler.onSplitToDesktop()
}
@@ -289,7 +286,7 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
val state = requireTransitionState()
@@ -387,7 +384,7 @@
taskDisplayAreaOrganizer.reparentToDisplayArea(
change.endDisplayId,
change.leash,
- startTransaction
+ startTransaction,
)
val bounds = change.endAbsBounds
startTransaction.apply {
@@ -454,7 +451,7 @@
info: TransitionInfo,
t: SurfaceControl.Transaction,
mergeTarget: IBinder,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
) {
val state = requireTransitionState()
// We don't want to merge the split select animation if that's what we requested.
@@ -483,7 +480,7 @@
setupEndDragToDesktop(
info,
startTransaction = t,
- finishTransaction = startTransactionFinishT
+ finishTransaction = startTransactionFinishT,
)
// Call finishCallback to merge animation before startTransitionFinishCb is called
finishCallback.onTransitionFinished(null /* wct */)
@@ -503,7 +500,7 @@
protected open fun setupEndDragToDesktop(
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction
+ finishTransaction: SurfaceControl.Transaction,
) {
val state = requireTransitionState()
val freeformTaskChanges = mutableListOf<Change>()
@@ -545,7 +542,7 @@
protected open fun animateEndDragToDesktop(
startTransaction: SurfaceControl.Transaction,
- startTransitionFinishCb: Transitions.TransitionFinishCallback
+ startTransitionFinishCb: Transitions.TransitionFinishCallback,
) {
val state = requireTransitionState()
val draggedTaskChange =
@@ -568,7 +565,7 @@
startPosition.x.toInt(),
startPosition.y.toInt(),
startPosition.x.toInt() + unscaledStartWidth,
- startPosition.y.toInt() + unscaledStartHeight
+ startPosition.y.toInt() + unscaledStartHeight,
)
dragToDesktopStateListener?.onCommitToDesktopAnimationStart(startTransaction)
@@ -578,7 +575,7 @@
onTaskResizeAnimationListener.onAnimationStart(
state.draggedTaskId,
startTransaction,
- unscaledStartBounds
+ unscaledStartBounds,
)
val tx: SurfaceControl.Transaction = transactionSupplier.get()
ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
@@ -594,21 +591,21 @@
setPosition(
draggedTaskLeash,
animBounds.left.toFloat(),
- animBounds.top.toFloat()
+ animBounds.top.toFloat(),
)
setWindowCrop(draggedTaskLeash, animBounds.width(), animBounds.height())
}
onTaskResizeAnimationListener.onBoundsChange(
state.draggedTaskId,
tx,
- animBounds
+ animBounds,
)
}
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
- startTransitionFinishCb.onTransitionFinished(/* wct = */ null)
+ startTransitionFinishCb.onTransitionFinished(/* wct= */ null)
clearState()
interactionJankMonitor.end(
CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
@@ -622,7 +619,7 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? {
// Only handle transitions started from shell.
return null
@@ -631,7 +628,7 @@
override fun onTransitionConsumed(
transition: IBinder,
aborted: Boolean,
- finishTransaction: SurfaceControl.Transaction?
+ finishTransaction: SurfaceControl.Transaction?,
) {
val state = transitionState ?: return
if (!aborted) {
@@ -640,15 +637,13 @@
if (state.startTransitionToken == transition) {
ProtoLog.v(
ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
- "DragToDesktop: onTransitionConsumed() start transition aborted"
+ "DragToDesktop: onTransitionConsumed() start transition aborted",
)
state.startAborted = true
// The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction.
interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
} else if (state.cancelTransitionToken == transition) {
- state.draggedTaskChange?.leash?.let {
- state.startTransitionFinishTransaction?.show(it)
- }
+ state.draggedTaskChange?.leash?.let { state.startTransitionFinishTransaction?.show(it) }
state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
clearState()
} else {
@@ -724,7 +719,7 @@
private fun restoreWindowOrder(
wct: WindowContainerTransaction,
- state: TransitionState = requireTransitionState()
+ state: TransitionState = requireTransitionState(),
) {
when (state) {
is TransitionState.FromFullscreen -> {
@@ -831,7 +826,7 @@
override var surfaceLayers: DragToDesktopLayers? = null,
override var cancelState: CancelState = CancelState.NO_CANCEL,
override var startAborted: Boolean = false,
- var otherRootChanges: MutableList<Change> = mutableListOf()
+ var otherRootChanges: MutableList<Change> = mutableListOf(),
) : TransitionState()
data class FromSplit(
@@ -848,7 +843,7 @@
override var cancelState: CancelState = CancelState.NO_CANCEL,
override var startAborted: Boolean = false,
var splitRootChange: Change? = null,
- var otherSplitTask: Int
+ var otherSplitTask: Int,
) : TransitionState()
}
@@ -861,7 +856,7 @@
/** A cancel event where the task will request to enter split on the left side. */
CANCEL_SPLIT_LEFT,
/** A cancel event where the task will request to enter split on the right side. */
- CANCEL_SPLIT_RIGHT
+ CANCEL_SPLIT_RIGHT,
}
companion object {
@@ -888,7 +883,7 @@
transitions,
taskDisplayAreaOrganizer,
interactionJankMonitor,
- transactionSupplier
+ transactionSupplier,
) {
/**
@@ -903,7 +898,7 @@
topAppLayer = info.changes.size,
topHomeLayer = info.changes.size * 2,
topWallpaperLayer = info.changes.size * 3,
- dragLayer = info.changes.size * 3
+ dragLayer = info.changes.size * 3,
)
}
@@ -924,7 +919,7 @@
transitions,
taskDisplayAreaOrganizer,
interactionJankMonitor,
- transactionSupplier
+ transactionSupplier,
) {
private val positionSpringConfig =
@@ -945,13 +940,13 @@
topAppLayer = -1,
topHomeLayer = info.changes.size - 1,
topWallpaperLayer = info.changes.size * 2 - 1,
- dragLayer = info.changes.size * 2
+ dragLayer = info.changes.size * 2,
)
override fun setupEndDragToDesktop(
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
- finishTransaction: SurfaceControl.Transaction
+ finishTransaction: SurfaceControl.Transaction,
) {
super.setupEndDragToDesktop(info, startTransaction, finishTransaction)
@@ -974,7 +969,7 @@
override fun animateEndDragToDesktop(
startTransaction: SurfaceControl.Transaction,
- startTransitionFinishCb: Transitions.TransitionFinishCallback
+ startTransitionFinishCb: Transitions.TransitionFinishCallback,
) {
val state = requireTransitionState()
val draggedTaskChange =
@@ -1002,7 +997,7 @@
onTaskResizeAnimationListener.onAnimationStart(
state.draggedTaskId,
startTransaction,
- startBoundsWithOffset
+ startBoundsWithOffset,
)
val tx: SurfaceControl.Transaction = transactionSupplier.get()
@@ -1011,13 +1006,13 @@
FloatProperties.RECT_X,
endBounds.left.toFloat(),
currentVelocity.x,
- positionSpringConfig
+ positionSpringConfig,
)
.spring(
FloatProperties.RECT_Y,
endBounds.top.toFloat(),
currentVelocity.y,
- positionSpringConfig
+ positionSpringConfig,
)
.spring(FloatProperties.RECT_WIDTH, endBounds.width().toFloat(), sizeSpringConfig)
.spring(FloatProperties.RECT_HEIGHT, endBounds.height().toFloat(), sizeSpringConfig)
@@ -1050,7 +1045,7 @@
setPosition(
draggedTaskLeash,
animBounds.left.toFloat(),
- animBounds.top.toFloat()
+ animBounds.top.toFloat(),
)
// Update freeform tasks
freeformTaskChanges.forEach {
@@ -1069,7 +1064,7 @@
}
.withEndActions({
onTaskResizeAnimationListener.onAnimationEnd(state.draggedTaskId)
- startTransitionFinishCb.onTransitionFinished(/* wct = */ null)
+ startTransitionFinishCb.onTransitionFinished(/* wct= */ null)
clearState()
interactionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
})
@@ -1094,7 +1089,7 @@
propertyValue(
"position_damping_ratio",
scale = 100f,
- default = SpringForce.DAMPING_RATIO_LOW_BOUNCY
+ default = SpringForce.DAMPING_RATIO_LOW_BOUNCY,
)
/** The spring force stiffness used to resize the window into the final bounds. */
@@ -1106,7 +1101,7 @@
propertyValue(
"size_damping_ratio",
scale = 100f,
- default = SpringForce.DAMPING_RATIO_NO_BOUNCY
+ default = SpringForce.DAMPING_RATIO_NO_BOUNCY,
)
/** Drag to desktop transition system properties group. */
@@ -1123,7 +1118,7 @@
fun propertyValue(name: String, scale: Float = 1f, default: Float = 0f): Float =
SystemProperties.getInt(
/* key= */ "$SYSTEM_PROPERTIES_GROUP.$name",
- /* def= */ (default * scale).toInt()
+ /* def= */ (default * scale).toInt(),
) / scale
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
index 4e08d10..3edeecb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt
@@ -30,13 +30,14 @@
/** Animates the task surface moving from its current drag position to its pre-drag position. */
class ReturnToDragStartAnimator(
private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
- private val interactionJankMonitor: InteractionJankMonitor
+ private val interactionJankMonitor: InteractionJankMonitor,
) {
private var boundsAnimator: Animator? = null
private lateinit var taskRepositionAnimationListener: OnTaskRepositionAnimationListener
- constructor(interactionJankMonitor: InteractionJankMonitor) :
- this(Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
+ constructor(
+ interactionJankMonitor: InteractionJankMonitor
+ ) : this(Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
/** Sets a listener for the start and end of the reposition animation. */
fun setTaskRepositionAnimationListener(listener: OnTaskRepositionAnimationListener) {
@@ -65,7 +66,7 @@
.setPosition(
taskSurface,
startBounds.left.toFloat(),
- startBounds.top.toFloat()
+ startBounds.top.toFloat(),
)
.show(taskSurface)
.apply()
@@ -77,7 +78,7 @@
.setPosition(
taskSurface,
endBounds.left.toFloat(),
- endBounds.top.toFloat()
+ endBounds.top.toFloat(),
)
.show(taskSurface)
.apply()
@@ -85,7 +86,7 @@
boundsAnimator = null
doOnEnd?.invoke()
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
- }
+ },
)
addUpdateListener { anim ->
val rect = anim.animatedValue as Rect
@@ -100,4 +101,4 @@
companion object {
const val RETURN_TO_DRAG_START_ANIMATION_MS = 300L
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
index 6df3302..e683f62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt
@@ -39,7 +39,7 @@
class ToggleResizeDesktopTaskTransitionHandler(
private val transitions: Transitions,
private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
- private val interactionJankMonitor: InteractionJankMonitor
+ private val interactionJankMonitor: InteractionJankMonitor,
) : Transitions.TransitionHandler {
private val rectEvaluator = RectEvaluator(Rect())
@@ -50,16 +50,16 @@
constructor(
transitions: Transitions,
- interactionJankMonitor: InteractionJankMonitor
+ interactionJankMonitor: InteractionJankMonitor,
) : this(transitions, Supplier { SurfaceControl.Transaction() }, interactionJankMonitor)
/**
* Starts a quick resize transition.
*
- * @param wct WindowContainerTransaction that will update core about the task changes applied
- * @param taskLeashBounds current bounds of the task leash (Note: not guaranteed to be the
- * bounds of the actual task). This is provided so that the animation
- * resizing can begin where the task leash currently is for smoother UX.
+ * @param wct WindowContainerTransaction that will update core about the task changes applied
+ * @param taskLeashBounds current bounds of the task leash (Note: not guaranteed to be the
+ * bounds of the actual task). This is provided so that the animation resizing can begin where
+ * the task leash currently is for smoother UX.
*/
fun startTransition(wct: WindowContainerTransaction, taskLeashBounds: Rect? = null) {
transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this)
@@ -75,7 +75,7 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
val change = findRelevantChange(info)
val leash = change.leash
@@ -95,7 +95,7 @@
.setPosition(
leash,
startBounds.left.toFloat(),
- startBounds.top.toFloat()
+ startBounds.top.toFloat(),
)
.setWindowCrop(leash, startBounds.width(), startBounds.height())
.show(leash)
@@ -110,7 +110,7 @@
.setPosition(
leash,
endBounds.left.toFloat(),
- endBounds.top.toFloat()
+ endBounds.top.toFloat(),
)
.setWindowCrop(leash, endBounds.width(), endBounds.height())
.show(leash)
@@ -121,7 +121,7 @@
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW)
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW)
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)
- }
+ },
)
addUpdateListener { anim ->
val rect = anim.animatedValue as Rect
@@ -138,7 +138,7 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? {
return null
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
index 8bfcca0..131b748 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/WindowDecorCaptionHandleRepository.kt
@@ -27,23 +27,22 @@
/** Repository to observe caption state. */
class WindowDecorCaptionHandleRepository {
- private val _captionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
- /** Observer for app handle state changes. */
- val captionStateFlow: StateFlow<CaptionState> = _captionStateFlow
- private val _appToWebUsageFlow = MutableSharedFlow<Unit>()
- /** Observer for App-to-Web usage. */
- val appToWebUsageFlow = _appToWebUsageFlow
+ private val _captionStateFlow = MutableStateFlow<CaptionState>(CaptionState.NoCaption)
+ /** Observer for app handle state changes. */
+ val captionStateFlow: StateFlow<CaptionState> = _captionStateFlow
+ private val _appToWebUsageFlow = MutableSharedFlow<Unit>()
+ /** Observer for App-to-Web usage. */
+ val appToWebUsageFlow = _appToWebUsageFlow
+ /** Notifies [captionStateFlow] if there is a change to caption state. */
+ fun notifyCaptionChanged(captionState: CaptionState) {
+ _captionStateFlow.value = captionState
+ }
- /** Notifies [captionStateFlow] if there is a change to caption state. */
- fun notifyCaptionChanged(captionState: CaptionState) {
- _captionStateFlow.value = captionState
- }
-
- /** Notifies [appToWebUsageFlow] if App-to-Web feature is used. */
- fun onAppToWebUsage() {
- _appToWebUsageFlow.tryEmit(Unit)
- }
+ /** Notifies [appToWebUsageFlow] if App-to-Web feature is used. */
+ fun onAppToWebUsage() {
+ _appToWebUsageFlow.tryEmit(Unit)
+ }
}
/**
@@ -54,20 +53,20 @@
* * [AppHeader]: Indicating that there is at least one visible app chip on the screen.
* * [NoCaption]: Signifying that no caption handle is currently visible on the device.
*/
-sealed class CaptionState{
- data class AppHandle(
- val runningTaskInfo: RunningTaskInfo,
- val isHandleMenuExpanded: Boolean,
- val globalAppHandleBounds: Rect,
- val isCapturedLinkAvailable: Boolean
- ) : CaptionState()
+sealed class CaptionState {
+ data class AppHandle(
+ val runningTaskInfo: RunningTaskInfo,
+ val isHandleMenuExpanded: Boolean,
+ val globalAppHandleBounds: Rect,
+ val isCapturedLinkAvailable: Boolean,
+ ) : CaptionState()
- data class AppHeader(
- val runningTaskInfo: RunningTaskInfo,
- val isHeaderMenuExpanded: Boolean,
- val globalAppChipBounds: Rect,
- val isCapturedLinkAvailable: Boolean
- ) : CaptionState()
+ data class AppHeader(
+ val runningTaskInfo: RunningTaskInfo,
+ val isHeaderMenuExpanded: Boolean,
+ val globalAppChipBounds: Rect,
+ val isCapturedLinkAvailable: Boolean,
+ ) : CaptionState()
- data object NoCaption : CaptionState()
+ data object NoCaption : CaptionState()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
new file mode 100644
index 0000000..7afd8d7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/ToggleTaskSizeUtils.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.desktopmode.common
+
+import com.android.internal.jank.Cuj
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
+import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction.AmbiguousSource
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction.Source
+
+/** Represents a user interaction to toggle a desktop task's size from to maximize or vice versa. */
+data class ToggleTaskSizeInteraction(
+ val direction: Direction,
+ val source: Source,
+ val inputMethod: InputMethod,
+) {
+ constructor(
+ isMaximized: Boolean,
+ source: Source,
+ inputMethod: InputMethod,
+ ) : this(
+ direction = if (isMaximized) Direction.RESTORE else Direction.MAXIMIZE,
+ source = source,
+ inputMethod = inputMethod,
+ )
+
+ val jankTag: String? =
+ when (source) {
+ Source.HEADER_BUTTON_TO_MAXIMIZE -> "caption_bar_button"
+ Source.HEADER_BUTTON_TO_RESTORE -> "caption_bar_button"
+ Source.KEYBOARD_SHORTCUT -> null
+ Source.HEADER_DRAG_TO_TOP -> null
+ Source.MAXIMIZE_MENU_TO_MAXIMIZE -> "maximize_menu"
+ Source.MAXIMIZE_MENU_TO_RESTORE -> "maximize_menu"
+ Source.DOUBLE_TAP_TO_MAXIMIZE -> "double_tap"
+ Source.DOUBLE_TAP_TO_RESTORE -> "double_tap"
+ }
+ val uiEvent: DesktopUiEventEnum? =
+ when (source) {
+ Source.HEADER_BUTTON_TO_MAXIMIZE ->
+ DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP
+ Source.HEADER_BUTTON_TO_RESTORE -> DesktopUiEventEnum.DESKTOP_WINDOW_RESTORE_BUTTON_TAP
+ Source.KEYBOARD_SHORTCUT -> null
+ Source.HEADER_DRAG_TO_TOP -> null
+ Source.MAXIMIZE_MENU_TO_MAXIMIZE -> {
+ DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_MAXIMIZE
+ }
+ Source.MAXIMIZE_MENU_TO_RESTORE -> {
+ DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE
+ }
+ Source.DOUBLE_TAP_TO_MAXIMIZE -> {
+ DesktopUiEventEnum.DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE
+ }
+ Source.DOUBLE_TAP_TO_RESTORE -> {
+ DesktopUiEventEnum.DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_RESTORE
+ }
+ }
+ val resizeTrigger =
+ when (source) {
+ Source.HEADER_BUTTON_TO_MAXIMIZE -> ResizeTrigger.MAXIMIZE_BUTTON
+ Source.HEADER_BUTTON_TO_RESTORE -> ResizeTrigger.MAXIMIZE_BUTTON
+ Source.KEYBOARD_SHORTCUT -> ResizeTrigger.UNKNOWN_RESIZE_TRIGGER
+ Source.HEADER_DRAG_TO_TOP -> ResizeTrigger.DRAG_TO_TOP_RESIZE_TRIGGER
+ Source.MAXIMIZE_MENU_TO_MAXIMIZE -> ResizeTrigger.MAXIMIZE_MENU
+ Source.MAXIMIZE_MENU_TO_RESTORE -> ResizeTrigger.MAXIMIZE_MENU
+ Source.DOUBLE_TAP_TO_MAXIMIZE -> ResizeTrigger.DOUBLE_TAP_APP_HEADER
+ Source.DOUBLE_TAP_TO_RESTORE -> ResizeTrigger.DOUBLE_TAP_APP_HEADER
+ }
+ val cujTracing: Int? =
+ when (source) {
+ Source.HEADER_BUTTON_TO_MAXIMIZE -> Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW
+ Source.HEADER_BUTTON_TO_RESTORE -> Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW
+ Source.KEYBOARD_SHORTCUT -> null
+ Source.HEADER_DRAG_TO_TOP -> null
+ Source.MAXIMIZE_MENU_TO_MAXIMIZE -> null
+ Source.MAXIMIZE_MENU_TO_RESTORE -> null
+ Source.DOUBLE_TAP_TO_MAXIMIZE -> null
+ Source.DOUBLE_TAP_TO_RESTORE -> null
+ }
+
+ /** The direction to which the task is being resized. */
+ enum class Direction {
+ MAXIMIZE,
+ RESTORE,
+ }
+
+ /** The user interaction source. */
+ enum class Source {
+ HEADER_BUTTON_TO_MAXIMIZE,
+ HEADER_BUTTON_TO_RESTORE,
+ KEYBOARD_SHORTCUT,
+ HEADER_DRAG_TO_TOP,
+ MAXIMIZE_MENU_TO_MAXIMIZE,
+ MAXIMIZE_MENU_TO_RESTORE,
+ DOUBLE_TAP_TO_MAXIMIZE,
+ DOUBLE_TAP_TO_RESTORE,
+ }
+
+ /**
+ * Temporary sources for interactions that should be broken into more specific sources, for
+ * example, the header button click should use [Source.HEADER_BUTTON_TO_MAXIMIZE] and
+ * [Source.HEADER_BUTTON_TO_RESTORE].
+ *
+ * TODO: b/341320112 - break these out into different [Source]s.
+ */
+ enum class AmbiguousSource {
+ HEADER_BUTTON,
+ MAXIMIZE_MENU,
+ DOUBLE_TAP,
+ }
+}
+
+/** Returns the non-ambiguous [Source] based on the maximized state of the task. */
+fun AmbiguousSource.toSource(isMaximized: Boolean): Source {
+ return when (this) {
+ AmbiguousSource.HEADER_BUTTON ->
+ if (isMaximized) {
+ Source.HEADER_BUTTON_TO_RESTORE
+ } else {
+ Source.HEADER_BUTTON_TO_MAXIMIZE
+ }
+ AmbiguousSource.MAXIMIZE_MENU ->
+ if (isMaximized) {
+ Source.MAXIMIZE_MENU_TO_RESTORE
+ } else {
+ Source.MAXIMIZE_MENU_TO_MAXIMIZE
+ }
+ AmbiguousSource.DOUBLE_TAP ->
+ if (isMaximized) {
+ Source.DOUBLE_TAP_TO_RESTORE
+ } else {
+ Source.DOUBLE_TAP_TO_MAXIMIZE
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
index 826de08..a428ce1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandler.kt
@@ -29,7 +29,7 @@
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil.isClosingMode
import com.android.wm.shell.shared.TransitionUtil.isClosingType
@@ -46,7 +46,7 @@
private val animExecutor: ShellExecutor,
private val shellInit: ShellInit,
private val transitions: Transitions,
- private val desktopRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
) : TransitionHandler {
private val showingSystemModalsIds = mutableSetOf<Int>()
@@ -156,7 +156,7 @@
}
private fun isDesktopModeShowing(displayId: Int): Boolean =
- desktopRepository.getVisibleTaskCount(displayId) > 0
+ desktopUserRepositories.current.getVisibleTaskCount(displayId) > 0
override fun handleRequest(
transition: IBinder,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt
index bfe1b12..ac0a627 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppToWebEducationController.kt
@@ -116,7 +116,12 @@
}
private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
- if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppToWebEducation()) block()
+ if (
+ canEnterDesktopMode(context) &&
+ Flags.enableDesktopWindowingAppToWebEducationIntegration()
+ ) {
+ block()
+ }
}
private fun showEducation(captionState: CaptionState, colorScheme: EducationColorScheme) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt
index 7554cbb..4298bd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/minimize/DesktopWindowLimitRemoteHandler.kt
@@ -43,7 +43,7 @@
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
remoteTransition: RemoteTransition,
private val taskIdToMinimize: Int,
- ) : TransitionHandler {
+) : TransitionHandler {
private val oneShotRemoteHandler = OneShotRemoteHandler(mainExecutor, remoteTransition)
private var transition: IBinder? = null
@@ -56,7 +56,7 @@
override fun handleRequest(
transition: IBinder,
- request: TransitionRequestInfo
+ request: TransitionRequestInfo,
): WindowContainerTransaction? {
this.transition = transition
return oneShotRemoteHandler.handleRequest(transition, request)
@@ -67,7 +67,7 @@
info: TransitionInfo,
startTransaction: SurfaceControl.Transaction,
finishTransaction: SurfaceControl.Transaction,
- finishCallback: Transitions.TransitionFinishCallback
+ finishCallback: Transitions.TransitionFinishCallback,
): Boolean {
if (transition != this.transition) return false
val minimizeChange = findMinimizeChange(info, taskIdToMinimize) ?: return false
@@ -76,7 +76,12 @@
// have access to RootTaskDisplayAreaOrganizer.
applyMinimizeChangeReparenting(info, minimizeChange, startTransaction)
return oneShotRemoteHandler.startAnimation(
- transition, info, startTransaction, finishTransaction, finishCallback)
+ transition,
+ info,
+ startTransaction,
+ finishTransaction,
+ finishCallback,
+ )
}
private fun applyMinimizeChangeReparenting(
@@ -87,14 +92,15 @@
val taskInfo = minimizeChange.taskInfo ?: return
if (taskInfo.isFreeform && TransitionUtil.isOpeningMode(info.type)) {
rootTaskDisplayAreaOrganizer.reparentToDisplayArea(
- taskInfo.displayId, minimizeChange.leash, startTransaction)
+ taskInfo.displayId,
+ minimizeChange.leash,
+ startTransaction,
+ )
}
}
- private fun findMinimizeChange(
- info: TransitionInfo,
- taskIdToMinimize: Int,
- ): Change? =
+ private fun findMinimizeChange(info: TransitionInfo, taskIdToMinimize: Int): Change? =
info.changes.find { change ->
- change.taskInfo?.taskId == taskIdToMinimize && change.mode == TRANSIT_TO_BACK }
+ change.taskInfo?.taskId == taskIdToMinimize && change.mode == TRANSIT_TO_BACK
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index 9e646f4..a6998e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -40,9 +40,7 @@
*
* The main constructor is public only for testing purposes.
*/
-class DesktopPersistentRepository(
- private val dataStore: DataStore<DesktopPersistentRepositories>,
-) {
+class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersistentRepositories>) {
constructor(
context: Context,
@ShellBackgroundThread bgCoroutineScope: CoroutineScope,
@@ -51,7 +49,7 @@
serializer = DesktopPersistentRepositoriesSerializer,
produceFile = { context.dataStoreFile(DESKTOP_REPOSITORIES_DATASTORE_FILE) },
scope = bgCoroutineScope,
- ),
+ )
)
/** Provides `dataStore.data` flow and handles exceptions thrown during collection */
@@ -63,7 +61,8 @@
TAG,
"Error in reading desktop mode related data from datastore, data is " +
"stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
- exception)
+ exception,
+ )
} else {
throw exception
}
@@ -73,13 +72,17 @@
* Reads and returns the [DesktopRepositoryState] proto object from the DataStore for a user. If
* the DataStore is empty or there's an error reading, it returns the default value of Proto.
*/
- private suspend fun getDesktopRepositoryState(
- userId: Int = DEFAULT_USER_ID
- ): DesktopRepositoryState? =
+ suspend fun getDesktopRepositoryState(userId: Int): DesktopRepositoryState? =
try {
- dataStoreFlow
- .first()
- .desktopRepoByUserMap[userId]
+ dataStoreFlow.first().desktopRepoByUserMap[userId]
+ } catch (e: Exception) {
+ Log.e(TAG, "Unable to read from datastore", e)
+ null
+ }
+
+ suspend fun getUserDesktopRepositoryMap(): Map<Int, DesktopRepositoryState>? =
+ try {
+ dataStoreFlow.first().desktopRepoByUserMap
} catch (e: Exception) {
Log.e(TAG, "Unable to read from datastore", e)
null
@@ -89,10 +92,7 @@
* Reads the [Desktop] of a desktop filtering by the [userId] and [desktopId]. Executes the
* [callback] using the [mainCoroutineScope].
*/
- suspend fun readDesktop(
- userId: Int = DEFAULT_USER_ID,
- desktopId: Int = DEFAULT_DESKTOP_ID,
- ): Desktop? =
+ suspend fun readDesktop(userId: Int, desktopId: Int = DEFAULT_DESKTOP_ID): Desktop? =
try {
val repository = getDesktopRepositoryState(userId)
repository?.getDesktopOrThrow(desktopId)
@@ -103,7 +103,7 @@
/** Adds or updates a desktop stored in the datastore */
suspend fun addOrUpdateDesktop(
- userId: Int = DEFAULT_USER_ID,
+ userId: Int,
desktopId: Int = 0,
visibleTasks: ArraySet<Int> = ArraySet(),
minimizedTasks: ArraySet<Int> = ArraySet(),
@@ -111,36 +111,33 @@
) {
// TODO: b/367609270 - Improve the API to support multi-user
try {
- dataStore.updateData { desktopPersistentRepositories: DesktopPersistentRepositories ->
+ dataStore.updateData { persistentRepositories: DesktopPersistentRepositories ->
val currentRepository =
- desktopPersistentRepositories.getDesktopRepoByUserOrDefault(
- userId, DesktopRepositoryState.getDefaultInstance())
+ persistentRepositories.getDesktopRepoByUserOrDefault(
+ userId,
+ DesktopRepositoryState.getDefaultInstance(),
+ )
val desktop =
getDesktop(currentRepository, desktopId)
.toBuilder()
- .updateTaskStates(
- visibleTasks,
- minimizedTasks,
- freeformTasksInZOrder,
- )
+ .updateTaskStates(visibleTasks, minimizedTasks, freeformTasksInZOrder)
.updateZOrder(freeformTasksInZOrder)
- desktopPersistentRepositories
+ persistentRepositories
.toBuilder()
.putDesktopRepoByUser(
userId,
- currentRepository
- .toBuilder()
- .putDesktop(desktopId, desktop.build())
- .build())
+ currentRepository.toBuilder().putDesktop(desktopId, desktop.build()).build(),
+ )
.build()
}
- } catch (exception: IOException) {
+ } catch (exception: Exception) {
Log.e(
TAG,
"Error in updating desktop mode related data, data is " +
"stored in a file named $DESKTOP_REPOSITORIES_DATASTORE_FILE",
- exception)
+ exception,
+ )
}
}
@@ -148,13 +145,13 @@
// If there are no desktops set up, create one on the default display
currentRepository.getDesktopOrDefault(
desktopId,
- Desktop.newBuilder().setDesktopId(desktopId).setDisplayId(DEFAULT_DISPLAY).build())
+ Desktop.newBuilder().setDesktopId(desktopId).setDisplayId(DEFAULT_DISPLAY).build(),
+ )
companion object {
private const val TAG = "DesktopPersistenceRepo"
private const val DESKTOP_REPOSITORIES_DATASTORE_FILE = "desktop_persistent_repositories.pb"
- private const val DEFAULT_USER_ID = 1000
private const val DEFAULT_DESKTOP_ID = 0
object DesktopPersistentRepositoriesSerializer : Serializer<DesktopPersistentRepositories> {
@@ -185,19 +182,22 @@
// visible, they will be marked as not visible afterwards. This ensures that they are
// still persisted as visible.
// TODO - b/350476823: Remove this logic once repository holds expanded tasks
- if (freeformTasksInZOrder.size > visibleTasks.size + minimizedTasks.size &&
- visibleTasks.isEmpty()
+ if (
+ freeformTasksInZOrder.size > visibleTasks.size + minimizedTasks.size &&
+ visibleTasks.isEmpty()
) {
visibleTasks.addAll(freeformTasksInZOrder.filterNot { it in minimizedTasks })
}
putAllTasksByTaskId(
visibleTasks.associateWith {
createDesktopTask(it, state = DesktopTaskState.VISIBLE)
- })
+ }
+ )
putAllTasksByTaskId(
minimizedTasks.associateWith {
createDesktopTask(it, state = DesktopTaskState.MINIMIZED)
- })
+ }
+ )
return this
}
@@ -211,7 +211,7 @@
private fun createDesktopTask(
taskId: Int,
- state: DesktopTaskState = DesktopTaskState.VISIBLE
+ state: DesktopTaskState = DesktopTaskState.VISIBLE,
): DesktopTask =
DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
index 771c3d1..a26ebbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
@@ -16,9 +16,9 @@
package com.android.wm.shell.desktopmode.persistence
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
-/** Interface for initializing the [DesktopRepository]. */
+/** Interface for initializing the [DesktopUserRepositories]. */
fun interface DesktopRepositoryInitializer {
- fun initialize(repository: DesktopRepository)
+ fun initialize(userRepositories: DesktopUserRepositories)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index d815656..58a49a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.window.DesktopModeFlags
import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import kotlinx.coroutines.CoroutineScope
@@ -35,32 +36,50 @@
private val persistentRepository: DesktopPersistentRepository,
@ShellMainThread private val mainCoroutineScope: CoroutineScope,
) : DesktopRepositoryInitializer {
- override fun initialize(repository: DesktopRepository) {
+ override fun initialize(userRepositories: DesktopUserRepositories) {
if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) return
// TODO: b/365962554 - Handle the case that user moves to desktop before it's initialized
mainCoroutineScope.launch {
- val desktop = persistentRepository.readDesktop() ?: return@launch
-
- val maxTasks =
- DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
- ?: desktop.zOrderedTasksCount
-
- var visibleTasksCount = 0
- desktop.zOrderedTasksList
- // Reverse it so we initialize the repo from bottom to top.
- .reversed()
- .mapNotNull { taskId -> desktop.tasksByTaskIdMap[taskId] }
- .forEach { task ->
- if (task.desktopTaskState == DesktopTaskState.VISIBLE
- && visibleTasksCount < maxTasks
- ) {
- visibleTasksCount++
- repository.addTask(desktop.displayId, task.taskId, isVisible = false)
- } else {
- repository.addTask(desktop.displayId, task.taskId, isVisible = false)
- repository.minimizeTask(desktop.displayId, task.taskId)
- }
+ val desktopUserPersistentRepositoryMap =
+ persistentRepository.getUserDesktopRepositoryMap() ?: return@launch
+ for (userId in desktopUserPersistentRepositoryMap.keys) {
+ val repository = userRepositories.getProfile(userId)
+ val desktopRepositoryState =
+ persistentRepository.getDesktopRepositoryState(userId) ?: continue
+ val desktopByDesktopIdMap = desktopRepositoryState.desktopMap
+ for (desktopId in desktopByDesktopIdMap.keys) {
+ val persistentDesktop =
+ persistentRepository.readDesktop(userId, desktopId) ?: continue
+ val maxTasks =
+ DesktopModeStatus.getMaxTaskLimit(context).takeIf { it > 0 }
+ ?: persistentDesktop.zOrderedTasksCount
+ var visibleTasksCount = 0
+ persistentDesktop.zOrderedTasksList
+ // Reverse it so we initialize the repo from bottom to top.
+ .reversed()
+ .mapNotNull { taskId -> persistentDesktop.tasksByTaskIdMap[taskId] }
+ .forEach { task ->
+ if (
+ task.desktopTaskState == DesktopTaskState.VISIBLE &&
+ visibleTasksCount < maxTasks
+ ) {
+ visibleTasksCount++
+ repository.addTask(
+ persistentDesktop.displayId,
+ task.taskId,
+ isVisible = false,
+ )
+ } else {
+ repository.addTask(
+ persistentDesktop.displayId,
+ task.taskId,
+ isVisible = false,
+ )
+ repository.minimizeTask(persistentDesktop.displayId, task.taskId)
+ }
+ }
}
+ }
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index cd20d97..4b59efb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
@@ -49,7 +50,7 @@
private final Context mContext;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<DesktopRepository> mDesktopRepository;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
private final Optional<DesktopTasksController> mDesktopTasksController;
private final WindowDecorViewModel mWindowDecorationViewModel;
private final LaunchAdjacentController mLaunchAdjacentController;
@@ -61,7 +62,7 @@
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
LaunchAdjacentController launchAdjacentController,
WindowDecorViewModel windowDecorationViewModel,
@@ -69,7 +70,7 @@
mContext = context;
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
- mDesktopRepository = desktopRepository;
+ mDesktopUserRepositories = desktopUserRepositories;
mDesktopTasksController = desktopTasksController;
mLaunchAdjacentController = launchAdjacentController;
mTaskChangeListener = taskChangeListener;
@@ -99,8 +100,9 @@
if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
DesktopModeStatus.canEnterDesktopMode(mContext)) {
- mDesktopRepository.ifPresent(repository -> {
- repository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
+ mDesktopUserRepositories.ifPresent(userRepositories -> {
+ DesktopRepository currentRepo = userRepositories.getProfile(taskInfo.userId);
+ currentRepo.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
});
}
updateLaunchAdjacentController();
@@ -113,21 +115,20 @@
mTasks.remove(taskInfo.taskId);
if (!DesktopModeFlags.ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS.isTrue() &&
- DesktopModeStatus.canEnterDesktopMode(mContext)) {
- mDesktopRepository.ifPresent(repository -> {
- // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
- if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
- || repository.isClosingTask(taskInfo.taskId)) {
- // A task that's vanishing should be removed:
- // - If it's closed by the X button which means it's marked as a closing task.
- repository.removeClosingTask(taskInfo.taskId);
- repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
- } else {
- repository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */
- false);
- repository.minimizeTask(taskInfo.displayId, taskInfo.taskId);
- }
- });
+ DesktopModeStatus.canEnterDesktopMode(mContext)
+ && mDesktopUserRepositories.isPresent()) {
+ DesktopRepository repository =
+ mDesktopUserRepositories.get().getProfile(taskInfo.userId);
+ // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
+ if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
+ || !repository.isMinimizedTask(taskInfo.taskId)) {
+ // A task that's vanishing should be removed:
+ // - If it's not yet minimized. It can be minimized when a back navigation is
+ // triggered on a task and the task is closing. It will be marked as minimized in
+ // [DesktopTasksTransitionObserver] before it gets here.
+ repository.removeClosingTask(taskInfo.taskId);
+ repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
+ }
}
mWindowDecorationViewModel.onTaskVanished(taskInfo);
updateLaunchAdjacentController();
@@ -148,11 +149,11 @@
// does not propagate all task info changes.
mTaskChangeListener.ifPresent(listener ->
listener.onNonTransitionTaskChanging(taskInfo));
- } else {
- mDesktopRepository.ifPresent(repository -> {
- repository.updateTask(taskInfo.displayId, taskInfo.taskId,
- taskInfo.isVisible);
- });
+ } else if (mDesktopUserRepositories.isPresent()) {
+ DesktopRepository currentRepo =
+ mDesktopUserRepositories.get().getProfile(taskInfo.userId);
+ currentRepo.updateTask(taskInfo.displayId, taskInfo.taskId,
+ taskInfo.isVisible);
}
}
updateLaunchAdjacentController();
@@ -176,10 +177,11 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
"Freeform Task Focus Changed: #%d focused=%b",
taskInfo.taskId, taskInfo.isFocused);
- if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused) {
- mDesktopRepository.ifPresent(repository -> {
- repository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
- });
+ if (DesktopModeStatus.canEnterDesktopMode(mContext) && taskInfo.isFocused
+ && mDesktopUserRepositories.isPresent()) {
+ DesktopRepository repository =
+ mDesktopUserRepositories.get().getProfile(taskInfo.userId);
+ repository.addTask(taskInfo.displayId, taskInfo.taskId, taskInfo.isVisible);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 4c316de..f8e6285 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -42,7 +42,6 @@
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
-import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IRemoteTransition;
@@ -444,10 +443,8 @@
@Override
public void startKeyguardTransition(boolean keyguardShowing, boolean aodShowing) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
- for (Display display : mDisplayController.getDisplays()) {
- wct.addKeyguardState(new KeyguardState.Builder(display.getDisplayId())
- .setKeyguardShowing(keyguardShowing).setAodShowing(aodShowing).build());
- }
+ wct.addKeyguardState(new KeyguardState.Builder().setKeyguardShowing(keyguardShowing)
+ .setAodShowing(aodShowing).build());
mMainExecutor.execute(() -> {
mTransitions.startTransition(keyguardShowing ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK,
wct, KeyguardTransitionHandler.this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index fb4afe4..af18768 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -94,6 +94,7 @@
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.pip.phone.PipMotionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.Interpolators;
@@ -152,7 +153,7 @@
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
@Nullable private final PipPerfHintController mPipPerfHintController;
- private final Optional<DesktopRepository> mDesktopRepositoryOptional;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final DisplayController mDisplayController;
protected final ShellTaskOrganizer mTaskOrganizer;
@@ -398,7 +399,7 @@
@NonNull PipParamsChangedForwarder pipParamsChangedForwarder,
Optional<SplitScreenController> splitScreenOptional,
Optional<PipPerfHintController> pipPerfHintControllerOptional,
- Optional<DesktopRepository> desktopRepositoryOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
@NonNull DisplayController displayController,
@NonNull PipUiEventLogger pipUiEventLogger,
@@ -426,7 +427,7 @@
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
mSplitScreenOptional = splitScreenOptional;
mPipPerfHintController = pipPerfHintControllerOptional.orElse(null);
- mDesktopRepositoryOptional = desktopRepositoryOptional;
+ mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mDisplayController = displayController;
mTaskOrganizer = shellTaskOrganizer;
@@ -764,7 +765,7 @@
// previous freeform bounds that is saved in DesktopRepository.
// 2) If PiP was entered through other means (e.g. user swipe up), exit to initial
// freeform bounds. Note that this case has a flicker at the moment (b/379984108).
- Rect freeformBounds = mDesktopRepositoryOptional.get().removeBoundsBeforeMinimize(
+ Rect freeformBounds = getCurrentRepo().removeBoundsBeforeMinimize(
mTaskInfo.taskId);
return freeformBounds != null
? freeformBounds
@@ -779,11 +780,17 @@
/** Returns whether PiP is exiting while we're in desktop mode. */
// TODO(b/377581840): Update this check to include non-minimized cases, e.g. split to PiP etc.
private boolean isPipExitingToDesktopMode() {
- return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent()
- && (mDesktopRepositoryOptional.get().getVisibleTaskCount(mTaskInfo.displayId) > 0
+ DesktopRepository currentRepo = getCurrentRepo();
+ return Flags.enableDesktopWindowingPip() && currentRepo != null
+ && (currentRepo.getVisibleTaskCount(mTaskInfo.displayId) > 0
|| isDisplayInFreeform());
}
+ private DesktopRepository getCurrentRepo() {
+ return mDesktopUserRepositoriesOptional.map(DesktopUserRepositories::getCurrent).orElse(
+ null);
+ }
+
private void exitLaunchIntoPipTask(WindowContainerTransaction wct) {
wct.startTask(mTaskInfo.launchIntoPipHostTaskId, null /* ActivityOptions */);
mTaskOrganizer.applyTransaction(wct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 7145e06..4461a5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -39,7 +39,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -58,7 +58,7 @@
private final PipBoundsState mPipBoundsState;
private final ShellExecutor mMainExecutor;
private final PipTransitionState mPipTransitionState;
- private final Optional<DesktopRepository> mDesktopRepositoryOptional;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private PipTransitionController mPipTransitionController;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -72,13 +72,13 @@
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
PipTransitionState pipTransitionState,
- Optional<DesktopRepository> desktopRepositoryOptional,
+ Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mPipBoundsState = pipBoundsState;
mMainExecutor = mainExecutor;
mPipTransitionState = pipTransitionState;
- mDesktopRepositoryOptional = desktopRepositoryOptional;
+ mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mSurfaceControlTransactionFactory =
@@ -268,8 +268,8 @@
/** Returns whether PiP is exiting while we're in desktop mode. */
private boolean isPipExitingToDesktopMode() {
- return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent()
- && (mDesktopRepositoryOptional.get().getVisibleTaskCount(
+ return Flags.enableDesktopWindowingPip() && mDesktopUserRepositoriesOptional.isPresent()
+ && (mDesktopUserRepositoriesOptional.get().getCurrent().getVisibleTaskCount(
Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId) > 0
|| isDisplayInFreeform());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 8f02c1b..b171db2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -627,6 +627,12 @@
finishTransition();
});
cacheAndStartTransitionAnimator(animator);
+
+ // Save the PiP bounds in case, we re-enter the PiP with the same component.
+ float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+ mPipBoundsState.getBounds());
+ mPipBoundsState.saveReentryState(snapFraction);
+
return true;
}
@@ -912,11 +918,6 @@
"Unexpected bundle for " + mPipTransitionState);
break;
case PipTransitionState.EXITED_PIP:
- // Save the PiP bounds in case, we re-enter the PiP with the same component.
- float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
- mPipBoundsState.getBounds());
- mPipBoundsState.saveReentryState(snapFraction);
-
mPipTransitionState.setPinnedTaskLeash(null);
mPipTransitionState.setPipTaskInfo(null);
break;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 363c95f..441f967 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -63,6 +63,7 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.annotations.ExternalThread;
@@ -72,6 +73,7 @@
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.UserChangeListener;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -89,13 +91,14 @@
*/
public class RecentTasksController implements TaskStackListenerCallback,
RemoteCallable<RecentTasksController>, DesktopRepository.ActiveTasksListener,
- TaskStackTransitionObserver.TaskStackTransitionObserverListener {
+ TaskStackTransitionObserver.TaskStackTransitionObserverListener, UserChangeListener {
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
- private final Optional<DesktopRepository> mDesktopRepository;
+ private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
+
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasksImpl mImpl = new RecentTasksImpl();
@@ -108,6 +111,8 @@
// Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
private final SparseIntArray mSplitTasks = new SparseIntArray();
+
+ private int mUserId;
/**
* Maps taskId to {@link SplitBounds} for both taskIDs.
* Meaning there will be two taskId integers mapping to the same object.
@@ -133,7 +138,7 @@
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
@ShellMainThread ShellExecutor mainExecutor
) {
@@ -141,7 +146,7 @@
return null;
}
return new RecentTasksController(context, shellInit, shellController, shellCommandHandler,
- taskStackListener, activityTaskManager, desktopRepository,
+ taskStackListener, activityTaskManager, desktopUserRepositories,
taskStackTransitionObserver, mainExecutor);
}
@@ -151,7 +156,7 @@
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
ActivityTaskManager activityTaskManager,
- Optional<DesktopRepository> desktopRepository,
+ Optional<DesktopUserRepositories> desktopUserRepositories,
TaskStackTransitionObserver taskStackTransitionObserver,
ShellExecutor mainExecutor) {
mContext = context;
@@ -160,7 +165,7 @@
mActivityTaskManager = activityTaskManager;
mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
- mDesktopRepository = desktopRepository;
+ mDesktopUserRepositories = desktopUserRepositories;
mTaskStackTransitionObserver = taskStackTransitionObserver;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
@@ -175,12 +180,15 @@
}
@RequiresPermission(Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)
- private void onInit() {
+ void onInit() {
mShellController.addExternalInterface(KEY_EXTRA_SHELL_RECENT_TASKS,
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
+ mUserId = ActivityManager.getCurrentUser();
+ mDesktopUserRepositories.ifPresent(
+ desktopUserRepositories ->
+ desktopUserRepositories.getCurrent().addActiveTaskListener(this));
mTaskStackListener.addListener(this);
- mDesktopRepository.ifPresent(it -> it.addActiveTaskListener(this));
mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this,
mMainExecutor);
mContext.getSystemService(KeyguardManager.class).addKeyguardLockedStateListener(
@@ -291,9 +299,9 @@
*/
@Override
public void onRecentTaskRemovedForAddTask(int taskId) {
- mDesktopRepository.ifPresent(
- repo -> repo.removeFreeformTask(INVALID_DISPLAY, taskId)
- );
+ mDesktopUserRepositories.ifPresent(
+ desktopUserRepositories -> desktopUserRepositories.getCurrent().removeFreeformTask(
+ INVALID_DISPLAY, taskId));
}
public void onTaskAdded(RunningTaskInfo taskInfo) {
@@ -512,10 +520,9 @@
// If it's not in the mapping, then it was already paired with another task
continue;
}
-
- if (DesktopModeStatus.canEnterDesktopMode(mContext)
- && mDesktopRepository.isPresent()
- && mDesktopRepository.get().isActiveTask(taskInfo.taskId)) {
+ if (DesktopModeStatus.canEnterDesktopMode(mContext) &&
+ mDesktopUserRepositories.isPresent()
+ && mDesktopUserRepositories.get().getCurrent().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
if (mostRecentFreeformTaskIndex == Integer.MAX_VALUE) {
mostRecentFreeformTaskIndex = groupedTasks.size();
@@ -531,7 +538,7 @@
taskInfo.lastNonFullscreenBounds.top);
}
freeformTasks.add(taskInfo);
- if (mDesktopRepository.get().isMinimizedTask(taskInfo.taskId)) {
+ if (mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskInfo.taskId)) {
minimizedFreeformTasks.add(taskInfo.taskId);
}
continue;
@@ -703,6 +710,21 @@
}
}
+ @Override
+ public void onUserChanged(int newUserId, @NonNull Context userContext) {
+ if (mDesktopUserRepositories.isEmpty()) return;
+
+ DesktopRepository previousUserRepository =
+ mDesktopUserRepositories.get().getProfile(mUserId);
+ mUserId = newUserId;
+ DesktopRepository currentUserRepository =
+ mDesktopUserRepositories.get().getProfile(newUserId);
+
+ // No-op if both profile ids map to the same user.
+ if (previousUserRepository.getUserId() == currentUserRepository.getUserId()) return;
+ previousUserRepository.removeActiveTasksListener(this);
+ currentUserRepository.addActiveTaskListener(this);
+ }
/**
* The interface for calls from outside the host process.
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 6e0e696..fc757ef 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
@@ -94,6 +94,7 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.SplitDragPolicy;
@@ -199,6 +200,7 @@
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final Optional<DesktopTasksController> mDesktopTasksController;
private final MultiInstanceHelper mMultiInstanceHelpher;
+ private final SplitState mSplitState;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
@VisibleForTesting
@@ -228,6 +230,7 @@
Optional<DesktopTasksController> desktopTasksController,
@Nullable StageCoordinator stageCoordinator,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
ShellExecutor mainExecutor,
Handler mainHandler) {
mShellCommandHandler = shellCommandHandler;
@@ -252,6 +255,7 @@
mDesktopTasksController = desktopTasksController;
mStageCoordinator = stageCoordinator;
mMultiInstanceHelpher = multiInstanceHelper;
+ mSplitState = splitState;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
@@ -296,7 +300,7 @@
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
- mWindowDecorViewModel);
+ mWindowDecorViewModel, mSplitState);
}
@Override
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 07c157b..b40996f 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
@@ -139,6 +139,7 @@
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.common.split.SplitWindowManager;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
@@ -218,6 +219,8 @@
private final Optional<RecentTasksController> mRecentTasks;
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
+ /** Singleton source of truth for the current state of split screen on this device. */
+ private final SplitState mSplitState;
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
@@ -344,7 +347,7 @@
TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -355,6 +358,7 @@
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
+ mSplitState = splitState;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
@@ -412,7 +416,7 @@
Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
mContext = context;
mDisplayId = displayId;
mSyncQueue = syncQueue;
@@ -432,6 +436,8 @@
mRecentTasks = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
+ mSplitState = splitState;
+
mDisplayController.addDisplayWindowListener(this);
transitions.addHandler(this);
mSplitUnsupportedToast = Toast.makeText(mContext,
@@ -1282,6 +1288,7 @@
setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(st -> {
+ mSplitLayout.updateStateWithCurrentPosition();
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
// updateSurfaceBounds(), above, officially puts the two apps in their new
@@ -1437,6 +1444,7 @@
if (!isSplitActive() || mIsExiting) return;
onSplitScreenExit();
+ mSplitState.exit();
clearSplitPairedInRecents(exitReason);
mShouldUpdateRecents = false;
@@ -1632,6 +1640,7 @@
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
}
deactivateSplit(wct, stageToTop);
+ mSplitState.exit();
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
@@ -1730,6 +1739,7 @@
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
+ mSplitLayout.updateStateWithCurrentPosition();
mSplitLayout.update(null, true /* resetImePosition */);
if (enableFlexibleSplit()) {
runForActiveStages((stage) ->
@@ -1954,6 +1964,8 @@
}
}
+ int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
+
if (Flags.enableFlexibleTwoAppSplit()) {
// Split screen can be laid out in such a way that some of the apps are offscreen.
// For the purposes of passing SplitBounds up to launcher (for use in thumbnails
@@ -1966,10 +1978,17 @@
Math.min(bottomRightBounds.right, mSplitLayout.getDisplayWidth());
bottomRightBounds.top =
Math.min(bottomRightBounds.top, mSplitLayout.getDisplayHeight());
+
+ // TODO (b/349828130): Can change to getState() fully after brief soak time.
+ if (mSplitState.get() != currentSnapPosition) {
+ Log.wtf(TAG, "SplitState is " + mSplitState.get()
+ + ", expected " + currentSnapPosition);
+ currentSnapPosition = mSplitState.get();
+ }
}
SplitBounds splitBounds = new SplitBounds(topLeftBounds, bottomRightBounds,
- leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
+ leftTopTaskId, rightBottomTaskId, currentSnapPosition);
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
@@ -2008,7 +2027,7 @@
mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
mRootTaskInfo.configuration, this, mParentContainerCallbacks,
mDisplayController, mDisplayImeController, mTaskOrganizer,
- PARALLAX_ALIGN_CENTER /* parallaxType */, mMainHandler);
+ PARALLAX_ALIGN_CENTER /* parallaxType */, mSplitState, mMainHandler);
mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index 3468156..c5e158c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -32,6 +32,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -60,6 +61,7 @@
private final IconProvider mIconProvider;
private final Optional<RecentTasksController> mRecentTasksOptional;
private final LaunchAdjacentController mLaunchAdjacentController;
+ private final SplitState mSplitState;
private final Handler mMainHandler;
private final SystemWindows mSystemWindows;
@@ -80,6 +82,7 @@
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
MultiInstanceHelper multiInstanceHelper,
+ SplitState splitState,
ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -87,8 +90,8 @@
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
- Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor,
- mainHandler);
+ Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
+ mainExecutor, mainHandler);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
@@ -102,6 +105,7 @@
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
mLaunchAdjacentController = launchAdjacentController;
+ mSplitState = splitState;
mMainHandler = mainHandler;
mSystemWindows = systemWindows;
@@ -117,7 +121,7 @@
mTaskOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mTransitions, mTransactionPool,
mIconProvider, mMainExecutor, mMainHandler,
- mRecentTasksOptional, mLaunchAdjacentController, mSystemWindows);
+ mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
index 4451ee8..ef1f88e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java
@@ -28,6 +28,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.split.SplitScreenConstants;
@@ -53,10 +54,12 @@
Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ SplitState splitState,
SystemWindows systemWindows) {
super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
displayInsetsController, transitions, transactionPool, iconProvider,
- mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty());
+ mainExecutor, mainHandler, recentTasks, launchAdjacentController, Optional.empty(),
+ splitState);
mTvSplitMenuController = new TvSplitMenuController(context, this,
systemWindows, mainHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index 4ea4613..d8884f6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -41,9 +41,9 @@
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
- @Nullable Rect clipRect, boolean isActivity) {
+ @Nullable Rect clipRect) {
final DefaultAnimationAdapter adapter = new DefaultAnimationAdapter(anim, leash,
- position, clipRect, cornerRadius, isActivity);
+ position, clipRect, cornerRadius);
buildSurfaceAnimation(animations, anim, finishCallback, pool, mainExecutor, adapter);
}
@@ -138,11 +138,9 @@
@Nullable final Rect mClipRect;
@Nullable private final Rect mAnimClipRect;
final float mCornerRadius;
- final boolean mIsActivity;
DefaultAnimationAdapter(@NonNull Animation anim, @NonNull SurfaceControl leash,
- @Nullable Point position, @Nullable Rect clipRect, float cornerRadius,
- boolean isActivity) {
+ @Nullable Point position, @Nullable Rect clipRect, float cornerRadius) {
super(leash);
mAnim = anim;
mPosition = (position != null && (position.x != 0 || position.y != 0))
@@ -150,7 +148,6 @@
mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
mAnimClipRect = mClipRect != null ? new Rect() : null;
mCornerRadius = cornerRadius;
- mIsActivity = isActivity;
}
@Override
@@ -160,10 +157,6 @@
final SurfaceControl leash = mLeash;
transformation.clear();
mAnim.getTransformation(currentPlayTime, transformation);
- if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
- && mIsActivity && mAnim.getExtensionEdges() != 0) {
- t.setEdgeExtensionEffect(leash, mAnim.getExtensionEdges());
- }
if (mPosition != null) {
transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 9fcf98b..e80016d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -506,6 +506,8 @@
if (!isTask && a.getExtensionEdges() != 0x0) {
if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+ startTransaction.setEdgeExtensionEffect(
+ change.getLeash(), a.getExtensionEdges());
finishTransaction.setEdgeExtensionEffect(change.getLeash(), /* edge */ 0);
} else {
if (!TransitionUtil.isOpeningType(mode)) {
@@ -564,7 +566,7 @@
buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
- clipRect, change.getActivityComponent() != null);
+ clipRect);
final TransitionInfo.AnimationOptions options;
if (Flags.moveAnimationOptionsToChange()) {
@@ -876,8 +878,7 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
- change.getActivityComponent() != null);
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -901,8 +902,7 @@
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
- change.getActivityComponent() != null);
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
}
private static int getWallpaperTransitType(TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 6f3aa11..aa42b7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -347,21 +347,21 @@
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateEnterAnimation, getEnterSurface(), finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */, false /* isActivity */);
+ null /* clipRect */);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */, false /* isActivity */);
+ null /* clipRect */);
}
private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */, false /* isActivity */);
+ null /* clipRect */);
}
private void buildLumaAnimation(@NonNull ArrayList<Animator> animations,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index c9f2d2e..885f3db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -63,6 +63,7 @@
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.shared.FocusTransitionListener;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
@@ -119,8 +120,8 @@
public CaptionWindowDecorViewModel(
Context context,
Handler mainHandler,
+ @ShellMainThread ShellExecutor shellExecutor,
@ShellBackgroundThread ShellExecutor bgExecutor,
- ShellExecutor shellExecutor,
Choreographer mainChoreographer,
IWindowManager windowManager,
ShellInit shellInit,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
index 101467d..ff52a45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenu.kt
@@ -32,7 +32,7 @@
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.shared.multiinstance.ManageWindowsViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
@@ -51,7 +51,7 @@
private val displayController: DisplayController,
private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
context: Context,
- private val desktopRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val surfaceControlBuilderSupplier: Supplier<SurfaceControl.Builder>,
private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
snapshotList: List<Pair<Int, TaskSnapshot>>,
@@ -76,6 +76,7 @@
val flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH or
WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ val desktopRepository = desktopUserRepositories.getProfile(callerTaskInfo.userId)
menuViewContainer = if (Flags.enableFullyImmersiveInDesktop()
&& desktopRepository.isTaskInFullImmersiveState(callerTaskInfo.taskId)) {
// Use system view container so that forcibly shown system bars take effect in
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 04ef7c1..e8b02dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -111,13 +111,17 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum;
+import com.android.wm.shell.desktopmode.DesktopModeUtils;
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction;
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeUtilsKt;
import com.android.wm.shell.desktopmode.education.AppHandleEducationController;
import com.android.wm.shell.desktopmode.education.AppToWebEducationController;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
@@ -166,7 +170,7 @@
private final ActivityTaskManager mActivityTaskManager;
private final ShellCommandHandler mShellCommandHandler;
private final ShellTaskOrganizer mTaskOrganizer;
- private final DesktopRepository mDesktopRepository;
+ private final DesktopUserRepositories mDesktopUserRepositories;
private final ShellController mShellController;
private final Context mContext;
private final @ShellMainThread Handler mMainHandler;
@@ -244,7 +248,7 @@
ShellCommandHandler shellCommandHandler,
IWindowManager windowManager,
ShellTaskOrganizer taskOrganizer,
- DesktopRepository desktopRepository,
+ DesktopUserRepositories desktopUserRepositories,
DisplayController displayController,
ShellController shellController,
DisplayInsetsController displayInsetsController,
@@ -275,7 +279,7 @@
shellCommandHandler,
windowManager,
taskOrganizer,
- desktopRepository,
+ desktopUserRepositories,
displayController,
shellController,
displayInsetsController,
@@ -315,7 +319,7 @@
ShellCommandHandler shellCommandHandler,
IWindowManager windowManager,
ShellTaskOrganizer taskOrganizer,
- DesktopRepository desktopRepository,
+ DesktopUserRepositories desktopUserRepositories,
DisplayController displayController,
ShellController shellController,
DisplayInsetsController displayInsetsController,
@@ -349,7 +353,7 @@
mBgExecutor = bgExecutor;
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
- mDesktopRepository = desktopRepository;
+ mDesktopUserRepositories = desktopUserRepositories;
mShellController = shellController;
mDisplayController = displayController;
mDisplayInsetsController = displayInsetsController;
@@ -580,34 +584,59 @@
>= MANAGE_WINDOWS_MINIMUM_INSTANCES);
}
- private void onMaximizeOrRestore(int taskId, String source, ResizeTrigger resizeTrigger,
- MotionEvent motionEvent) {
+ private void onToggleSizeInteraction(
+ int taskId, @NonNull ToggleTaskSizeInteraction.AmbiguousSource source,
+ @Nullable MotionEvent motionEvent) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
if (decoration == null) {
return;
}
- mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, resizeTrigger,
- DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent), () -> {
- mInteractionJankMonitor.begin(
- decoration.mTaskSurface, mContext, mMainHandler,
- Cuj.CUJ_DESKTOP_MODE_MAXIMIZE_WINDOW, source);
- return null;
- }, () -> {
- mInteractionJankMonitor.begin(
- decoration.mTaskSurface, mContext, mMainHandler,
- Cuj.CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW, source);
- return null;
- });
+ final ToggleTaskSizeInteraction interaction =
+ createToggleSizeInteraction(decoration, source, motionEvent);
+ if (interaction == null) {
+ return;
+ }
+ if (interaction.getCujTracing() != null) {
+ mInteractionJankMonitor.begin(
+ decoration.mTaskSurface, mContext, mMainHandler,
+ interaction.getCujTracing(), interaction.getJankTag());
+ }
+ mDesktopTasksController.toggleDesktopTaskSize(decoration.mTaskInfo, interaction);
decoration.closeHandleMenu();
decoration.closeMaximizeMenu();
}
- private void onEnterOrExitImmersive(int taskId) {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+ private ToggleTaskSizeInteraction createToggleSizeInteraction(
+ @NonNull DesktopModeWindowDecoration decoration,
+ @NonNull ToggleTaskSizeInteraction.AmbiguousSource source,
+ @Nullable MotionEvent motionEvent) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+
+ final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(taskInfo.displayId);
+ if (displayLayout == null) {
+ return null;
+ }
+ final Rect stableBounds = new Rect();
+ displayLayout.getStableBounds(stableBounds);
+ boolean isMaximized = DesktopModeUtils.isTaskMaximized(taskInfo, stableBounds);
+
+ return new ToggleTaskSizeInteraction(
+ isMaximized
+ ? ToggleTaskSizeInteraction.Direction.RESTORE
+ : ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeUtilsKt.toSource(source, isMaximized),
+ DesktopModeEventLogger.getInputMethodFromMotionEvent(motionEvent)
+ );
+ }
+
+ private void onEnterOrExitImmersive(RunningTaskInfo taskInfo) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
if (decoration == null) {
return;
}
- if (mDesktopRepository.isTaskInFullImmersiveState(taskId)) {
+ final DesktopRepository desktopRepository = mDesktopUserRepositories.getProfile(
+ taskInfo.userId);
+ if (desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId)) {
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_MENU_TAP_TO_RESTORE);
mDesktopImmersiveController.moveTaskToNonImmersive(
@@ -802,7 +831,6 @@
private boolean mIsResizeGesture;
private boolean mIsDragging;
private boolean mTouchscreenInUse;
- private boolean mHasLongClicked;
private int mDragPointerId = -1;
private MotionEvent mMotionEvent;
@@ -868,12 +896,12 @@
&& TaskInfoKt.getRequestingImmersive(decoration.mTaskInfo)) {
// Task is requesting immersive, so it should either enter or exit immersive,
// depending on immersive state.
- onEnterOrExitImmersive(decoration.mTaskInfo.taskId);
+ onEnterOrExitImmersive(decoration.mTaskInfo);
} else {
// Full immersive is disabled or task doesn't request/support it, so just
// toggle between maximize/restore states.
- onMaximizeOrRestore(decoration.mTaskInfo.taskId, "caption_bar_button",
- ResizeTrigger.MAXIMIZE_BUTTON, mMotionEvent);
+ onToggleSizeInteraction(decoration.mTaskInfo.taskId,
+ ToggleTaskSizeInteraction.AmbiguousSource.HEADER_BUTTON, mMotionEvent);
}
} else if (id == R.id.minimize_window) {
mDesktopTasksController.minimizeTask(decoration.mTaskInfo);
@@ -961,7 +989,6 @@
if (decoration.isMaximizeMenuActive()) {
decoration.closeMaximizeMenu();
} else {
- mHasLongClicked = true;
mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
DesktopUiEventEnum.DESKTOP_WINDOW_MAXIMIZE_BUTTON_REVEAL_MENU);
decoration.createMaximizeMenu();
@@ -1000,6 +1027,8 @@
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!mFocusTransitionObserver.hasGlobalFocus(taskInfo)) {
+ mDesktopModeUiEventLogger.log(taskInfo,
+ DesktopUiEventEnum.DESKTOP_WINDOW_HEADER_TAP_TO_REFOCUS);
mDesktopTasksController.moveTaskToFront(taskInfo);
}
}
@@ -1056,8 +1085,10 @@
}
final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window
|| id == R.id.open_menu_button || id == R.id.minimize_window);
+ final DesktopRepository desktopRepository = mDesktopUserRepositories.getProfile(
+ taskInfo.userId);
final boolean dragAllowed =
- !mDesktopRepository.isTaskInFullImmersiveState(taskInfo.taskId);
+ !desktopRepository.isTaskInFullImmersiveState(taskInfo.taskId);
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
if (dragAllowed) {
@@ -1068,7 +1099,6 @@
updateDragStatus(e.getActionMasked());
mOnDragStartInitialBounds.set(initialBounds);
}
- mHasLongClicked = false;
// Do not consume input event if a button is touched, otherwise it would
// prevent the button's ripple effect from showing.
return !touchingButton;
@@ -1102,6 +1132,8 @@
if (!wasDragging) {
return false;
}
+ mDesktopModeUiEventLogger.log(taskInfo,
+ DesktopUiEventEnum.DESKTOP_WINDOW_MOVE_BY_HEADER_DRAG);
if (e.findPointerIndex(mDragPointerId) == -1) {
mDragPointerId = e.getPointerId(0);
}
@@ -1123,7 +1155,7 @@
newTaskBounds, decoration.calculateValidDragArea(),
new Rect(mOnDragStartInitialBounds), e,
mWindowDecorByTaskId.get(taskInfo.taskId));
- if (touchingButton && !mHasLongClicked) {
+ if (touchingButton) {
// We need the input event to not be consumed here to end the ripple
// effect on the touched button. We will reset drag state in the ensuing
// onClick call that results.
@@ -1165,11 +1197,13 @@
&& action != MotionEvent.ACTION_CANCEL)) {
return false;
}
- if (mDesktopRepository.isTaskInFullImmersiveState(mTaskId)) {
+ final DesktopRepository desktopRepository = mDesktopUserRepositories.getCurrent();
+ if (desktopRepository.isTaskInFullImmersiveState(mTaskId)) {
// Disallow double-tap to resize when in full immersive.
return false;
}
- onMaximizeOrRestore(mTaskId, "double_tap", ResizeTrigger.DOUBLE_TAP_APP_HEADER, e);
+ onToggleSizeInteraction(mTaskId,
+ ToggleTaskSizeInteraction.AmbiguousSource.DOUBLE_TAP, e);
return true;
}
}
@@ -1577,7 +1611,7 @@
mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
mDisplayController,
mSplitScreenController,
- mDesktopRepository,
+ mDesktopUserRepositories,
mTaskOrganizer,
taskInfo,
taskSurface,
@@ -1608,12 +1642,13 @@
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setOnMaximizeOrRestoreClickListener(() -> {
- onMaximizeOrRestore(taskInfo.taskId, "maximize_menu", ResizeTrigger.MAXIMIZE_MENU,
+ onToggleSizeInteraction(taskInfo.taskId,
+ ToggleTaskSizeInteraction.AmbiguousSource.MAXIMIZE_MENU,
touchEventListener.mMotionEvent);
return Unit.INSTANCE;
});
windowDecoration.setOnImmersiveOrRestoreClickListener(() -> {
- onEnterOrExitImmersive(taskInfo.taskId);
+ onEnterOrExitImmersive(taskInfo);
return Unit.INSTANCE;
});
windowDecoration.setOnLeftSnapClickListener(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5eb0312..bd84ccc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -99,7 +99,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -206,14 +206,14 @@
private final Runnable mCapturedLinkExpiredRunnable = this::onCapturedLinkExpired;
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
- private final DesktopRepository mDesktopRepository;
+ private final DesktopUserRepositories mDesktopUserRepositories;
public DesktopModeWindowDecoration(
Context context,
@NonNull Context userContext,
DisplayController displayController,
SplitScreenController splitScreenController,
- DesktopRepository desktopRepository,
+ DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -228,10 +228,10 @@
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger) {
- this (context, userContext, displayController, splitScreenController, desktopRepository,
- taskOrganizer, taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
- appHeaderViewHolderFactory, rootTaskDisplayAreaOrganizer, genericLinksParser,
- assistContentRequester,
+ this (context, userContext, displayController, splitScreenController,
+ desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
+ bgExecutor, choreographer, syncQueue, appHeaderViewHolderFactory,
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
@@ -246,7 +246,7 @@
@NonNull Context userContext,
DisplayController displayController,
SplitScreenController splitScreenController,
- DesktopRepository desktopRepository,
+ DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -287,7 +287,7 @@
mMultiInstanceHelper = multiInstanceHelper;
mWindowManagerWrapper = windowManagerWrapper;
mWindowDecorCaptionHandleRepository = windowDecorCaptionHandleRepository;
- mDesktopRepository = desktopRepository;
+ mDesktopUserRepositories = desktopUserRepositories;
}
/**
@@ -437,7 +437,7 @@
public void updateDisabledResizingEdge(
DragResizeWindowGeometry.DisabledEdge disabledResizingEdge, boolean shouldDelayUpdate) {
mDisabledResizingEdge = disabledResizingEdge;
- final boolean inFullImmersive = mDesktopRepository
+ final boolean inFullImmersive = mDesktopUserRepositories.getCurrent()
.isTaskInFullImmersiveState(mTaskInfo.taskId);
if (shouldDelayUpdate) {
return;
@@ -541,7 +541,7 @@
mOpenByDefaultDialog.relayout(taskInfo);
}
- final boolean inFullImmersive = mDesktopRepository
+ final boolean inFullImmersive = mDesktopUserRepositories.getProfile(taskInfo.userId)
.isTaskInFullImmersiveState(taskInfo.taskId);
updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw,
shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible,
@@ -1302,9 +1302,11 @@
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
calculateMaximizeMenuPosition(menuWidth), mSurfaceControlTransactionSupplier);
+
mMaximizeMenu.show(
/* isTaskInImmersiveMode= */ Flags.enableFullyImmersiveInDesktop()
- && mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId),
+ && mDesktopUserRepositories.getProfile(mTaskInfo.userId)
+ .isTaskInFullImmersiveState(mTaskInfo.taskId),
/* menuWidth= */ menuWidth,
/* showImmersiveOption= */ Flags.enableFullyImmersiveInDesktop()
&& TaskInfoKt.getRequestingImmersive(mTaskInfo),
@@ -1394,7 +1396,7 @@
&& mMinimumInstancesFound;
final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
.shouldShowChangeAspectRatioButton(mTaskInfo);
- final boolean inDesktopImmersive = mDesktopRepository
+ final boolean inDesktopImmersive = mDesktopUserRepositories.getProfile(mTaskInfo.userId)
.isTaskInFullImmersiveState(mTaskInfo.taskId);
final boolean isBrowserApp = isBrowserApp();
mHandleMenu = mHandleMenuFactory.create(
@@ -1433,7 +1435,7 @@
/* openInBrowserClickListener= */ (intent) -> {
mOpenInBrowserClickListener.accept(intent);
onCapturedLinkExpired();
- if (Flags.enableDesktopWindowingAppToWebEducation()) {
+ if (Flags.enableDesktopWindowingAppToWebEducationIntegration()) {
mWindowDecorCaptionHandleRepository.onAppToWebUsage();
}
return Unit.INSTANCE;
@@ -1474,7 +1476,7 @@
mDisplayController,
mRootTaskDisplayAreaOrganizer,
mContext,
- mDesktopRepository,
+ mDesktopUserRepositories,
mSurfaceControlBuilderSupplier,
mSurfaceControlTransactionSupplier,
snapshotList,
@@ -1686,7 +1688,7 @@
/** Returns true if at least one education flag is enabled. */
private boolean isEducationEnabled() {
return Flags.enableDesktopWindowingAppHandleEducation()
- || Flags.enableDesktopWindowingAppToWebEducation();
+ || Flags.enableDesktopWindowingAppToWebEducationIntegration();
}
@Override
@@ -1770,7 +1772,8 @@
void setAnimatingTaskResizeOrReposition(boolean animatingTaskResizeOrReposition) {
if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_handle) return;
final boolean inFullImmersive =
- mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId);
+ mDesktopUserRepositories.getProfile(mTaskInfo.userId)
+ .isTaskInFullImmersiveState(mTaskInfo.taskId);
asAppHeader(mWindowDecorViewHolder).bindData(new AppHeaderViewHolder.HeaderData(
mTaskInfo,
TaskInfoKt.getRequestingImmersive(mTaskInfo),
@@ -1798,8 +1801,9 @@
return !animatingTaskResizeOrReposition;
}
final boolean inImmersiveAndRequesting =
- mDesktopRepository.isTaskInFullImmersiveState(mTaskInfo.taskId)
- && TaskInfoKt.getRequestingImmersive(mTaskInfo);
+ mDesktopUserRepositories.getProfile(mTaskInfo.userId)
+ .isTaskInFullImmersiveState(mTaskInfo.taskId)
+ && TaskInfoKt.getRequestingImmersive(mTaskInfo);
return !animatingTaskResizeOrReposition && !inImmersiveAndRequesting;
}
@@ -1820,7 +1824,7 @@
@NonNull Context userContext,
DisplayController displayController,
SplitScreenController splitScreenController,
- DesktopRepository desktopRepository,
+ DesktopUserRepositories desktopUserRepositories,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -1840,7 +1844,7 @@
userContext,
displayController,
splitScreenController,
- desktopRepository,
+ desktopUserRepositories,
taskOrganizer,
taskInfo,
taskSurface,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
new file mode 100644
index 0000000..c470eef
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHost.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.Context
+import android.content.res.Configuration
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import androidx.tracing.Trace
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+typealias SurfaceControlViewHostFactory =
+ (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost
+
+/**
+ * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
+ *
+ * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
+ * any attempts to do will throw, which means that once a [View] is added using [updateView] or
+ * [updateViewAsync], only its properties and binding may be changed, its children views may be
+ * added, removed or changed and its [WindowManager.LayoutParams] may be changed. It also supports
+ * asynchronously updating the view hierarchy using [updateViewAsync], in which case the update work
+ * will be posted on the [ShellMainThread] with no delay.
+ */
+class DefaultWindowDecorViewHost(
+ private val context: Context,
+ @ShellMainThread private val mainScope: CoroutineScope,
+ private val display: Display,
+ private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
+ SurfaceControlViewHost(c, d, wwm, s)
+ },
+) : WindowDecorViewHost {
+
+ private val rootSurface: SurfaceControl =
+ SurfaceControl.Builder()
+ .setName("DefaultWindowDecorViewHost surface")
+ .setContainerLayer()
+ .setCallsite("DefaultWindowDecorViewHost#init")
+ .build()
+
+ private var wwm: WindowlessWindowManager? = null
+ @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
+ private var currentUpdateJob: Job? = null
+
+ override val surfaceControl: SurfaceControl
+ get() = rootSurface
+
+ override fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("DefaultWindowDecorViewHost#updateView")
+ clearCurrentUpdateJob()
+ updateViewHost(view, attrs, configuration, onDrawTransaction)
+ Trace.endSection()
+ }
+
+ override fun updateViewAsync(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ ) {
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
+ clearCurrentUpdateJob()
+ currentUpdateJob =
+ mainScope.launch {
+ updateViewHost(view, attrs, configuration, onDrawTransaction = null)
+ }
+ Trace.endSection()
+ }
+
+ override fun release(t: SurfaceControl.Transaction) {
+ clearCurrentUpdateJob()
+ viewHost?.release()
+ t.remove(rootSurface)
+ }
+
+ private fun updateViewHost(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ ) {
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
+ if (wwm == null) {
+ wwm = WindowlessWindowManager(configuration, rootSurface, null)
+ }
+ requireWindowlessWindowManager().setConfiguration(configuration)
+ if (viewHost == null) {
+ viewHost =
+ surfaceControlViewHostFactory.invoke(
+ context,
+ display,
+ requireWindowlessWindowManager(),
+ "DefaultWindowDecorViewHost#updateViewHost",
+ )
+ }
+ onDrawTransaction?.let { requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) }
+ if (requireViewHost().view == null) {
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
+ requireViewHost().setView(view, attrs)
+ Trace.endSection()
+ } else {
+ check(requireViewHost().view == view) { "Changing view is not allowed" }
+ Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
+ requireViewHost().relayout(attrs)
+ Trace.endSection()
+ }
+ Trace.endSection()
+ }
+
+ private fun clearCurrentUpdateJob() {
+ currentUpdateJob?.cancel()
+ currentUpdateJob = null
+ }
+
+ private fun requireWindowlessWindowManager(): WindowlessWindowManager {
+ return wwm ?: error("Expected non-null windowless window manager")
+ }
+
+ private fun requireViewHost(): SurfaceControlViewHost {
+ return viewHost ?: error("Expected non-null view host")
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
new file mode 100644
index 0000000..27ffd6c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostSupplier.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.Context
+import android.view.Display
+import android.view.SurfaceControl
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
+ */
+class DefaultWindowDecorViewHostSupplier(@ShellMainThread private val mainScope: CoroutineScope) :
+ WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {
+
+ override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
+ return DefaultWindowDecorViewHost(context, mainScope, display)
+ }
+
+ override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
+ viewHost.release(t)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
new file mode 100644
index 0000000..7c1479e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHost.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.res.Configuration
+import android.view.SurfaceControl
+import android.view.View
+import android.view.WindowManager
+import com.android.wm.shell.windowdecor.WindowDecoration
+
+/**
+ * An interface for a utility that hosts a [WindowDecoration]'s [View] hierarchy under a
+ * [SurfaceControl].
+ */
+interface WindowDecorViewHost {
+ /** The surface where the underlying [View] hierarchy is being rendered. */
+ val surfaceControl: SurfaceControl
+
+ /** Synchronously update the view hierarchy of this view host. */
+ fun updateView(
+ view: View,
+ attrs: WindowManager.LayoutParams,
+ configuration: Configuration,
+ onDrawTransaction: SurfaceControl.Transaction?,
+ )
+
+ /** Asynchronously update the view hierarchy of this view host. */
+ fun updateViewAsync(view: View, attrs: WindowManager.LayoutParams, configuration: Configuration)
+
+ /** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
+ fun release(t: SurfaceControl.Transaction)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHostSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHostSupplier.kt
new file mode 100644
index 0000000..00e29ec
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/viewhost/WindowDecorViewHostSupplier.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.content.Context
+import android.view.Display
+import android.view.SurfaceControl
+
+/** An interface for a supplier of [WindowDecorViewHost]s. */
+interface WindowDecorViewHostSupplier<T : WindowDecorViewHost> {
+ /** Acquire a [WindowDecorViewHost]. */
+ fun acquire(context: Context, display: Display): T
+
+ /**
+ * Release a [WindowDecorViewHost] when it is no longer used.
+ *
+ * @param viewHost the [WindowDecorViewHost] to release
+ * @param t a transaction that may be used to remove any underlying backing [SurfaceControl]
+ * that are hosting this [WindowDecorViewHost]. The supplier is not expected to apply the
+ * transaction. It should be applied by the owner of this supplier.
+ */
+ fun release(viewHost: T, t: SurfaceControl.Transaction)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
index 0e40a53..9db69d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModel.kt
@@ -33,6 +33,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.Transitions
@@ -48,7 +49,7 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
- private val taskRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val desktopModeEventLogger: DesktopModeEventLogger,
) : DisplayChangeController.OnDisplayChangingListener {
@VisibleForTesting
@@ -81,7 +82,7 @@
shellTaskOrganizer,
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
- taskRepository,
+ desktopUserRepositories,
desktopModeEventLogger,
)
tilingTransitionHandlerByDisplayId.put(displayId, newHandler)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 3b5c6ca..7ceac52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -49,6 +49,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.Transitions
@@ -72,7 +73,7 @@
private val shellTaskOrganizer: ShellTaskOrganizer,
private val toggleResizeDesktopTaskTransitionHandler: ToggleResizeDesktopTaskTransitionHandler,
private val returnToDragStartAnimator: ReturnToDragStartAnimator,
- private val taskRepository: DesktopRepository,
+ private val desktopUserRepositories: DesktopUserRepositories,
private val desktopModeEventLogger: DesktopModeEventLogger,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
) :
@@ -630,6 +631,7 @@
private fun allTiledTasksVisible(): Boolean {
val leftTiledTask = leftTaskResizingHelper ?: return false
val rightTiledTask = rightTaskResizingHelper ?: return false
+ val taskRepository = desktopUserRepositories.current
return taskRepository.isVisibleTask(leftTiledTask.taskInfo.taskId) &&
taskRepository.isVisibleTask(rightTiledTask.taskInfo.taskId)
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index d9c36cc..f6d2cc0 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -62,6 +62,14 @@
class DesktopModeFlickerScenarios {
companion object {
+ // In DesktopMode, window snap can be done with just a single window. In this case, the
+ // divider tiling between left and right window won't be shown, and hence its states are not
+ // obtainable in test.
+ // As the test should just focus on ensuring window goes to one side of the screen, an
+ // acceptable approach is to ensure snapped window still fills > 95% of either side of the
+ // screen.
+ private const val SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO = 0.05
+
val END_DRAG_TO_DESKTOP =
FlickerConfigEntry(
scenarioId = ScenarioId("END_DRAG_TO_DESKTOP"),
@@ -230,9 +238,11 @@
TaggedCujTransitionMatcher(associatedTransitionRequired = false)
)
.build(),
- assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowCoversLeftHalfScreenAtEnd(DESKTOP_MODE_APP))
- .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversLeftHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
val SNAP_RESIZE_RIGHT_WITH_BUTTON =
@@ -245,9 +255,11 @@
TaggedCujTransitionMatcher(associatedTransitionRequired = false)
)
.build(),
- assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
- listOf(AppWindowCoversRightHalfScreenAtEnd(DESKTOP_MODE_APP))
- .associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ assertions = AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS + listOf(
+ AppWindowCoversRightHalfScreenAtEnd(
+ DESKTOP_MODE_APP, SNAP_WINDOW_MAX_DIFF_THRESHOLD_RATIO
+ )
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
val SNAP_RESIZE_LEFT_WITH_DRAG =
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
index 706c632..1de47df 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-legacy/AndroidTestTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
index 7df1675..34d001c 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/flicker-service/AndroidTestTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
index 7df1675..34d001c 100644
--- a/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/e2e/splitscreen/platinum/AndroidTestTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index d87c179..9c1a8f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 99969e7..02b2cec 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index 19c3e40..a136936 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -25,7 +25,7 @@
<!-- keeps the screen on during tests -->
<option name="screen-always-on" value="on"/>
<!-- Turns off Wi-fi -->
- <option name="wifi" value="off"/>
+ <option name="wifi" value="on"/>
<!-- Turns off Bluetooth -->
<option name="bluetooth" value="off"/>
<!-- prevents the phone from restarting -->
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
@@ -107,4 +109,11 @@
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
+ <!-- Enable mocking GPS location by the test app -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="appops set com.android.shell android:mock_location allow"/>
+ <option name="teardown-command"
+ value="appops set com.android.shell android:mock_location deny"/>
+ </target_preparer>
</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index 7505860..34e4e74 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -48,6 +48,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index fd4328d..609a284 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -27,6 +27,7 @@
import android.tools.flicker.subject.exceptions.ExceptionMessageBuilder
import android.tools.flicker.subject.exceptions.IncorrectRegionException
import android.tools.flicker.subject.layers.LayerSubject
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
@@ -65,6 +66,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
index d4ad4ef8..5698023 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/AutoEnterPipWithSourceRectHintTest.kt
@@ -22,6 +22,7 @@
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import org.junit.FixMethodOrder
import org.junit.Test
@@ -57,6 +58,8 @@
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class AutoEnterPipWithSourceRectHintTest(flicker: LegacyFlickerTest) :
AutoEnterPipOnGoToHomeTest(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
pipApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index 53725fa..880e4cd 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -21,6 +21,7 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ClosePipTransition
import org.junit.FixMethodOrder
@@ -56,6 +57,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.closePipWindow(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index a1551b7..4399a23 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -21,6 +21,7 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.Assume
@@ -47,6 +48,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index ea5b3e5..49efd1d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -31,6 +31,7 @@
import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.Flags
@@ -72,6 +73,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index a109c4b..97cc9d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -20,6 +20,7 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.EnterPipTransition
import org.junit.FixMethodOrder
@@ -53,6 +54,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
open class EnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.clickEnterPipButton(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 14ec303..b5b7847 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -20,6 +20,7 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
@@ -56,6 +57,8 @@
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
ExitPipToAppTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 8a34b5e..f9a9df4 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -20,6 +20,7 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.ExitPipToAppTransition
import org.junit.FixMethodOrder
@@ -54,6 +55,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 12e2328..79e2e4e 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -27,6 +27,7 @@
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.helpers.WindowUtils
import android.tools.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.Flags
@@ -68,6 +69,7 @@
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
EnterPipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
private val secondAppForSplitScreen =
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
index 04016a9..14ae93a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipAspectRatioChangeTest.kt
@@ -23,6 +23,7 @@
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
@@ -37,6 +38,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipAspectRatioChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.changeAspectRatio(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 6bcaabc..81162c6 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -49,7 +49,8 @@
val stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
setup {
tapl.setEnableRotation(true)
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ pipApp.waitForPip(wmHelper)
// determine the direction of dragging to test for
isDraggedLeft = pipApp.isCloserToRightEdge(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index d82bfdd..6118d73 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -59,7 +59,8 @@
// Launch the PIP activity and wait for it to enter PiP mode
setRotation(Rotation.ROTATION_0)
RemoveAllTasksButHomeRule.removeAllTasksButHome()
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+ pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
+ pipApp.waitForPip(wmHelper)
// get the initial region bounds and cache them
val initRegion = pipApp.getWindowRect(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index dbc97d0..61c59cc 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -25,6 +25,7 @@
import android.tools.flicker.legacy.LegacyFlickerTest
import android.tools.flicker.legacy.LegacyFlickerTestFactory
import android.tools.flicker.subject.exceptions.IncorrectRegionException
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.wm.shell.Flags
import com.android.wm.shell.flicker.pip.common.PipTransition
import org.junit.FixMethodOrder
@@ -40,6 +41,8 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+ override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
+
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
index 65b60ce..0867f65 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/AppsEnterPipTransition.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.Postsubmit
import android.tools.Rotation
-import android.tools.device.apphelpers.StandardAppHelper
import android.tools.flicker.junit.FlickerBuilderProvider
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
@@ -29,8 +28,6 @@
import org.junit.runners.Parameterized
abstract class AppsEnterPipTransition(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
- protected abstract val standardAppHelper: StandardAppHelper
-
protected abstract val permissions: Array<String>
@FlickerBuilderProvider
@@ -39,7 +36,7 @@
instrumentation.uiAutomation.adoptShellPermissionIdentity()
for (permission in permissions) {
instrumentation.uiAutomation.grantRuntimePermission(
- standardAppHelper.packageName,
+ pipApp.packageName,
permission
)
}
@@ -48,18 +45,18 @@
}
}
- /** Checks [standardAppHelper] window remains visible throughout the animation */
+ /** Checks [pipApp] window remains visible throughout the animation */
@Postsubmit
@Test
override fun pipAppWindowAlwaysVisible() {
- flicker.assertWm { this.isAppWindowVisible(standardAppHelper.packageNameMatcher) }
+ flicker.assertWm { this.isAppWindowVisible(pipApp.packageNameMatcher) }
}
- /** Checks [standardAppHelper] layer remains visible throughout the animation */
+ /** Checks [pipApp] layer remains visible throughout the animation */
@Postsubmit
@Test
override fun pipAppLayerAlwaysVisible() {
- flicker.assertLayers { this.isVisible(standardAppHelper.packageNameMatcher) }
+ flicker.assertLayers { this.isVisible(pipApp.packageNameMatcher) }
}
/** Checks the content overlay appears then disappears during the animation */
@@ -70,39 +67,39 @@
}
/**
- * Checks that [standardAppHelper] window remains inside the display bounds throughout the whole
+ * Checks that [pipApp] window remains inside the display bounds throughout the whole
* animation
*/
@Postsubmit
@Test
override fun pipWindowRemainInsideVisibleBounds() {
- flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) {
+ flicker.assertWmVisibleRegion(pipApp.packageNameMatcher) {
coversAtMost(displayBounds)
}
}
/**
- * Checks that the [standardAppHelper] layer remains inside the display bounds throughout the
+ * Checks that the [pipApp] layer remains inside the display bounds throughout the
* whole animation
*/
@Postsubmit
@Test
override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
flicker.assertLayersVisibleRegion(
- standardAppHelper.packageNameMatcher.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ pipApp.packageNameMatcher.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
) {
coversAtMost(displayBounds)
}
}
- /** Checks that the visible region of [standardAppHelper] always reduces during the animation */
+ /** Checks that the visible region of [pipApp] always reduces during the animation */
@Postsubmit
@Test
override fun pipLayerReduces() {
flicker.assertLayers {
val pipLayerList =
this.layers {
- standardAppHelper.packageNameMatcher.layerMatchesAnyOf(it) && it.isVisible
+ pipApp.packageNameMatcher.layerMatchesAnyOf(it) && it.isVisible
}
pipLayerList.zipWithNext { previous, current ->
current.visibleRegion.notBiggerThan(previous.visibleRegion.region)
@@ -110,14 +107,14 @@
}
}
- /** Checks that [standardAppHelper] window becomes pinned */
+ /** Checks that [pipApp] window becomes pinned */
@Postsubmit
@Test
override fun pipWindowBecomesPinned() {
flicker.assertWm {
- invoke("pipWindowIsNotPinned") { it.isNotPinned(standardAppHelper.packageNameMatcher) }
+ invoke("pipWindowIsNotPinned") { it.isNotPinned(pipApp.packageNameMatcher) }
.then()
- .invoke("pipWindowIsPinned") { it.isPinned(standardAppHelper.packageNameMatcher) }
+ .invoke("pipWindowIsPinned") { it.isPinned(pipApp.packageNameMatcher) }
}
}
@@ -129,14 +126,14 @@
}
/**
- * Checks that the focus changes between the [standardAppHelper] window and the launcher when
+ * Checks that the focus changes between the [pipApp] window and the launcher when
* closing the pip window
*/
@Postsubmit
@Test
override fun focusChanges() {
flicker.assertEventLog {
- this.focusChanges(standardAppHelper.packageName, "NexusLauncherActivity")
+ this.focusChanges(pipApp.packageName, "NexusLauncherActivity")
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
index 7b04b76..651c923 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/MapsEnterPipTest.kt
@@ -29,6 +29,8 @@
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
+import android.tools.traces.component.ComponentRegexMatcher
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -63,7 +65,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class MapsEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
- override val standardAppHelper: MapsAppHelper = MapsAppHelper(instrumentation)
+ override val pipApp: MapsAppHelper = MapsAppHelper(instrumentation)
override val permissions: Array<String> =
arrayOf(Manifest.permission.POST_NOTIFICATIONS, Manifest.permission.ACCESS_FINE_LOCATION)
@@ -110,23 +112,23 @@
// normal app open through the Launcher All Apps
// var mapsAddressOption = "Golden Gate Bridge"
- // standardAppHelper.open()
- // standardAppHelper.doSearch(mapsAddressOption)
- // standardAppHelper.getDirections()
- // standardAppHelper.startNavigation();
+ // pipApp.open()
+ // pipApp.doSearch(mapsAddressOption)
+ // pipApp.getDirections()
+ // pipApp.startNavigation();
- standardAppHelper.launchViaIntent(
+ pipApp.launchViaIntent(
wmHelper,
MapsAppHelper.getMapIntent(MapsAppHelper.INTENT_NAVIGATION)
)
- standardAppHelper.waitForNavigationToStart()
+ pipApp.waitForNavigationToStart()
}
}
override val defaultTeardown: FlickerBuilder.() -> Unit = {
teardown {
- standardAppHelper.exit(wmHelper)
+ pipApp.exit(wmHelper)
mainHandler.removeCallbacks(updateLocation)
// the main looper callback might have tried to provide a new location after the
// provider is no longer in test mode, causing a crash, this prevents it from happening
@@ -137,14 +139,14 @@
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
- /** Checks [standardAppHelper] layer remains visible throughout the animation */
+ /** Checks [pipApp] layer remains visible throughout the animation */
@Postsubmit
@Test
override fun pipAppLayerAlwaysVisible() {
// For Maps the transition goes through the UI mode change that adds a snapshot overlay so
// we assert only start/end layers matching the app instead.
- flicker.assertLayersStart { this.isVisible(standardAppHelper.packageNameMatcher) }
- flicker.assertLayersEnd { this.isVisible(standardAppHelper.packageNameMatcher) }
+ flicker.assertLayersStart { this.isVisible(pipApp.packageNameMatcher) }
+ flicker.assertLayersEnd { this.isVisible(pipApp.packageNameMatcher) }
}
@Postsubmit
@@ -154,4 +156,15 @@
Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
super.focusChanges()
}
+
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ ignoreLayers = VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
+ + ComponentRegexMatcher(Regex("Background for .* SurfaceView\\[com\\.google\\.android\\.apps\\.maps/com\\.google\\.android\\.maps\\.MapsActivity\\]\\#\\d+"))
+ )
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
index 6911946..be4cd78 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/NetflixEnterPipTest.kt
@@ -61,7 +61,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class NetflixEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
- override val standardAppHelper: NetflixAppHelper = NetflixAppHelper(instrumentation)
+ override val pipApp: NetflixAppHelper = NetflixAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
@@ -69,17 +69,17 @@
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- standardAppHelper.launchViaIntent(
+ pipApp.launchViaIntent(
wmHelper,
NetflixAppHelper.getNetflixWatchVideoIntent("81605060"),
ComponentNameMatcher(NetflixAppHelper.PACKAGE_NAME, NetflixAppHelper.WATCH_ACTIVITY)
)
- standardAppHelper.waitForVideoPlaying()
+ pipApp.waitForVideoPlaying()
}
}
override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown { standardAppHelper.exit(wmHelper) }
+ teardown { pipApp.exit(wmHelper) }
}
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -143,7 +143,7 @@
// might go outside of bounds as we resize from landscape fullscreen to destination bounds,
// and once the animation is over we assert that it's fully within the display bounds, at
// which point the device also performs orientation change from landscape to portrait
- flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) {
+ flicker.assertWmVisibleRegion(pipApp.packageNameMatcher) {
regionsCenterPointInside(startingBounds).then().coversAtMost(endingBounds)
}
}
@@ -156,7 +156,7 @@
// and once the animation is over we assert that it's fully within the display bounds, at
// which point the device also performs orientation change from landscape to portrait
// since Netflix uses source rect hint, there is no PiP overlay present
- flicker.assertLayersVisibleRegion(standardAppHelper.packageNameMatcher) {
+ flicker.assertLayersVisibleRegion(pipApp.packageNameMatcher) {
regionsCenterPointInside(startingBounds).then().coversAtMost(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
index 5e54f30..3e4ff30 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipTest.kt
@@ -57,23 +57,23 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class YouTubeEnterPipTest(flicker: LegacyFlickerTest) : AppsEnterPipTransition(flicker) {
- override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+ override val pipApp: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
override val permissions: Array<String> = arrayOf(Manifest.permission.POST_NOTIFICATIONS)
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- standardAppHelper.launchViaIntent(
+ pipApp.launchViaIntent(
wmHelper,
YouTubeAppHelper.getYoutubeVideoIntent("3KtWfp0UopM"),
ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
)
- standardAppHelper.waitForVideoPlaying()
+ pipApp.waitForVideoPlaying()
}
}
override val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown { standardAppHelper.exit(wmHelper) }
+ teardown { pipApp.exit(wmHelper) }
}
override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
index 159cba4..2c6cb503 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/apps/YouTubeEnterPipToOtherOrientationTest.kt
@@ -63,7 +63,7 @@
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
open class YouTubeEnterPipToOtherOrientationTest(flicker: LegacyFlickerTest) :
YouTubeEnterPipTest(flicker) {
- override val standardAppHelper: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
+ override val pipApp: YouTubeAppHelper = YouTubeAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
@@ -71,13 +71,13 @@
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- standardAppHelper.launchViaIntent(
+ pipApp.launchViaIntent(
wmHelper,
YouTubeAppHelper.getYoutubeVideoIntent("3KtWfp0UopM"),
ComponentNameMatcher(YouTubeAppHelper.PACKAGE_NAME, "")
)
- standardAppHelper.enterFullscreen()
- standardAppHelper.waitForVideoPlaying()
+ pipApp.enterFullscreen()
+ pipApp.waitForVideoPlaying()
}
}
@@ -101,7 +101,7 @@
// might go outside of bounds as we resize from landscape fullscreen to destination bounds,
// and once the animation is over we assert that it's fully within the display bounds, at
// which point the device also performs orientation change from landscape to portrait
- flicker.assertWmVisibleRegion(standardAppHelper.packageNameMatcher) {
+ flicker.assertWmVisibleRegion(pipApp.packageNameMatcher) {
regionsCenterPointInside(startingBounds).then().coversAtMost(endingBounds)
}
}
@@ -114,7 +114,7 @@
// and once the animation is over we assert that it's fully within the display bounds, at
// which point the device also performs orientation change from landscape to portrait
// since YouTube uses source rect hint, there is no PiP overlay present
- flicker.assertLayersVisibleRegion(standardAppHelper.packageNameMatcher) {
+ flicker.assertLayersVisibleRegion(pipApp.packageNameMatcher) {
regionsCenterPointInside(startingBounds).then().coversAtMost(endingBounds)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
index 6dd3a17..a72de0f 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/EnterPipTransition.kt
@@ -71,7 +71,9 @@
@Presubmit
@Test
open fun pipLayerOrOverlayRemainInsideVisibleBounds() {
- flicker.assertLayersVisibleRegion(pipApp.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)) {
+ flicker.assertLayersVisibleRegion(
+ pipApp.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
+ ) {
coversAtMost(displayBounds)
}
}
@@ -117,7 +119,9 @@
@Presubmit
@Test
open fun focusChanges() {
- flicker.assertEventLog { this.focusChanges(pipApp.packageName, "NexusLauncherActivity") }
+ flicker.assertEventLog {
+ this.focusChanges(pipApp.packageName, "NexusLauncherActivity")
+ }
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
index c37bf35..7b6625d 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/common/PipTransition.kt
@@ -27,6 +27,7 @@
import android.tools.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import android.tools.helpers.WindowUtils
import android.tools.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.PipApp
import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -40,7 +41,6 @@
@Rule
val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
- protected val pipApp = PipAppHelper(instrumentation)
protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
@@ -63,6 +63,11 @@
}
}
+ /**
+ * Defines the test app to run PIP flicker test.
+ */
+ protected open val pipApp: PipApp = PipAppHelper(instrumentation)
+
/** Defines the transition used to run the test */
protected open val thisTransition: FlickerBuilder.() -> Unit = {}
@@ -85,10 +90,11 @@
/** Defines the default method of entering PiP */
protected open val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- pipApp.launchViaIntentAndWaitForPip(
+ pipApp.launchViaIntent(
wmHelper,
stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
)
+ pipApp.waitForPip(wmHelper)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index c4954f9..feb3edc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -20,6 +20,7 @@
import android.graphics.Point
import android.os.SystemClock
import android.tools.Rotation
+import android.tools.device.apphelpers.IStandardAppHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.traces.component.ComponentNameMatcher
@@ -102,8 +103,8 @@
wmHelper: WindowManagerStateHelper,
tapl: LauncherInstrumentation,
device: UiDevice,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper,
+ primaryApp: IStandardAppHelper,
+ secondaryApp: IStandardAppHelper,
rotation: Rotation
) {
primaryApp.launchViaIntent(wmHelper)
@@ -117,8 +118,8 @@
fun enterSplitViaIntent(
wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ primaryApp: IStandardAppHelper,
+ secondaryApp: IStandardAppHelper
) {
val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 0373bbd..6f3a3ec 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -172,10 +172,10 @@
var mockPp = mock(DisplayImeController.ImePositionProcessor.class);
mDisplayImeController.addPositionProcessor(mockPp);
- mPerDisplay.setImeInputTargetRequestedVisibility(true);
+ mPerDisplay.setImeInputTargetRequestedVisibility(true, null /* statsToken */);
verify(mockPp).onImeRequested(anyInt(), eq(true));
- mPerDisplay.setImeInputTargetRequestedVisibility(false);
+ mPerDisplay.setImeInputTargetRequestedVisibility(false, null /* statsToken */);
verify(mockPp).onImeRequested(anyInt(), eq(false));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
index cf69704..fd3d3b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java
@@ -56,6 +56,7 @@
private @Mock DisplayController mDisplayController;
private @Mock DisplayImeController mDisplayImeController;
private @Mock ShellTaskOrganizer mTaskOrganizer;
+ private @Mock SplitState mSplitState;
private @Mock Handler mHandler;
private SplitLayout mSplitLayout;
private DividerView mDividerView;
@@ -67,7 +68,7 @@
Configuration configuration = getConfiguration();
mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration,
mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController,
- mTaskOrganizer, SplitLayout.PARALLAX_NONE, mHandler);
+ mTaskOrganizer, SplitLayout.PARALLAX_NONE, mSplitState, mHandler);
SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager",
mContext,
configuration, mCallbacks);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index dc0f213..1904c43 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -66,6 +66,7 @@
@Mock DisplayImeController mDisplayImeController;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock WindowContainerTransaction mWct;
+ @Mock SplitState mSplitState;
@Mock Handler mHandler;
@Captor ArgumentCaptor<Runnable> mRunnableCaptor;
private SplitLayout mSplitLayout;
@@ -83,6 +84,7 @@
mDisplayImeController,
mTaskOrganizer,
SplitLayout.PARALLAX_NONE,
+ mSplitState,
mHandler));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index d5287e7..67573da 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -30,6 +30,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.TaskInfo;
@@ -61,12 +62,16 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.api.CompatUIInfo;
+import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import dagger.Lazy;
+import java.util.Optional;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -130,6 +135,10 @@
private CompatUIShellCommandHandler mCompatUIShellCommandHandler;
@Mock
private AccessibilityManager mAccessibilityManager;
+ @Mock
+ private DesktopUserRepositories mDesktopUserRepositories;
+ @Mock
+ private DesktopRepository mDesktopRepository;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -137,7 +146,6 @@
@NonNull
private CompatUIStatusManager mCompatUIStatusManager;
- private boolean mInDesktopModePredicateResult;
@Before
public void setUp() {
@@ -152,6 +160,8 @@
doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId();
doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean());
doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean());
+ doReturn(mDesktopRepository).when(mDesktopUserRepositories).getCurrent();
+ doReturn(mDesktopRepository).when(mDesktopUserRepositories).getProfile(anyInt());
doReturn(DISPLAY_ID).when(mMockRestartDialogLayout).getDisplayId();
doReturn(TASK_ID).when(mMockRestartDialogLayout).getTaskId();
@@ -164,7 +174,7 @@
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager,
- mCompatUIStatusManager, i -> mInDesktopModePredicateResult) {
+ mCompatUIStatusManager, Optional.of(mDesktopUserRepositories)) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
@@ -707,13 +717,17 @@
@RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
@EnableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagEnabled() {
- mInDesktopModePredicateResult = false;
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+ when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
+
mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+
verify(mController, never()).removeLayouts(taskInfo.taskId);
- mInDesktopModePredicateResult = true;
+ when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(2);
+
mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+
verify(mController).removeLayouts(taskInfo.taskId);
}
@@ -721,13 +735,17 @@
@RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
@DisableFlags(Flags.FLAG_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE)
public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagDisabled() {
- mInDesktopModePredicateResult = false;
+ when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
+
mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+
verify(mController, never()).removeLayouts(taskInfo.taskId);
- mInDesktopModePredicateResult = true;
+ when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(2);
+
mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
+
verify(mController, never()).removeLayouts(taskInfo.taskId);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
index 68d9bf9..70341b6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxSurfaceBuilderTest.kt
@@ -30,9 +30,7 @@
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
-import org.mockito.kotlin.never
import org.mockito.kotlin.times
-import org.mockito.verification.VerificationMode
/**
* Tests for [LetterboxSurfaceBuilder].
@@ -140,7 +138,5 @@
val components = letterboxConfiguration.getBackgroundColorRgbArray()
verify(tx, expected.asMode()).setColor(anyOrNull(), eq(components))
}
-
- private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt
new file mode 100644
index 0000000..29ea09a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTestUtils.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.compatui.letterbox
+
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.verification.VerificationMode
+
+// Utility to make verification mode depending on a [Boolean].
+fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
index 07bfefe..8825bb4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -43,10 +43,8 @@
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
-import org.mockito.verification.VerificationMode
/**
* Tests for [LetterboxTransitionObserver].
@@ -292,14 +290,16 @@
expected: Boolean,
displayId: Int = DISPLAY_ID,
taskId: Int = TASK_ID,
- taskBounds: Rect = Rect()
+ taskBounds: Rect = Rect(),
+ activityBounds: Rect = Rect()
) = verify(
letterboxController,
expected.asMode()
).updateLetterboxSurfaceBounds(
eq(LetterboxKey(displayId, taskId)),
any<SurfaceControl.Transaction>(),
- eq(taskBounds)
+ eq(taskBounds),
+ eq(activityBounds)
)
fun createTopActivityChange(
@@ -334,7 +334,5 @@
this.displayId = displayId
}, changeMode = TRANSIT_CLOSE)
}
-
- private fun Boolean.asMode(): VerificationMode = if (this) times(1) else never()
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index 2ea0379..41a594a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -23,6 +23,7 @@
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
import android.graphics.Rect
import android.os.Binder
+import android.os.UserManager
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
@@ -96,11 +97,12 @@
@Mock lateinit var taskStackListener: TaskStackListenerImpl
@Mock lateinit var persistentRepository: DesktopPersistentRepository
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
+ @Mock lateinit var userManager: UserManager
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var handler: DesktopActivityOrientationChangeHandler
private lateinit var shellInit: ShellInit
- private lateinit var taskRepository: DesktopRepository
+ private lateinit var userRepositories: DesktopUserRepositories
private lateinit var testScope: CoroutineScope
// Mock running tasks are registered here so we can get the list from mock shell task organizer.
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -117,13 +119,14 @@
testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
shellInit = spy(ShellInit(testExecutor))
- taskRepository =
- DesktopRepository(
+ userRepositories =
+ DesktopUserRepositories(
context,
shellInit,
persistentRepository,
repositoryInitializer,
- testScope
+ testScope,
+ userManager
)
whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
@@ -132,7 +135,7 @@
)
handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
- taskStackListener, resizeTransitionHandler, taskRepository)
+ taskStackListener, resizeTransitionHandler, userRepositories)
shellInit.init()
}
@@ -156,7 +159,7 @@
clearInvocations(shellInit)
handler = DesktopActivityOrientationChangeHandler(context, shellInit, shellTaskOrganizer,
- taskStackListener, resizeTransitionHandler, taskRepository)
+ taskStackListener, resizeTransitionHandler, userRepositories)
verify(shellInit, never()).addInitCallback(any(),
any<DesktopActivityOrientationChangeHandler>())
@@ -180,7 +183,7 @@
activityInfo.screenOrientation = SCREEN_ORIENTATION_PORTRAIT
task.topActivityInfo = activityInfo
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- taskRepository.addTask(DEFAULT_DISPLAY, task.taskId, isVisible = true)
+ userRepositories.current.addTask(DEFAULT_DISPLAY, task.taskId, isVisible = true)
runningTasks.add(task)
taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
@@ -203,7 +206,7 @@
@Test
fun handleActivityOrientationChange_notInDesktopMode_doNothing() {
val task = setUpFreeformTask(isResizeable = false)
- taskRepository.updateTask(task.displayId, task.taskId, isVisible = false)
+ userRepositories.current.updateTask(task.displayId, task.taskId, isVisible = false)
taskStackListener.onActivityRequestedOrientationChanged(task.taskId,
SCREEN_ORIENTATION_LANDSCAPE)
@@ -268,7 +271,7 @@
task.topActivityInfo = activityInfo
task.isResizeable = isResizeable
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
- taskRepository.addTask(displayId, task.taskId, isVisible = true)
+ userRepositories.current.addTask(displayId, task.taskId, isVisible = true)
runningTasks.add(task)
return task
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index b57c55c..db4c746 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -76,18 +76,19 @@
@JvmField @Rule val animatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var mockTransitions: Transitions
- private lateinit var desktopRepository: DesktopRepository
+ private lateinit var userRepositories: DesktopUserRepositories
@Mock private lateinit var mockDisplayController: DisplayController
@Mock private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
@Mock private lateinit var mockDisplayLayout: DisplayLayout
private val transactionSupplier = { StubTransaction() }
private lateinit var controller: DesktopImmersiveController
+ private lateinit var desktopRepository: DesktopRepository
@Before
fun setUp() {
- desktopRepository = DesktopRepository(
- context, ShellInit(TestShellExecutor()), mock(), mock(), mock()
+ userRepositories = DesktopUserRepositories(
+ context, ShellInit(TestShellExecutor()), mock(), mock(), mock(), mock()
)
whenever(mockDisplayController.getDisplayLayout(DEFAULT_DISPLAY))
.thenReturn(mockDisplayLayout)
@@ -97,12 +98,13 @@
controller = DesktopImmersiveController(
shellInit = mock(),
transitions = mockTransitions,
- desktopRepository = desktopRepository,
+ desktopUserRepositories = userRepositories,
displayController = mockDisplayController,
shellTaskOrganizer = mockShellTaskOrganizer,
shellCommandHandler = mock(),
transactionSupplier = transactionSupplier,
)
+ desktopRepository = userRepositories.current
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 62717a3..2d55445 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -60,6 +60,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
@@ -84,7 +85,7 @@
@Mock
lateinit var transitions: Transitions
@Mock
- lateinit var desktopRepository: DesktopRepository
+ lateinit var userRepositories: DesktopUserRepositories
@Mock
lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
@Mock
@@ -103,16 +104,21 @@
lateinit var shellInit: ShellInit
@Mock
lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock
+ private lateinit var desktopRepository: DesktopRepository
private lateinit var mixedHandler: DesktopMixedTransitionHandler
+
@Before
fun setUp() {
+ whenever(userRepositories.current).thenReturn(desktopRepository)
+ whenever(userRepositories.getProfile(Mockito.anyInt())).thenReturn(desktopRepository)
mixedHandler =
DesktopMixedTransitionHandler(
context,
transitions,
- desktopRepository,
+ userRepositories,
freeformTaskTransitionHandler,
closeDesktopTaskTransitionHandler,
desktopImmersiveController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 6050695..dc7fb5f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -136,8 +136,13 @@
desktopModeKeyGestureHandler = DesktopModeKeyGestureHandler(
context,
- Optional.of(desktopModeWindowDecorViewModel), Optional.of(desktopTasksController),
- inputManager, shellTaskOrganizer, focusTransitionObserver, testExecutor
+ Optional.of(desktopModeWindowDecorViewModel),
+ Optional.of(desktopTasksController),
+ inputManager,
+ shellTaskOrganizer,
+ focusTransitionObserver,
+ testExecutor,
+ displayController
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index 7f790d5..9059d7d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -30,7 +30,6 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.persistence.Desktop
import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository
-import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.fail
@@ -70,7 +69,6 @@
@Mock private lateinit var testExecutor: ShellExecutor
@Mock private lateinit var persistentRepository: DesktopPersistentRepository
- @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
@Before
fun setUp() {
@@ -80,11 +78,9 @@
repo =
DesktopRepository(
- context,
- shellInit,
persistentRepository,
- repositoryInitializer,
- datastoreScope
+ datastoreScope,
+ DEFAULT_USER_ID
)
whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }).thenReturn(
Desktop.getDefaultInstance()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 8e323ac..b4daa66 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
@@ -29,6 +30,7 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
@@ -47,131 +49,163 @@
private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
+ private val desktopUserRepositories = mock<DesktopUserRepositories>()
private val desktopRepository = mock<DesktopRepository>()
@Before
fun setUp() {
- desktopTaskChangeListener = DesktopTaskChangeListener(desktopRepository)
+ desktopTaskChangeListener = DesktopTaskChangeListener(desktopUserRepositories)
+
+ whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
+ whenever(desktopUserRepositories.getProfile(anyInt())).thenReturn(desktopRepository)
}
@Test
fun onTaskOpening_fullscreenTask_notActiveDesktopTask_noop() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(false)
desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopRepository, never()).addTask(task.displayId, task.taskId, task.isVisible)
- verify(desktopRepository, never()).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current, never())
+ .addTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current, never())
+ .removeFreeformTask(task.displayId, task.taskId)
}
@Test
fun onTaskOpening_freeformTask_activeDesktopTask_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeFreeformTask(task.displayId, task.taskId)
}
@Test
fun onTaskOpening_freeformTask_visibleDesktopTask_addsTaskToRepository() {
val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(false)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(false)
desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopRepository).addTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current)
+ .addTask(task.displayId, task.taskId, task.isVisible)
}
@Test
fun onTaskOpening_freeformTask_nonVisibleDesktopTask_addsTaskToRepository() {
val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskOpening(task)
- verify(desktopRepository).addTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current)
+ .addTask(task.displayId, task.taskId, task.isVisible)
}
@Test
fun onTaskChanging_freeformTaskOutsideDesktop_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskChanging(task)
- verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current)
+ .removeFreeformTask(task.displayId, task.taskId)
}
@Test
fun onTaskChanging_visibleTaskInDesktop_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskChanging(task)
- verify(desktopRepository).updateTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, task.isVisible)
}
@Test
fun onTaskChanging_nonVisibleTask_updatesTaskVisibility() {
val task = createFreeformTask().apply { isVisible = false }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskChanging(task)
- verify(desktopRepository).updateTask(task.displayId, task.taskId, task.isVisible)
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, task.isVisible)
}
@Test
fun onTaskMovingToFront_freeformTaskOutsideDesktop_removesTaskFromRepo() {
val task = createFullscreenTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskMovingToFront(task)
- verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current)
+ .removeFreeformTask(task.displayId, task.taskId)
}
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavEnabled_nonClosingTask_minimizesTaskInRepo() {
val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
- whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
+ .thenReturn(false)
desktopTaskChangeListener.onTaskClosing(task)
- verify(desktopRepository).updateTask(task.displayId, task.taskId, isVisible = false)
- verify(desktopRepository).minimizeTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current)
+ .updateTask(task.displayId, task.taskId, isVisible = false)
+ verify(desktopUserRepositories.current)
+ .minimizeTask(task.displayId, task.taskId)
}
@Test
@DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavDisabled_closingTask_removesTaskInRepo() {
val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
- whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskClosing(task)
- verify(desktopRepository, never()).minimizeTask(task.displayId, task.taskId)
- verify(desktopRepository).removeClosingTask(task.taskId)
- verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current, never())
+ .minimizeTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current)
+ .removeClosingTask(task.taskId)
+ verify(desktopUserRepositories.current)
+ .removeFreeformTask(task.displayId, task.taskId)
}
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun onTaskClosing_backNavEnabled_closingTask_removesTaskFromRepo() {
val task = createFreeformTask().apply { isVisible = true }
- whenever(desktopRepository.isActiveTask(task.taskId)).thenReturn(true)
- whenever(desktopRepository.isClosingTask(task.taskId)).thenReturn(true)
+ whenever(desktopUserRepositories.current.isActiveTask(task.taskId))
+ .thenReturn(true)
+ whenever(desktopUserRepositories.current.isClosingTask(task.taskId))
+ .thenReturn(true)
desktopTaskChangeListener.onTaskClosing(task)
- verify(desktopRepository).removeClosingTask(task.taskId)
- verify(desktopRepository).removeFreeformTask(task.displayId, task.taskId)
+ verify(desktopUserRepositories.current).removeClosingTask(task.taskId)
+ verify(desktopUserRepositories.current)
+ .removeFreeformTask(task.displayId, task.taskId)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 5c00272..c10434a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -44,6 +44,7 @@
import android.os.Bundle
import android.os.Handler
import android.os.IBinder
+import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -93,6 +94,7 @@
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -233,9 +235,11 @@
@Mock private lateinit var resources: Resources
@Mock
lateinit var desktopModeEnterExitTransitionListener: DesktopModeEntryExitTransitionListener
+ @Mock private lateinit var userManager: UserManager
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
private lateinit var taskRepository: DesktopRepository
+ private lateinit var userRepositories: DesktopUserRepositories
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
private lateinit var testScope: CoroutineScope
@@ -267,12 +271,18 @@
testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
shellInit = spy(ShellInit(testExecutor))
- taskRepository =
- DesktopRepository(context, shellInit, persistentRepository, repositoryInitializer, testScope)
+ userRepositories =
+ DesktopUserRepositories(
+ context,
+ shellInit,
+ persistentRepository,
+ repositoryInitializer,
+ testScope,
+ userManager)
desktopTasksLimiter =
DesktopTasksLimiter(
transitions,
- taskRepository,
+ userRepositories,
shellTaskOrganizer,
MAX_TASK_LIMIT,
mockInteractionJankMonitor,
@@ -315,6 +325,8 @@
controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ taskRepository = userRepositories.current
}
private fun createController(): DesktopTasksController {
@@ -338,7 +350,7 @@
toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler,
mMockDesktopImmersiveController,
- taskRepository,
+ userRepositories,
recentsTransitionHandler,
multiInstanceHelper,
shellExecutor,
@@ -377,7 +389,14 @@
val task1 = setUpFreeformTask()
val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
- controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task1,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
@@ -399,21 +418,29 @@
}
@Test
- fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() {
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfMaximizedTask_returnFalse() {
val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
- controller.toggleDesktopTaskSize(task1, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task1,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH
+ )
+ )
verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
verify(desktopModeEventLogger, times(1)).logTaskResizingEnded(
- ResizeTrigger.MAXIMIZE_BUTTON,
- InputMethod.TOUCH,
- task1,
- 0,
- 0,
- displayController
+ eq(ResizeTrigger.MAXIMIZE_BUTTON),
+ eq(InputMethod.TOUCH),
+ eq(task1),
+ anyOrNull(),
+ anyOrNull(),
+ eq(displayController),
+ anyOrNull()
)
assertThat(argumentCaptor.value).isFalse()
}
@@ -3359,7 +3386,14 @@
val bounds = Rect(0, 0, 100, 100)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct()
@@ -3584,7 +3618,14 @@
// Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct()
@@ -3604,7 +3645,15 @@
val bounds = Rect(0, 0, 100, 100)
val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
+
assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isEqualTo(bounds)
verify(desktopModeEventLogger, never()).logTaskResizingEnded(
any(), any(), any(), any(),
@@ -3618,11 +3667,25 @@
val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
// Maximize
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
// Restore
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH
+ )
+ )
// Assert bounds set to last bounds before maximize
val wct = getLatestToggleResizeDesktopTaskWct()
@@ -3645,12 +3708,26 @@
}
// Maximize
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left,
boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom)
// Restore
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH
+ )
+ )
// Assert bounds set to last bounds before maximize
val wct = getLatestToggleResizeDesktopTaskWct()
@@ -3673,12 +3750,26 @@
}
// Maximize
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left,
STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom)
// Restore
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH
+ )
+ )
// Assert bounds set to last bounds before maximize
val wct = getLatestToggleResizeDesktopTaskWct()
@@ -3699,11 +3790,25 @@
val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)
// Maximize
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.TOUCH
+ )
+ )
task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)
// Restore
- controller.toggleDesktopTaskSize(task, ResizeTrigger.MAXIMIZE_BUTTON, InputMethod.TOUCH)
+ controller.toggleDesktopTaskSize(
+ task,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.RESTORE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_RESTORE,
+ InputMethod.TOUCH
+ )
+ )
// Assert last bounds before maximize removed after use
assertThat(taskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 797b125..0712d58 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -20,6 +20,7 @@
import android.graphics.Rect
import android.os.Binder
import android.os.Handler
+import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
@@ -73,7 +74,6 @@
import org.mockito.kotlin.verify
import org.mockito.quality.Strictness
-
/**
* Test class for {@link DesktopTasksLimiter}
*
@@ -95,9 +95,11 @@
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var persistentRepository: DesktopPersistentRepository
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
+ @Mock lateinit var userManager: UserManager
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var desktopTasksLimiter: DesktopTasksLimiter
+ private lateinit var userRepositories: DesktopUserRepositories
private lateinit var desktopTaskRepo: DesktopRepository
private lateinit var shellInit: ShellInit
private lateinit var testScope: CoroutineScope
@@ -111,16 +113,18 @@
Dispatchers.setMain(StandardTestDispatcher())
testScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
- desktopTaskRepo =
- DesktopRepository(
+ userRepositories =
+ DesktopUserRepositories(
context,
shellInit,
persistentRepository,
repositoryInitializer,
- testScope
+ testScope,
+ userManager
)
+ desktopTaskRepo = userRepositories.current
desktopTasksLimiter =
- DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT,
+ DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT,
interactionJankMonitor, mContext, handler)
}
@@ -133,7 +137,7 @@
@Test
fun createDesktopTasksLimiter_withZeroLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
- DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, 0,
+ DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, 0,
interactionJankMonitor, mContext, handler)
}
}
@@ -141,7 +145,7 @@
@Test
fun createDesktopTasksLimiter_withNegativeLimit_shouldThrow() {
assertFailsWith<IllegalArgumentException> {
- DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, -5,
+ DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, -5,
interactionJankMonitor, mContext, handler)
}
}
@@ -411,7 +415,7 @@
@Test
fun getTaskToMinimize_tasksAboveLimit_otherLimit_returnsBackTask() {
desktopTasksLimiter =
- DesktopTasksLimiter(transitions, desktopTaskRepo, shellTaskOrganizer, MAX_TASK_LIMIT2,
+ DesktopTasksLimiter(transitions, userRepositories, shellTaskOrganizer, MAX_TASK_LIMIT2,
interactionJankMonitor, mContext, handler)
val tasks = (1..MAX_TASK_LIMIT2 + 1).map { setUpFreeformTask() }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index 7f1c1db..b31a3f5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -39,6 +39,7 @@
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -50,6 +51,7 @@
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isA
import org.mockito.Mockito
@@ -66,17 +68,17 @@
@JvmField
@Rule
val extendedMockitoRule =
- ExtendedMockitoRule.Builder(this)
- .mockStatic(DesktopModeStatus::class.java)
- .build()!!
+ ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!
private val testExecutor = mock<ShellExecutor>()
private val mockShellInit = mock<ShellInit>()
private val transitions = mock<Transitions>()
private val context = mock<Context>()
private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
+ private val userRepositories = mock<DesktopUserRepositories>()
private val taskRepository = mock<DesktopRepository>()
private val mixedHandler = mock<DesktopMixedTransitionHandler>()
+ private val backAnimationController = mock<BackAnimationController>()
private lateinit var transitionObserver: DesktopTasksTransitionObserver
private lateinit var shellInit: ShellInit
@@ -86,9 +88,18 @@
whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
shellInit = spy(ShellInit(testExecutor))
+ whenever(userRepositories.current).thenReturn(taskRepository)
+ whenever(userRepositories.getProfile(anyInt())).thenReturn(taskRepository)
+
transitionObserver =
DesktopTasksTransitionObserver(
- context, taskRepository, transitions, shellTaskOrganizer, mixedHandler, shellInit
+ context,
+ userRepositories,
+ transitions,
+ shellTaskOrganizer,
+ mixedHandler,
+ backAnimationController,
+ shellInit
)
}
@@ -100,8 +111,7 @@
transitionObserver.onTransitionReady(
transition = mock(),
- info =
- createBackNavigationTransition(task),
+ info = createBackNavigationTransition(task),
startTransaction = mock(),
finishTransaction = mock(),
)
@@ -112,14 +122,59 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun backNavigation_withCloseTransitionNotLastTask_taskMinimized() {
+ val task = createTaskInfo(1)
+ val transition = mock<IBinder>()
+ whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(2)
+ whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = transition,
+ info = createBackNavigationTransition(task, TRANSIT_CLOSE),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ val pendingTransition =
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, task.taskId, isLastTask = false)
+ verify(mixedHandler).addPendingMixedTransition(pendingTransition)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
+ fun backNavigation_withCloseTransitionLastTask_taskMinimized() {
+ val task = createTaskInfo(1)
+ val transition = mock<IBinder>()
+ whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
+ whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
+ whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)
+
+ transitionObserver.onTransitionReady(
+ transition = transition,
+ info = createBackNavigationTransition(task, TRANSIT_CLOSE, true),
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(taskRepository).minimizeTask(task.displayId, task.taskId)
+ val pendingTransition =
+ DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
+ transition, task.taskId, isLastTask = true)
+ verify(mixedHandler).addPendingMixedTransition(pendingTransition)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
fun backNavigation_nullTaskInfo_taskNotMinimized() {
val task = createTaskInfo(1)
whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
transitionObserver.onTransitionReady(
transition = mock(),
- info =
- createBackNavigationTransition(null),
+ info = createBackNavigationTransition(null),
startTransaction = mock(),
finishTransaction = mock(),
)
@@ -168,7 +223,7 @@
val mockTransition = Mockito.mock(IBinder::class.java)
val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
val wallpaperToken = MockToken().token()
- whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(1)
+ whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(0)
whenever(taskRepository.wallpaperActivityToken).thenReturn(wallpaperToken)
transitionObserver.onTransitionReady(
@@ -185,17 +240,27 @@
}
private fun createBackNavigationTransition(
- task: RunningTaskInfo?
+ task: RunningTaskInfo?,
+ type: Int = TRANSIT_TO_BACK,
+ withWallpaper: Boolean = false,
): TransitionInfo {
- return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+ return TransitionInfo(type, 0 /* flags */).apply {
addChange(
Change(mock(), mock()).apply {
- mode = TRANSIT_TO_BACK
+ mode = type
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
+ if (withWallpaper) {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_CLOSE
+ parent = null
+ taskInfo = createWallpaperTaskInfo()
+ flags = flags
+ })
+ }
}
}
@@ -210,14 +275,11 @@
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
}
}
- private fun createCloseTransition(
- task: RunningTaskInfo?
- ): TransitionInfo {
+ private fun createCloseTransition(task: RunningTaskInfo?): TransitionInfo {
return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
addChange(
Change(mock(), mock()).apply {
@@ -225,8 +287,7 @@
parent = null
taskInfo = task
flags = flags
- }
- )
+ })
}
}
@@ -238,8 +299,7 @@
if (handlerClass == null) {
Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isNull())
} else {
- Mockito.verify(transitions)
- .startTransition(eq(type), arg.capture(), isA(handlerClass))
+ Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
}
return arg.value
}
@@ -263,8 +323,15 @@
displayId = DEFAULT_DISPLAY
configuration.windowConfiguration.windowingMode = windowingMode
token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
- baseIntent = Intent().apply {
- component = ComponentName("package", "component.name")
- }
+ baseIntent = Intent().apply { component = ComponentName("package", "component.name") }
+ }
+
+ private fun createWallpaperTaskInfo() =
+ RunningTaskInfo().apply {
+ token = mock<WindowContainerToken>()
+ baseIntent =
+ Intent().apply {
+ component = DesktopWallpaperActivity.wallpaperActivityComponent
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
index 226e974..b9d7bbf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/compatui/SystemModalsTransitionHandlerTest.kt
@@ -25,10 +25,11 @@
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTaskBuilder
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSystemModalTask
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
@@ -49,6 +50,7 @@
private val animExecutor = mock<ShellExecutor>()
private val shellInit = mock<ShellInit>()
private val transitions = mock<Transitions>()
+ private val desktopUserRepositories = mock<DesktopUserRepositories>()
private val desktopRepository = mock<DesktopRepository>()
private val startT = mock<SurfaceControl.Transaction>()
private val finishT = mock<SurfaceControl.Transaction>()
@@ -58,6 +60,7 @@
@Before
fun setUp() {
// Simulate having one Desktop task so that we see Desktop Mode as active
+ whenever(desktopUserRepositories.current).thenReturn(desktopRepository)
whenever(desktopRepository.getVisibleTaskCount(anyInt())).thenReturn(1)
transitionHandler = createTransitionHandler()
}
@@ -69,7 +72,7 @@
animExecutor,
shellInit,
transitions,
- desktopRepository,
+ desktopUserRepositories,
)
@Test
@@ -79,7 +82,7 @@
@Test
fun startAnimation_desktopNotActive_doesNotAnimate() {
- whenever(desktopRepository.getVisibleTaskCount(anyInt())).thenReturn(1)
+ whenever(desktopUserRepositories.current.getVisibleTaskCount(anyInt())).thenReturn(1)
val info =
TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN, createSystemModalTask())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
index 8495580..4f7e80c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
@@ -112,7 +112,8 @@
datastoreRepository.addOrUpdateDesktop(
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
- freeformTasksInZOrder = freeformTasksInZOrder)
+ freeformTasksInZOrder = freeformTasksInZOrder,
+ userId = DEFAULT_USER_ID)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap).hasSize(2)
@@ -135,7 +136,8 @@
datastoreRepository.addOrUpdateDesktop(
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
- freeformTasksInZOrder = freeformTasksInZOrder)
+ freeformTasksInZOrder = freeformTasksInZOrder,
+ userId = DEFAULT_USER_ID)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap?.get(task.taskId)?.desktopTaskState)
@@ -158,7 +160,8 @@
datastoreRepository.addOrUpdateDesktop(
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
- freeformTasksInZOrder = freeformTasksInZOrder)
+ freeformTasksInZOrder = freeformTasksInZOrder,
+ userId = DEFAULT_USER_ID)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
assertThat(actualDesktop?.tasksByTaskIdMap).isEmpty()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index 9753429..1c88a29 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -16,14 +16,17 @@
package com.android.wm.shell.desktopmode.persistence
+import android.os.UserManager
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_HSUM
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
@@ -36,26 +39,30 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.inOrder
import org.mockito.Mockito.spy
-import org.mockito.kotlin.any
import org.mockito.kotlin.mock
-import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
+
@SmallTest
@RunWith(AndroidTestingRunner::class)
@ExperimentalCoroutinesApi
class DesktopRepositoryInitializerTest : ShellTestCase() {
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule()
+
private lateinit var repositoryInitializer: DesktopRepositoryInitializer
private lateinit var shellInit: ShellInit
private lateinit var datastoreScope: CoroutineScope
- private lateinit var desktopRepository: DesktopRepository
+ private lateinit var desktopUserRepositories: DesktopUserRepositories
private val persistentRepository = mock<DesktopPersistentRepository>()
+ private val userManager = mock<UserManager>()
private val testExecutor = mock<ShellExecutor>()
@Before
@@ -65,55 +72,193 @@
datastoreScope = CoroutineScope(Dispatchers.Unconfined + SupervisorJob())
repositoryInitializer =
DesktopRepositoryInitializerImpl(context, persistentRepository, datastoreScope)
- desktopRepository =
- DesktopRepository(
- context, shellInit, persistentRepository, repositoryInitializer, datastoreScope)
+ desktopUserRepositories =
+ DesktopUserRepositories(
+ context, shellInit, persistentRepository, repositoryInitializer, datastoreScope,
+ userManager
+ )
}
@Test
- @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
- fun initWithPersistence_multipleTasks_addedCorrectly() =
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, FLAG_ENABLE_DESKTOP_WINDOWING_HSUM)
+ fun initWithPersistence_multipleUsers_addedCorrectly() =
runTest(StandardTestDispatcher()) {
- val freeformTasksInZOrder = listOf(1, 2, 3)
- whenever(persistentRepository.readDesktop(any(), any()))
- .thenReturn(
- Desktop.newBuilder()
- .setDesktopId(1)
- .addAllZOrderedTasks(freeformTasksInZOrder)
- .putTasksByTaskId(
- 1,
- DesktopTask.newBuilder()
- .setTaskId(1)
- .setDesktopTaskState(DesktopTaskState.VISIBLE)
- .build())
- .putTasksByTaskId(
- 2,
- DesktopTask.newBuilder()
- .setTaskId(2)
- .setDesktopTaskState(DesktopTaskState.VISIBLE)
- .build())
- .putTasksByTaskId(
- 3,
- DesktopTask.newBuilder()
- .setTaskId(3)
- .setDesktopTaskState(DesktopTaskState.MINIMIZED)
- .build())
- .build())
+ whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
+ mapOf(
+ USER_ID_1 to desktopRepositoryState1,
+ USER_ID_2 to desktopRepositoryState2
+ )
+ )
+ whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
+ .thenReturn(desktopRepositoryState1)
+ whenever(persistentRepository.getDesktopRepositoryState(USER_ID_2))
+ .thenReturn(desktopRepositoryState2)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1))
+ .thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2))
+ .thenReturn(desktop2)
+ whenever(persistentRepository.readDesktop(USER_ID_2, DESKTOP_ID_3))
+ .thenReturn(desktop3)
- repositoryInitializer.initialize(desktopRepository)
+ repositoryInitializer.initialize(desktopUserRepositories)
- verify(persistentRepository).readDesktop(any(), any())
- assertThat(desktopRepository.getActiveTasks(DEFAULT_DISPLAY))
- .containsExactly(1, 2, 3)
+ // Desktop Repository currently returns all tasks across desktops for a specific user
+ // since the repository currently doesn't handle desktops. This test logic should be updated
+ // once the repository handles multiple desktops.
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getActiveTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(1, 3, 4, 5)
.inOrder()
- assertThat(desktopRepository.getExpandedTasksOrdered(DEFAULT_DISPLAY))
- .containsExactly(1, 2)
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
+ .containsExactly(5, 1)
.inOrder()
- assertThat(desktopRepository.getMinimizedTasks(DEFAULT_DISPLAY)).containsExactly(3)
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getMinimizedTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(3, 4)
+ .inOrder()
+
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_2)
+ .getActiveTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(7, 8)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_2)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
+ .contains(7)
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_2)
+ .getMinimizedTasks(DEFAULT_DISPLAY)
+ ).containsExactly(8)
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+ fun initWithPersistence_singleUser_addedCorrectly() =
+ runTest(StandardTestDispatcher()) {
+ whenever(persistentRepository.getUserDesktopRepositoryMap()).thenReturn(
+ mapOf(
+ USER_ID_1 to desktopRepositoryState1,
+ )
+ )
+ whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
+ .thenReturn(desktopRepositoryState1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1))
+ .thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2))
+ .thenReturn(desktop2)
+
+ repositoryInitializer.initialize(desktopUserRepositories)
+
+ // Desktop Repository currently returns all tasks across desktops for a specific user
+ // since the repository currently doesn't handle desktops. This test logic should be updated
+ // once the repository handles multiple desktops.
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getActiveTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(1, 3, 4, 5)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getExpandedTasksOrdered(DEFAULT_DISPLAY)
+ )
+ .containsExactly(5, 1)
+ .inOrder()
+ assertThat(
+ desktopUserRepositories.getProfile(USER_ID_1)
+ .getMinimizedTasks(DEFAULT_DISPLAY)
+ )
+ .containsExactly(3, 4)
+ .inOrder()
}
@After
fun tearDown() {
datastoreScope.cancel()
}
+
+ private companion object {
+ const val USER_ID_1 = 5
+ const val USER_ID_2 = 6
+ const val DESKTOP_ID_1 = 2
+ const val DESKTOP_ID_2 = 3
+ const val DESKTOP_ID_3 = 4
+
+ val freeformTasksInZOrder1 = listOf(1, 3)
+ val desktop1: Desktop = Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_1)
+ .addAllZOrderedTasks(freeformTasksInZOrder1)
+ .putTasksByTaskId(
+ 1,
+ DesktopTask.newBuilder()
+ .setTaskId(1)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build()
+ )
+ .putTasksByTaskId(
+ 3,
+ DesktopTask.newBuilder()
+ .setTaskId(3)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build()
+ )
+ .build()
+
+ val freeformTasksInZOrder2 = listOf(4, 5)
+ val desktop2: Desktop = Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_2)
+ .addAllZOrderedTasks(freeformTasksInZOrder2)
+ .putTasksByTaskId(
+ 4,
+ DesktopTask.newBuilder()
+ .setTaskId(4)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build()
+ )
+ .putTasksByTaskId(
+ 5,
+ DesktopTask.newBuilder()
+ .setTaskId(5)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build()
+ )
+ .build()
+
+ val freeformTasksInZOrder3 = listOf(7, 8)
+ val desktop3: Desktop = Desktop.newBuilder()
+ .setDesktopId(DESKTOP_ID_3)
+ .addAllZOrderedTasks(freeformTasksInZOrder3)
+ .putTasksByTaskId(
+ 7,
+ DesktopTask.newBuilder()
+ .setTaskId(7)
+ .setDesktopTaskState(DesktopTaskState.VISIBLE)
+ .build()
+ )
+ .putTasksByTaskId(
+ 8,
+ DesktopTask.newBuilder()
+ .setTaskId(8)
+ .setDesktopTaskState(DesktopTaskState.MINIMIZED)
+ .build()
+ )
+ .build()
+ val desktopRepositoryState1: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
+ .putDesktop(DESKTOP_ID_1, desktop1)
+ .putDesktop(DESKTOP_ID_2, desktop2)
+ .build()
+ val desktopRepositoryState2: DesktopRepositoryState = DesktopRepositoryState.newBuilder()
+ .putDesktop(DESKTOP_ID_3, desktop3)
+ .build()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index b504a88..b8629b2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -27,6 +27,7 @@
import static com.android.window.flags.Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,6 +48,7 @@
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -81,6 +83,8 @@
@Mock
private SurfaceControl mMockSurfaceControl;
@Mock
+ private DesktopUserRepositories mDesktopUserRepositories;
+ @Mock
private DesktopRepository mDesktopRepository;
@Mock
private DesktopTasksController mDesktopTasksController;
@@ -101,13 +105,14 @@
.mockStatic(DesktopModeStatus.class)
.startMocking();
doReturn(true).when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
-
+ when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
+ when(mDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
mFreeformTaskListener =
new FreeformTaskListener(
mContext,
mShellInit,
mTaskOrganizer,
- Optional.of(mDesktopRepository),
+ Optional.of(mDesktopUserRepositories),
Optional.of(mDesktopTasksController),
mLaunchAdjacentController,
mWindowDecorViewModel,
@@ -123,7 +128,8 @@
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
- verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible = true);
+ verify(mDesktopUserRepositories.getCurrent())
+ .addTask(task.displayId, task.taskId, task.isVisible = true);
}
@Test
@@ -135,7 +141,8 @@
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
- verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible);
+ verify(mDesktopUserRepositories.getCurrent())
+ .addTask(task.displayId, task.taskId, task.isVisible);
}
@Test
@@ -147,7 +154,8 @@
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
- verify(mDesktopRepository, never()).addTask(task.displayId, task.taskId, task.isVisible);
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .addTask(task.displayId, task.taskId, task.isVisible);
}
@Test
@@ -158,7 +166,8 @@
mFreeformTaskListener.onFocusTaskChanged(task);
- verify(mDesktopRepository).addTask(task.displayId, task.taskId, task.isVisible);
+ verify(mDesktopUserRepositories.getCurrent())
+ .addTask(task.displayId, task.taskId, task.isVisible);
}
@Test
@@ -171,7 +180,7 @@
mFreeformTaskListener.onFocusTaskChanged(fullscreenTask);
- verify(mDesktopRepository, never())
+ verify(mDesktopUserRepositories.getCurrent(), never())
.addTask(fullscreenTask.displayId, fullscreenTask.taskId, fullscreenTask.isVisible);
}
@@ -203,10 +212,11 @@
@Test
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
@DisableFlags(FLAG_ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS)
- public void onTaskVanished_nonClosingTask_noTransitionObservers_isMinimized() {
+ public void onTaskVanished_minimizedTask_noTransitionObservers_isNotRemoved() {
ActivityManager.RunningTaskInfo task =
new TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build();
task.isVisible = true;
+ when(mDesktopRepository.isMinimizedTask(task.taskId)).thenReturn(true);
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
@@ -214,7 +224,8 @@
task.displayId = INVALID_DISPLAY;
mFreeformTaskListener.onTaskVanished(task);
- verify(mDesktopRepository).minimizeTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never()).removeFreeformTask(task.displayId,
+ task.taskId);
}
@Test
@@ -227,14 +238,17 @@
mFreeformTaskListener.onTaskAppeared(task, mMockSurfaceControl);
- when(mDesktopRepository.isClosingTask(task.taskId)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent()
+ .isClosingTask(task.taskId)).thenReturn(true);
task.isVisible = false;
task.displayId = INVALID_DISPLAY;
mFreeformTaskListener.onTaskVanished(task);
- verify(mDesktopRepository, never()).minimizeTask(task.displayId, task.taskId);
- verify(mDesktopRepository).removeClosingTask(task.taskId);
- verify(mDesktopRepository).removeFreeformTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .minimizeTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent()).removeClosingTask(task.taskId);
+ verify(mDesktopUserRepositories.getCurrent())
+ .removeFreeformTask(task.displayId, task.taskId);
}
@Test
@@ -246,9 +260,12 @@
mFreeformTaskListener.onTaskVanished(task);
- verify(mDesktopRepository, never()).minimizeTask(task.displayId, task.taskId);
- verify(mDesktopRepository, never()).removeClosingTask(task.taskId);
- verify(mDesktopRepository, never()).removeFreeformTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .minimizeTask(task.displayId, task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .removeClosingTask(task.taskId);
+ verify(mDesktopUserRepositories.getCurrent(), never())
+ .removeFreeformTask(task.displayId, task.taskId);
}
@Test
@@ -274,7 +291,8 @@
mFreeformTaskListener.onTaskInfoChanged(task);
verify(mTaskChangeListener, never()).onTaskChanging(any());
- verify(mDesktopRepository).updateTask(task.displayId, task.taskId, task.isVisible);
+ verify(mDesktopUserRepositories.getCurrent())
+ .updateTask(task.displayId, task.taskId, task.isVisible);
}
@Test
@@ -289,7 +307,7 @@
mFreeformTaskListener.onTaskInfoChanged(task);
verify(mTaskChangeListener).onNonTransitionTaskChanging(any());
- verify(mDesktopRepository, never())
+ verify(mDesktopUserRepositories.getCurrent(), never())
.updateTask(task.displayId, task.taskId, task.isVisible);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 289fd2d..2eb2c3b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -65,7 +65,7 @@
import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -103,7 +103,7 @@
@Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper;
@Mock private PipUiEventLogger mMockPipUiEventLogger;
@Mock private Optional<SplitScreenController> mMockOptionalSplitScreen;
- @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository;
+ @Mock private Optional<DesktopUserRepositories> mMockOptionalDesktopUserRepositories;
@Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
@Mock private PipParamsChangedForwarder mMockPipParamsChangedForwarder;
@@ -136,7 +136,7 @@
mMockPipSurfaceTransactionHelper, mMockPipTransitionController,
mMockPipParamsChangedForwarder, mMockOptionalSplitScreen,
Optional.empty() /* pipPerfHintControllerOptional */,
- mMockOptionalDesktopRepository, mRootTaskDisplayAreaOrganizer,
+ mMockOptionalDesktopUserRepositories, mRootTaskDisplayAreaOrganizer,
mMockDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer,
mMainExecutor);
mMainExecutor.flushAll();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index 57b6d7f..3fe8c10 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -45,7 +45,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
-import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -83,7 +83,7 @@
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
@Mock private SurfaceControl.Transaction mMockTransaction;
@Mock private PipAlphaAnimator mMockAlphaAnimator;
- @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository;
+ @Mock private Optional<DesktopUserRepositories> mMockOptionalDesktopUserRepositories;
@Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@@ -102,7 +102,7 @@
.thenReturn(mMockTransaction);
mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
- mMockPipTransitionState, mMockOptionalDesktopRepository,
+ mMockPipTransitionState, mMockOptionalDesktopUserRepositories,
mRootTaskDisplayAreaOrganizer);
mPipScheduler.setPipTransitionController(mMockPipTransitionController);
mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 68c8aab..95f371f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -26,6 +26,8 @@
import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -57,6 +59,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -74,6 +77,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.shared.GroupedTaskInfo;
import com.android.wm.shell.shared.ShellSharedConstants;
@@ -113,8 +117,6 @@
@Mock
private ShellCommandHandler mShellCommandHandler;
@Mock
- private DesktopRepository mDesktopRepository;
- @Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
private DisplayInsetsController mDisplayInsetsController;
@@ -122,6 +124,10 @@
private IRecentTasksListener mRecentTasksListener;
@Mock
private TaskStackTransitionObserver mTaskStackTransitionObserver;
+ @Mock
+ private DesktopUserRepositories mDesktopUserRepositories;
+ @Mock
+ private DesktopRepository mDesktopRepository;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -142,6 +148,8 @@
.when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
mMainExecutor = new TestShellExecutor();
+ when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
+ when(mDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
when(mContext.getSystemService(KeyguardManager.class))
.thenReturn(mock(KeyguardManager.class));
@@ -150,7 +158,7 @@
mDisplayInsetsController, mMainExecutor));
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
- Optional.of(mDesktopRepository), mTaskStackTransitionObserver,
+ Optional.of(mDesktopUserRepositories), mTaskStackTransitionObserver,
mMainExecutor);
mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
@@ -182,6 +190,12 @@
}
@Test
+ public void instantiateController_initializesRepository() {
+ verify(mDesktopUserRepositories, times(1)).getCurrent();
+ verify(mDesktopRepository, times(1)).addActiveTaskListener(any());
+ }
+
+ @Test
public void testInvalidateExternalInterface_unregistersListener() {
// Note: We have to use the real instance of the controller here since that is the instance
// that is passed to ShellController internally, and the instance that the listener will be
@@ -323,8 +337,8 @@
RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
- when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
- when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(1)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(3)).thenReturn(true);
ArrayList<GroupedTaskInfo> recentTasks =
mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -362,8 +376,8 @@
new SplitBounds(new Rect(), new Rect(), 1, 2, SNAP_TO_2_50_50);
mRecentTasksController.addSplitPair(t1.taskId, t2.taskId, pair1Bounds);
- when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
- when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(3)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(5)).thenReturn(true);
ArrayList<GroupedTaskInfo> recentTasks =
mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -402,8 +416,8 @@
RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
- when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
- when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(1)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(3)).thenReturn(true);
ArrayList<GroupedTaskInfo> recentTasks =
mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -431,7 +445,9 @@
setRawList(t1, t2, t3, t4, t5);
when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
+ when(mDesktopRepository.isActiveTask(2)).thenReturn(false);
when(mDesktopRepository.isActiveTask(3)).thenReturn(true);
+ when(mDesktopRepository.isActiveTask(4)).thenReturn(false);
when(mDesktopRepository.isActiveTask(5)).thenReturn(true);
when(mDesktopRepository.isMinimizedTask(3)).thenReturn(true);
@@ -470,8 +486,8 @@
t2.lastNonFullscreenBounds = new Rect(150, 250, 350, 450);
setRawList(t1, t2);
- when(mDesktopRepository.isActiveTask(1)).thenReturn(true);
- when(mDesktopRepository.isActiveTask(2)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(1)).thenReturn(true);
+ when(mDesktopUserRepositories.getCurrent().isActiveTask(2)).thenReturn(true);
ArrayList<GroupedTaskInfo> recentTasks =
mRecentTasksController.getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index f0f5fe1..894d238 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -65,6 +65,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
@@ -100,7 +101,7 @@
@Mock
private ShellCommandHandler mShellCommandHandler;
@Mock
- private DesktopRepository mDesktopRepository;
+ private DesktopUserRepositories mDesktopUserRepositories;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
@@ -112,6 +113,8 @@
@Mock
private Transitions mTransitions;
+ @Mock private DesktopRepository mDesktopRepository;
+
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -131,6 +134,7 @@
ExtendedMockito.doReturn(true)
.when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ when(mDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
when(mContext.getSystemService(KeyguardManager.class))
@@ -140,7 +144,7 @@
mDisplayInsetsController, mMainExecutor));
mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
- Optional.of(mDesktopRepository), mTaskStackTransitionObserver,
+ Optional.of(mDesktopUserRepositories), mTaskStackTransitionObserver,
mMainExecutor);
mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 72a7a3f..7726c97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -71,6 +71,7 @@
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.recents.RecentTasksController;
@@ -119,6 +120,7 @@
@Mock WindowDecorViewModel mWindowDecorViewModel;
@Mock DesktopTasksController mDesktopTasksController;
@Mock MultiInstanceHelper mMultiInstanceHelper;
+ @Mock SplitState mSplitState;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -136,7 +138,7 @@
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
- mStageCoordinator, mMultiInstanceHelper, mMainExecutor, mMainHandler));
+ mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index d13a888..1a2d60d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -35,6 +35,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.transition.Transitions;
@@ -80,11 +81,11 @@
ShellExecutor mainExecutor, Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
+ Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState) {
super(context, displayId, syncQueue, taskOrganizer, mainStage,
sideStage, displayController, imeController, insetsController, splitLayout,
transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
- launchAdjacentController, windowDecorViewModel);
+ launchAdjacentController, windowDecorViewModel, splitState);
// Prepare root task for testing.
mRootTask = new TestRunningTaskInfoBuilder().build();
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 e32cf38..de77837 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
@@ -78,6 +78,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.TestRemoteTransition;
@@ -108,6 +109,7 @@
@Mock private Transitions mTransitions;
@Mock private IconProvider mIconProvider;
@Mock private WindowDecorViewModel mWindowDecorViewModel;
+ @Mock private SplitState mSplitState;
@Mock private ShellExecutor mMainExecutor;
@Mock private Handler mMainHandler;
@Mock private LaunchAdjacentController mLaunchAdjacentController;
@@ -144,7 +146,7 @@
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
- mLaunchAdjacentController, Optional.empty());
+ mLaunchAdjacentController, Optional.empty(), mSplitState);
mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 1e739cd..7afcce1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -71,6 +71,7 @@
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.common.split.SplitState;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.splitscreen.SplitScreen.SplitScreenListener;
import com.android.wm.shell.sysui.ShellController;
@@ -116,6 +117,8 @@
private LaunchAdjacentController mLaunchAdjacentController;
@Mock
private DefaultMixedHandler mDefaultMixedHandler;
+ @Mock
+ private SplitState mSplitState;
private final Rect mBounds1 = new Rect(10, 20, 30, 40);
private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -139,7 +142,7 @@
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
- Optional.empty()));
+ Optional.empty(), mSplitState));
mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
index 1215c52..4403558 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
@@ -29,12 +29,13 @@
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,17 +56,18 @@
@Rule
val setFlagsRule: SetFlagsRule = SetFlagsRule()
- private lateinit var desktopRepository: DesktopRepository
+ private lateinit var userRepositories: DesktopUserRepositories
private lateinit var menu: DesktopHeaderManageWindowsMenu
@Before
fun setUp() {
- desktopRepository = DesktopRepository(
+ userRepositories = DesktopUserRepositories(
context = context,
shellInit = ShellInit(TestShellExecutor()),
persistentRepository = mock(),
repositoryInitializer = mock(),
- mainCoroutineScope = mock()
+ mainCoroutineScope = mock(),
+ userManager = mock(),
)
}
@@ -75,15 +77,15 @@
}
@Test
+ @Ignore("Test is failing internally")
@EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
fun testShow_forImmersiveTask_usesSystemViewContainer() {
val task = createFreeformTask()
- desktopRepository.setTaskInFullImmersiveState(
+ userRepositories.getProfile(DEFAULT_USER_ID).setTaskInFullImmersiveState(
displayId = task.displayId,
taskId = task.taskId,
immersive = true
)
-
menu = createMenu(task)
assertThat(menu.menuViewContainer).isInstanceOf(AdditionalSystemViewContainer::class.java)
@@ -96,7 +98,7 @@
displayController = mock(),
rootTdaOrganizer = mock(),
context = context,
- desktopRepository = desktopRepository,
+ desktopUserRepositories = userRepositories,
surfaceControlBuilderSupplier = { SurfaceControl.Builder() },
surfaceControlTransactionSupplier = { SurfaceControl.Transaction() },
snapshotList = emptyList(),
@@ -109,4 +111,8 @@
.setActivityType(ACTIVITY_TYPE_STANDARD)
.setWindowingMode(WINDOWING_MODE_FREEFORM)
.build()
+
+ private companion object {
+ const val DEFAULT_USER_ID = 10
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 153be07..a4e3af4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -59,6 +59,7 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger
@@ -398,11 +399,12 @@
maxOrRestoreListenerCaptor.value.invoke()
verify(mockDesktopTasksController).toggleDesktopTaskSize(
- eq(decor.mTaskInfo),
- eq(ResizeTrigger.MAXIMIZE_MENU),
- eq(InputMethod.UNKNOWN_INPUT_METHOD),
- any(),
- any()
+ decor.mTaskInfo,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.MAXIMIZE_MENU_TO_MAXIMIZE,
+ InputMethod.UNKNOWN_INPUT_METHOD
+ )
)
}
@@ -1061,11 +1063,12 @@
verify(mockDesktopTasksController)
.toggleDesktopTaskSize(
- eq(decor.mTaskInfo),
- eq(ResizeTrigger.MAXIMIZE_BUTTON),
- eq(InputMethod.UNKNOWN_INPUT_METHOD),
- any(),
- any(),
+ decor.mTaskInfo,
+ ToggleTaskSizeInteraction(
+ ToggleTaskSizeInteraction.Direction.MAXIMIZE,
+ ToggleTaskSizeInteraction.Source.HEADER_BUTTON_TO_MAXIMIZE,
+ InputMethod.UNKNOWN_INPUT_METHOD
+ )
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 080f496..afd4607 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -60,6 +60,7 @@
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTasksLimiter
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository
import com.android.wm.shell.desktopmode.education.AppHandleEducationController
import com.android.wm.shell.desktopmode.education.AppToWebEducationController
@@ -108,7 +109,7 @@
protected val mockTaskOrganizer = mock<ShellTaskOrganizer>()
protected val mockDisplayController = mock<DisplayController>()
protected val mockSplitScreenController = mock<SplitScreenController>()
- protected val mockDesktopRepository = mock<DesktopRepository>()
+ protected val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
protected val mockDisplayLayout = mock<DisplayLayout>()
protected val displayInsetsController = mock<DisplayInsetsController>()
protected val mockSyncQueue = mock<SyncTransactionQueue>()
@@ -142,6 +143,7 @@
protected val mockAppToWebEducationController = mock<AppToWebEducationController>()
protected val mockFocusTransitionObserver = mock<FocusTransitionObserver>()
protected val mockCaptionHandleRepository = mock<WindowDecorCaptionHandleRepository>()
+ protected val mockDesktopRepository: DesktopRepository = mock<DesktopRepository>()
protected val motionEvent = mock<MotionEvent>()
val displayController = mock<DisplayController>()
val displayLayout = mock<DisplayLayout>()
@@ -168,6 +170,9 @@
windowDecorByTaskIdSpy.clear()
spyContext.addMockSystemService(InputManager::class.java, mockInputManager)
desktopModeEventLogger = mock<DesktopModeEventLogger>()
+ whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
+ whenever(mockDesktopUserRepositories.getProfile(anyInt()))
+ .thenReturn(mockDesktopRepository)
desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel(
spyContext,
testShellExecutor,
@@ -178,7 +183,7 @@
mockShellCommandHandler,
mockWindowManager,
mockTaskOrganizer,
- mockDesktopRepository,
+ mockDesktopUserRepositories,
mockDisplayController,
mockShellController,
displayInsetsController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 03c7c98..61f3755 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -109,6 +109,7 @@
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -165,7 +166,7 @@
@Mock
private ShellTaskOrganizer mMockShellTaskOrganizer;
@Mock
- private DesktopRepository mMockDesktopRepository;
+ private DesktopUserRepositories mMockDesktopUserRepositories;
@Mock
private Choreographer mMockChoreographer;
@Mock
@@ -214,6 +215,8 @@
private WindowDecorCaptionHandleRepository mMockCaptionHandleRepository;
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
+ @Mock
+ private DesktopRepository mDesktopRepository;
@Captor
private ArgumentCaptor<Function1<Boolean, Unit>> mOnMaxMenuHoverChangeListener;
@Captor
@@ -270,6 +273,8 @@
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any())).thenReturn(false);
when(mMockAppHeaderViewHolderFactory.create(any(), any(), any(), any(), any(), any(), any(),
any())).thenReturn(mMockAppHeaderViewHolder);
+ when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
+ when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
}
@After
@@ -1469,8 +1474,8 @@
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
true /* relayout */);
- when(mMockDesktopRepository.isTaskInFullImmersiveState(taskInfo.taskId))
- .thenReturn(true);
+ when(mMockDesktopUserRepositories.getCurrent()
+ .isTaskInFullImmersiveState(taskInfo.taskId)).thenReturn(true);
createHandleMenu(decoration);
@@ -1491,7 +1496,7 @@
@Test
@DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION,
- Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION})
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION_INTEGRATION})
public void notifyCaptionStateChanged_flagDisabled_doNoNotify() {
when(DesktopModeStatus.canEnterDesktopMode(mContext)).thenReturn(true);
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
@@ -1705,8 +1710,8 @@
boolean relayout) {
final DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext,
mContext, mMockDisplayController, mMockSplitScreenController,
- mMockDesktopRepository, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
- mMockHandler, mBgExecutor, mMockChoreographer, mMockSyncQueue,
+ mMockDesktopUserRepositories, mMockShellTaskOrganizer, taskInfo,
+ mMockSurfaceControl, mMockHandler, mBgExecutor, mMockChoreographer, mMockSyncQueue,
mMockAppHeaderViewHolderFactory, mMockRootTaskDisplayAreaOrganizer,
mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
new file mode 100644
index 0000000..2f223de
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/common/viewhost/DefaultWindowDecorViewHostTest.kt
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor.common.viewhost
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for [DefaultWindowDecorViewHost].
+ *
+ * Build/Install/Run: atest WMShellUnitTests:DefaultWindowDecorViewHostTest
+ */
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class DefaultWindowDecorViewHostTest : ShellTestCase() {
+
+ @Test
+ fun updateView_layoutInViewHost() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
+ }
+
+ @Test
+ fun updateView_alreadyLaidOut_relayouts() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ val otherParams = WindowManager.LayoutParams(200, 200)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = otherParams,
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(view)
+ assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
+ .isEqualTo(otherParams.width)
+ }
+
+ @Test
+ fun updateView_replacingView_throws() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ val otherView = View(context)
+ assertThrows(Exception::class.java) {
+ windowDecorViewHost.updateView(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+ }
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateView_clearsPendingAsyncJob() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val asyncView = View(context)
+ val syncView = View(context)
+ val asyncAttrs = WindowManager.LayoutParams(100, 100)
+ val syncAttrs = WindowManager.LayoutParams(200, 200)
+
+ windowDecorViewHost.updateViewAsync(
+ view = asyncView,
+ attrs = asyncAttrs,
+ configuration = context.resources.configuration,
+ )
+
+ // No view host yet, since the coroutine hasn't run.
+ assertThat(windowDecorViewHost.viewHost).isNull()
+
+ windowDecorViewHost.updateView(
+ view = syncView,
+ attrs = syncAttrs,
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ // Would run coroutine if it hadn't been cancelled.
+ advanceUntilIdle()
+
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
+ // View host view/attrs should match the ones from the sync call, plus, since the
+ // sync/async were made with different views, if the job hadn't been cancelled there
+ // would've been an exception thrown as replacing views isn't allowed.
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(syncView)
+ assertThat(windowDecorViewHost.viewHost!!.view!!.layoutParams.width)
+ .isEqualTo(syncAttrs.width)
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+ val view = View(context)
+ val attrs = WindowManager.LayoutParams(100, 100)
+
+ windowDecorViewHost.updateViewAsync(
+ view = view,
+ attrs = attrs,
+ configuration = context.resources.configuration,
+ )
+
+ assertThat(windowDecorViewHost.viewHost).isNull()
+
+ advanceUntilIdle()
+
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun updateViewAsync_clearsPendingAsyncJob() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+
+ val view = View(context)
+ windowDecorViewHost.updateViewAsync(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+ val otherView = View(context)
+ windowDecorViewHost.updateViewAsync(
+ view = otherView,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ )
+
+ advanceUntilIdle()
+
+ assertThat(windowDecorViewHost.viewHost).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isNotNull()
+ assertThat(windowDecorViewHost.viewHost!!.view).isEqualTo(otherView)
+ }
+
+ @Test
+ fun release() = runTest {
+ val windowDecorViewHost = createDefaultViewHost()
+
+ val view = View(context)
+ windowDecorViewHost.updateView(
+ view = view,
+ attrs = WindowManager.LayoutParams(100, 100),
+ configuration = context.resources.configuration,
+ onDrawTransaction = null,
+ )
+
+ val t = mock(SurfaceControl.Transaction::class.java)
+ windowDecorViewHost.release(t)
+
+ verify(windowDecorViewHost.viewHost!!).release()
+ verify(t).remove(windowDecorViewHost.surfaceControl)
+ }
+
+ private fun CoroutineScope.createDefaultViewHost() =
+ DefaultWindowDecorViewHost(
+ context = context,
+ mainScope = this,
+ display = context.display,
+ surfaceControlViewHostFactory = { c, d, wwm, s ->
+ spy(SurfaceControlViewHost(c, d, wwm, s))
+ },
+ )
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index d290021..193c2c2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -25,7 +25,7 @@
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
-import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
@@ -52,7 +52,7 @@
private val syncQueueMock: SyncTransactionQueue = mock()
private val transitionsMock: Transitions = mock()
private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
- private val desktopRepository: DesktopRepository = mock()
+ private val userRepositories: DesktopUserRepositories = mock()
private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val toggleResizeDesktopTaskTransitionHandlerMock:
ToggleResizeDesktopTaskTransitionHandler =
@@ -75,7 +75,7 @@
shellTaskOrganizerMock,
toggleResizeDesktopTaskTransitionHandlerMock,
returnToDragStartAnimatorMock,
- desktopRepository,
+ userRepositories,
desktopModeEventLogger,
)
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(contextMock)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index 3b39f1e..95e2151 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -39,6 +39,7 @@
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
import com.android.wm.shell.transition.Transitions
@@ -93,10 +94,11 @@
private val transition: IBinder = mock()
private val info: TransitionInfo = mock()
private val finishCallback: Transitions.TransitionFinishCallback = mock()
- private val desktopRepository: DesktopRepository = mock()
+ private val userRepositories: DesktopUserRepositories = mock()
private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val desktopTilingDividerWindowManager: DesktopTilingDividerWindowManager = mock()
private val motionEvent: MotionEvent = mock()
+ private val desktopRepository: DesktopRepository = mock()
private lateinit var tilingDecoration: DesktopTilingWindowDecoration
private val split_divider_width = 10
@@ -116,10 +118,11 @@
shellTaskOrganizer,
toggleResizeDesktopTaskTransitionHandler,
returnToDragStartAnimator,
- desktopRepository,
+ userRepositories,
desktopModeEventLogger,
)
whenever(context.createContextAsUser(any(), any())).thenReturn(context)
+ whenever(userRepositories.current).thenReturn(desktopRepository)
}
@Test
@@ -275,8 +278,8 @@
}
whenever(context.resources).thenReturn(resources)
whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
- whenever(desktopRepository.isVisibleTask(eq(task1.taskId))).thenReturn(true)
- whenever(desktopRepository.isVisibleTask(eq(task2.taskId))).thenReturn(true)
+ whenever(userRepositories.current.isVisibleTask(eq(task1.taskId))).thenReturn(true)
+ whenever(userRepositories.current.isVisibleTask(eq(task2.taskId))).thenReturn(true)
tilingDecoration.onAppTiled(
task1,
@@ -308,7 +311,7 @@
whenever(context.resources).thenReturn(resources)
whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock)
- whenever(desktopRepository.isVisibleTask(any())).thenReturn(true)
+ whenever(userRepositories.current.isVisibleTask(any())).thenReturn(true)
tilingDecoration.onAppTiled(
task1,
desktopWindowDecoration,
@@ -341,7 +344,7 @@
whenever(context.resources).thenReturn(resources)
whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
whenever(desktopWindowDecoration.getLeash()).thenReturn(surfaceControlMock)
- whenever(desktopRepository.isVisibleTask(any())).thenReturn(true)
+ whenever(userRepositories.current.isVisibleTask(any())).thenReturn(true)
tilingDecoration.onAppTiled(
task1,
desktopWindowDecoration,
@@ -614,7 +617,7 @@
private fun createVisibleTask() =
createFreeformTask().also {
- whenever(desktopRepository.isVisibleTask(eq(it.taskId))).thenReturn(true)
+ whenever(userRepositories.current.isVisibleTask(eq(it.taskId))).thenReturn(true)
}
companion object {
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index e5fb755..7b45070 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -48,7 +48,14 @@
minikinPaint.localeListId = paint->getMinikinLocaleListId();
minikinPaint.fontStyle = resolvedFace->fStyle;
minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
- minikinPaint.fontVariationSettings = paint->getFontVariationOverride();
+ if (!resolvedFace->fIsVariationInstance) {
+ // This is an optimization for direct private API use typically done by System UI.
+ // In the public API surface, if Typeface is already configured for variation instance
+ // (Target SDK <= 35) the font variation settings of Paint is not set.
+ // On the other hand, if Typeface is not configured so (Target SDK >= 36), the font
+ // variation settings are configured dynamically.
+ minikinPaint.fontVariationSettings = paint->getFontVariationOverride();
+ }
minikinPaint.verticalText = paint->isVerticalText();
const std::optional<minikin::FamilyVariant>& familyVariant = paint->getFamilyVariant();
diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java
index 1ecba31..3efb5f9 100644
--- a/media/java/android/media/MediaCas.java
+++ b/media/java/android/media/MediaCas.java
@@ -1010,17 +1010,17 @@
* scenario, when both resource holder and resource challenger have same processId and same
* priority.
*
- * @param resourceHolderRetain Set to {@code true} to allow the resource holder to retain
- * ownership, or false to allow the resource challenger to acquire the resource.
- * If not explicitly set, resourceHolderRetain is set to {@code false}.
+ *@param enabled Set to {@code true} to allow the resource holder to retain ownership,
+ * or false to allow the resource challenger to acquire the resource.
+ * If not explicitly set, enabled is set to {@code false}.
* @hide
*/
@FlaggedApi(FLAG_SET_RESOURCE_HOLDER_RETAIN)
@SystemApi
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
- public void setResourceHolderRetain(boolean resourceHolderRetain) {
+ public void setResourceOwnershipRetention(boolean enabled) {
if (mTunerResourceManager != null) {
- mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+ mTunerResourceManager.setResourceOwnershipRetention(mClientId, enabled);
}
}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index d433ec87..dd5067a 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -24,6 +24,7 @@
import static com.android.media.flags.Flags.FLAG_ENABLE_MIRRORING_IN_MEDIA_ROUTER_2;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_MEDIA_ROUTE_2_INFO_TYPES;
import static com.android.media.flags.Flags.FLAG_ENABLE_NEW_WIRED_MEDIA_ROUTE_2_INFO_TYPES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -616,6 +617,7 @@
private final String mProviderId;
private final boolean mIsVisibilityRestricted;
private final Set<String> mAllowedPackages;
+ private final Set<String> mRequiredPermissions;
@SuitabilityStatus private final int mSuitabilityStatus;
MediaRoute2Info(@NonNull Builder builder) {
@@ -640,6 +642,7 @@
mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
mAllowedPackages = builder.mAllowedPackages;
mSuitabilityStatus = builder.mSuitabilityStatus;
+ mRequiredPermissions = Set.copyOf(builder.mRequiredPermissions);
}
MediaRoute2Info(@NonNull Parcel in) {
@@ -664,6 +667,7 @@
mProviderId = in.readString();
mIsVisibilityRestricted = in.readBoolean();
mAllowedPackages = Set.of(in.createString8Array());
+ mRequiredPermissions = Set.of(in.createString8Array());
mSuitabilityStatus = in.readInt();
}
@@ -907,6 +911,15 @@
}
/**
+ * @return the set of permissions which must be held to see this route
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Set<String> getRequiredPermissions() {
+ return mRequiredPermissions;
+ }
+
+ /**
* Returns whether this route's type can only be published by the system route provider.
*
* @see #isSystemRoute()
@@ -977,6 +990,7 @@
pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
pw.println(indent + "mSuitabilityStatus=" + mSuitabilityStatus);
+ pw.println(indent + "mRequiredPermissions=" + mRequiredPermissions);
}
private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -1013,6 +1027,7 @@
&& Objects.equals(mProviderId, other.mProviderId)
&& (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
&& Objects.equals(mAllowedPackages, other.mAllowedPackages)
+ && Objects.equals(mRequiredPermissions, other.mRequiredPermissions)
&& mSuitabilityStatus == other.mSuitabilityStatus;
}
@@ -1039,6 +1054,7 @@
mProviderId,
mIsVisibilityRestricted,
mAllowedPackages,
+ mRequiredPermissions,
mSuitabilityStatus);
}
@@ -1079,6 +1095,8 @@
.append(mIsVisibilityRestricted)
.append(", allowedPackages=")
.append(String.join(",", mAllowedPackages))
+ .append(", mRequiredPermissions=")
+ .append(String.join(",", mRequiredPermissions))
.append(", suitabilityStatus=")
.append(mSuitabilityStatus)
.append(" }")
@@ -1112,6 +1130,7 @@
dest.writeString(mProviderId);
dest.writeBoolean(mIsVisibilityRestricted);
dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
+ dest.writeString8Array(mRequiredPermissions.toArray(new String[0]));
dest.writeInt(mSuitabilityStatus);
}
@@ -1260,6 +1279,7 @@
private String mProviderId;
private boolean mIsVisibilityRestricted;
private Set<String> mAllowedPackages;
+ private Set<String> mRequiredPermissions;
@SuitabilityStatus private int mSuitabilityStatus;
/**
@@ -1285,6 +1305,7 @@
mDeduplicationIds = Set.of();
mAllowedPackages = Set.of();
mSuitabilityStatus = SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER;
+ mRequiredPermissions = Set.of();
}
/**
@@ -1334,6 +1355,7 @@
mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
mAllowedPackages = routeInfo.mAllowedPackages;
mSuitabilityStatus = routeInfo.mSuitabilityStatus;
+ mRequiredPermissions = routeInfo.mRequiredPermissions;
}
/**
@@ -1565,6 +1587,7 @@
public Builder setVisibilityPublic() {
mIsVisibilityRestricted = false;
mAllowedPackages = Set.of();
+ mRequiredPermissions = Set.of();
return this;
}
@@ -1589,6 +1612,19 @@
}
/**
+ * Limits the visibility of this route to holders of a set of permissions.
+ *
+ * @param requiredPermissions the list of all permissions which must be held in order to
+ * see this route.
+ */
+ @NonNull
+ @FlaggedApi(FLAG_ENABLE_ROUTE_VISIBILITY_CONTROL_API)
+ public Builder setRequiredPermissions(@NonNull Set<String> requiredPermissions) {
+ mRequiredPermissions = Set.copyOf(requiredPermissions);
+ return this;
+ }
+
+ /**
* Sets route suitability status.
*
* <p>The default value is {@link
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index b7e75b7..dc3fbf6 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -30,42 +30,42 @@
* @hide
*/
interface IMediaQualityManager {
- PictureProfile createPictureProfile(in PictureProfile pp);
- void updatePictureProfile(in String id, in PictureProfile pp);
- void removePictureProfile(in String id);
- PictureProfile getPictureProfile(in int type, in String name);
- List<PictureProfile> getPictureProfilesByPackage(in String packageName);
- List<PictureProfile> getAvailablePictureProfiles();
- List<String> getPictureProfilePackageNames();
- List<String> getPictureProfileAllowList();
- void setPictureProfileAllowList(in List<String> packages);
- PictureProfileHandle getPictureProfileHandle(in String id);
+ PictureProfile createPictureProfile(in PictureProfile pp, int userId);
+ void updatePictureProfile(in String id, in PictureProfile pp, int userId);
+ void removePictureProfile(in String id, int userId);
+ PictureProfile getPictureProfile(in int type, in String name, int userId);
+ List<PictureProfile> getPictureProfilesByPackage(in String packageName, int userId);
+ List<PictureProfile> getAvailablePictureProfiles(int userId);
+ List<String> getPictureProfilePackageNames(int userId);
+ List<String> getPictureProfileAllowList(int userId);
+ void setPictureProfileAllowList(in List<String> packages, int userId);
+ PictureProfileHandle getPictureProfileHandle(in String id, int userId);
- SoundProfile createSoundProfile(in SoundProfile pp);
- void updateSoundProfile(in String id, in SoundProfile pp);
- void removeSoundProfile(in String id);
- SoundProfile getSoundProfile(in int type, in String name);
- List<SoundProfile> getSoundProfilesByPackage(in String packageName);
- List<SoundProfile> getAvailableSoundProfiles();
- List<String> getSoundProfilePackageNames();
- List<String> getSoundProfileAllowList();
- void setSoundProfileAllowList(in List<String> packages);
+ SoundProfile createSoundProfile(in SoundProfile pp, int userId);
+ void updateSoundProfile(in String id, in SoundProfile pp, int userId);
+ void removeSoundProfile(in String id, int userId);
+ SoundProfile getSoundProfile(in int type, in String name, int userId);
+ List<SoundProfile> getSoundProfilesByPackage(in String packageName, int userId);
+ List<SoundProfile> getAvailableSoundProfiles(int userId);
+ List<String> getSoundProfilePackageNames(int userId);
+ List<String> getSoundProfileAllowList(int userId);
+ void setSoundProfileAllowList(in List<String> packages, int userId);
void registerPictureProfileCallback(in IPictureProfileCallback cb);
void registerSoundProfileCallback(in ISoundProfileCallback cb);
void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
- List<ParamCapability> getParamCapabilities(in List<String> names);
+ List<ParamCapability> getParamCapabilities(in List<String> names, int userId);
- boolean isSupported();
- void setAutoPictureQualityEnabled(in boolean enabled);
- boolean isAutoPictureQualityEnabled();
- void setSuperResolutionEnabled(in boolean enabled);
- boolean isSuperResolutionEnabled();
- void setAutoSoundQualityEnabled(in boolean enabled);
- boolean isAutoSoundQualityEnabled();
+ boolean isSupported(int userId);
+ void setAutoPictureQualityEnabled(in boolean enabled, int userId);
+ boolean isAutoPictureQualityEnabled(int userId);
+ void setSuperResolutionEnabled(in boolean enabled, int userId);
+ boolean isSuperResolutionEnabled(int userId);
+ void setAutoSoundQualityEnabled(in boolean enabled, int userId);
+ boolean isAutoSoundQualityEnabled(int userId);
- void setAmbientBacklightSettings(in AmbientBacklightSettings settings);
- void setAmbientBacklightEnabled(in boolean enabled);
- boolean isAmbientBacklightEnabled();
+ void setAmbientBacklightSettings(in AmbientBacklightSettings settings, int userId);
+ void setAmbientBacklightEnabled(in boolean enabled, int userId);
+ boolean isAmbientBacklightEnabled(int userId);
}
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 5005597..d4de99a 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -47,6 +47,7 @@
private final IMediaQualityManager mService;
private final Context mContext;
+ private final int mUserId;
private final Object mLock = new Object();
// @GuardedBy("mLock")
private final List<PictureProfileCallbackRecord> mPpCallbackRecords = new ArrayList<>();
@@ -61,6 +62,7 @@
*/
public MediaQualityManager(Context context, IMediaQualityManager service) {
mContext = context;
+ mUserId = context.getUserId();
mService = service;
IPictureProfileCallback ppCallback = new IPictureProfileCallback.Stub() {
@Override
@@ -219,7 +221,7 @@
public PictureProfile getPictureProfile(
@PictureProfile.ProfileType int type, @NonNull String name) {
try {
- return mService.getPictureProfile(type, name);
+ return mService.getPictureProfile(type, name, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -236,7 +238,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<PictureProfile> getPictureProfilesByPackage(@NonNull String packageName) {
try {
- return mService.getPictureProfilesByPackage(packageName);
+ return mService.getPictureProfilesByPackage(packageName, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -248,7 +250,7 @@
@NonNull
public List<PictureProfile> getAvailablePictureProfiles() {
try {
- return mService.getAvailablePictureProfiles();
+ return mService.getAvailablePictureProfiles(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -265,7 +267,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public List<String> getPictureProfilePackageNames() {
try {
- return mService.getPictureProfilePackageNames();
+ return mService.getPictureProfilePackageNames(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -277,7 +279,7 @@
*/
public PictureProfileHandle getPictureProfileHandle(String id) {
try {
- return mService.getPictureProfileHandle(id);
+ return mService.getPictureProfileHandle(id, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -291,7 +293,7 @@
*/
public void createPictureProfile(@NonNull PictureProfile pp) {
try {
- mService.createPictureProfile(pp);
+ mService.createPictureProfile(pp, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -303,7 +305,7 @@
*/
public void updatePictureProfile(@NonNull String profileId, @NonNull PictureProfile pp) {
try {
- mService.updatePictureProfile(profileId, pp);
+ mService.updatePictureProfile(profileId, pp, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -315,7 +317,7 @@
*/
public void removePictureProfile(@NonNull String profileId) {
try {
- mService.removePictureProfile(profileId);
+ mService.removePictureProfile(profileId, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -362,7 +364,7 @@
public SoundProfile getSoundProfile(
@SoundProfile.ProfileType int type, @NonNull String name) {
try {
- return mService.getSoundProfile(type, name);
+ return mService.getSoundProfile(type, name, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -379,7 +381,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<SoundProfile> getSoundProfilesByPackage(@NonNull String packageName) {
try {
- return mService.getSoundProfilesByPackage(packageName);
+ return mService.getSoundProfilesByPackage(packageName, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -391,7 +393,7 @@
@NonNull
public List<SoundProfile> getAvailableSoundProfiles() {
try {
- return mService.getAvailableSoundProfiles();
+ return mService.getAvailableSoundProfiles(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -409,7 +411,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public List<String> getSoundProfilePackageNames() {
try {
- return mService.getSoundProfilePackageNames();
+ return mService.getSoundProfilePackageNames(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -424,7 +426,7 @@
*/
public void createSoundProfile(@NonNull SoundProfile sp) {
try {
- mService.createSoundProfile(sp);
+ mService.createSoundProfile(sp, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -436,7 +438,7 @@
*/
public void updateSoundProfile(@NonNull String profileId, @NonNull SoundProfile sp) {
try {
- mService.updateSoundProfile(profileId, sp);
+ mService.updateSoundProfile(profileId, sp, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -448,7 +450,7 @@
*/
public void removeSoundProfile(@NonNull String profileId) {
try {
- mService.removeSoundProfile(profileId);
+ mService.removeSoundProfile(profileId, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -460,7 +462,7 @@
@NonNull
public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
try {
- return mService.getParamCapabilities(names);
+ return mService.getParamCapabilities(names, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -478,7 +480,7 @@
@NonNull
public List<String> getPictureProfileAllowList() {
try {
- return mService.getPictureProfileAllowList();
+ return mService.getPictureProfileAllowList(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -492,7 +494,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setPictureProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setPictureProfileAllowList(packageNames);
+ mService.setPictureProfileAllowList(packageNames, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -510,7 +512,7 @@
@NonNull
public List<String> getSoundProfileAllowList() {
try {
- return mService.getSoundProfileAllowList();
+ return mService.getSoundProfileAllowList(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -524,7 +526,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setSoundProfileAllowList(@NonNull List<String> packageNames) {
try {
- mService.setSoundProfileAllowList(packageNames);
+ mService.setSoundProfileAllowList(packageNames, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -536,7 +538,7 @@
*/
public boolean isSupported() {
try {
- return mService.isSupported();
+ return mService.isSupported(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -554,7 +556,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setAutoPictureQualityEnabled(boolean enabled) {
try {
- mService.setAutoPictureQualityEnabled(enabled);
+ mService.setAutoPictureQualityEnabled(enabled, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -565,7 +567,7 @@
*/
public boolean isAutoPictureQualityEnabled() {
try {
- return mService.isAutoPictureQualityEnabled();
+ return mService.isAutoPictureQualityEnabled(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -582,7 +584,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
public void setSuperResolutionEnabled(boolean enabled) {
try {
- mService.setSuperResolutionEnabled(enabled);
+ mService.setSuperResolutionEnabled(enabled, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -593,7 +595,7 @@
*/
public boolean isSuperResolutionEnabled() {
try {
- return mService.isSuperResolutionEnabled();
+ return mService.isSuperResolutionEnabled(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -611,7 +613,7 @@
@RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
public void setAutoSoundQualityEnabled(boolean enabled) {
try {
- mService.setAutoSoundQualityEnabled(enabled);
+ mService.setAutoSoundQualityEnabled(enabled, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -622,7 +624,7 @@
*/
public boolean isAutoSoundQualityEnabled() {
try {
- return mService.isAutoSoundQualityEnabled();
+ return mService.isAutoSoundQualityEnabled(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -668,7 +670,7 @@
@NonNull AmbientBacklightSettings settings) {
Preconditions.checkNotNull(settings);
try {
- mService.setAmbientBacklightSettings(settings);
+ mService.setAmbientBacklightSettings(settings, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -679,7 +681,7 @@
*/
public boolean isAmbientBacklightEnabled() {
try {
- return mService.isAmbientBacklightEnabled();
+ return mService.isAmbientBacklightEnabled(mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -692,7 +694,7 @@
*/
public void setAmbientBacklightEnabled(boolean enabled) {
try {
- mService.setAmbientBacklightEnabled(enabled);
+ mService.setAmbientBacklightEnabled(enabled, mUserId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 65e83b9..8fe5436 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -328,7 +328,7 @@
mRecognitionCallback,
new RecognitionConfig.Builder()
.setCaptureRequested(captureTriggerAudio)
- .setAllowMultipleTriggers(allowMultipleTriggers)
+ .setMultipleTriggersAllowed(allowMultipleTriggers)
.setAudioCapabilities(audioCapabilities)
.build(),
runInBatterySaver);
diff --git a/media/java/android/media/tv/flags/media_tv.aconfig b/media/java/android/media/tv/flags/media_tv.aconfig
index 572db97..3451dfc 100644
--- a/media/java/android/media/tv/flags/media_tv.aconfig
+++ b/media/java/android/media/tv/flags/media_tv.aconfig
@@ -93,7 +93,7 @@
name: "set_resource_holder_retain"
is_exported: true
namespace: "media_tv"
- description: "Feature flag to add setResourceHolderRetain api to MediaCas and Tuner JAVA."
+ description: "Feature flag to add setResourceOwnershipRetention api to MediaCas and Tuner JAVA."
bug: "372973197"
}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index b1adb77..7f7a239 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -757,14 +757,14 @@
* scenario, when both resource holder and resource challenger have same processId and same
* priority.
*
- * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
- * false to allow the resource challenger to acquire the resource. If not explicitly set,
- * resourceHolderRetain is set to false.
+ * @param enabled Set to {@code true} to allow the resource holder to retain ownership,
+ * or false to allow the resource challenger to acquire the resource.
+ * If not explicitly set, enabled is set to {@code false}.
*/
@FlaggedApi(FLAG_SET_RESOURCE_HOLDER_RETAIN)
@RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
- public void setResourceHolderRetain(boolean resourceHolderRetain) {
- mTunerResourceManager.setResourceHolderRetain(mClientId, resourceHolderRetain);
+ public void setResourceOwnershipRetention(boolean enabled) {
+ mTunerResourceManager.setResourceOwnershipRetention(mClientId, enabled);
}
/**
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
index be65ad9..2ed642e 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -227,15 +228,16 @@
* scenario, when both resource holder and resource challenger have same processId and same
* priority.
*
- * @param clientId The client id used to set ownership of resource to owner in case of resource
+ * @param clientId The client id used to set ownership of resource in case of resource
* challenger situation.
- * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
- * false to allow the resource challenger to acquire the resource. If not explicitly set,
- * resourceHolderRetain is set to false.
+ * @param enabled Set to {@code true} to allow the resource holder to retain ownership,
+ * or false to allow the resource challenger to acquire the resource.
+ * If not explicitly set, enabled is set to {@code false}.
*/
- public void setResourceHolderRetain(int clientId, boolean resourceHolderRetain) {
+ @RequiresPermission(android.Manifest.permission.TUNER_RESOURCE_ACCESS)
+ public void setResourceOwnershipRetention(int clientId, boolean enabled) {
try {
- mService.setResourceHolderRetain(clientId, resourceHolderRetain);
+ mService.setResourceOwnershipRetention(clientId, enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index c57be1b0..50f9fe5 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -156,12 +156,13 @@
* scenario, when both Resource Holder and Resource Challenger have same processId and same
* priority.
*
- * @param clientId The resourceHolderRetain of the client is updated using client ID.
- * @param resourceHolderRetain set to true to allow the Resource Holder to retain ownership, or
- * false to allow the Resource Challenger to acquire the resource. If not explicitly set,
- * resourceHolderRetain is set to false.
+ * @param clientId The client id used to set ownership of resource in case of resource
+ * challenger situation.
+ * @param enabled Set to {@code true} to allow the Resource Holder to retain ownership,
+ * or false to allow the Resource Challenger to acquire the resource.
+ * If not explicitly set, enabled is set to {@code false}.
*/
- void setResourceHolderRetain(int clientId, boolean resourceHolderRetain);
+ void setResourceOwnershipRetention(int clientId, boolean enabled);
/*
* This API is used by the Tuner framework to request a frontend from the TunerHAL.
diff --git a/native/android/OWNERS b/native/android/OWNERS
index 9a3527d..1fde7d2 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -2,7 +2,7 @@
# General NDK API reviewers
per-file libandroid.map.txt = danalbert@google.com, etalvala@google.com, michaelwr@google.com
-per-file libandroid.map.txt = jreck@google.com, zyy@google.com
+per-file libandroid.map.txt = jreck@google.com, zyy@google.com, mattbuckley@google.com
# Networking
per-file libandroid_net.map.txt, net.c = set noparent
@@ -31,3 +31,4 @@
# PerformanceHint
per-file performance_hint.cpp = file:/ADPF_OWNERS
+per-file thermal.cpp = file:/ADPF_OWNERS
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 7f555a8..e8644ee 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -301,7 +301,6 @@
ASurfaceTransaction_setEnableBackPressure; # introduced=31
ASurfaceTransaction_setFrameRate; # introduced=30
ASurfaceTransaction_setFrameRateWithChangeStrategy; # introduced=31
- ASurfaceTransaction_setFrameRateParams; # introduced=36
ASurfaceTransaction_clearFrameRate; # introduced=34
ASurfaceTransaction_setFrameTimeline; # introduced=Tiramisu
ASurfaceTransaction_setGeometry; # introduced=29
@@ -376,6 +375,7 @@
APerformanceHint_notifyWorkloadIncrease; # introduced=36
APerformanceHint_notifyWorkloadReset; # introduced=36
APerformanceHint_borrowSessionFromJava; # introduced=36
+ APerformanceHint_setNativeSurfaces; # introduced=36
AWorkDuration_create; # introduced=VanillaIceCream
AWorkDuration_release; # introduced=VanillaIceCream
AWorkDuration_setWorkPeriodStartTimestampNanos; # introduced=VanillaIceCream
@@ -388,6 +388,8 @@
ASessionCreationConfig_setTargetWorkDurationNanos; # introduced=36
ASessionCreationConfig_setPreferPowerEfficiency; # introduced=36
ASessionCreationConfig_setGraphicsPipeline; # introduced=36
+ ASessionCreationConfig_setNativeSurfaces; # introduced=36
+ ASessionCreationConfig_setUseAutoTiming; # introduced=36
local:
*;
};
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 883e139..608c01c 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -29,13 +29,19 @@
#include <aidl/android/os/SessionCreationConfig.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <android/binder_libbinder.h>
#include <android/binder_manager.h>
#include <android/binder_status.h>
+#include <android/native_window.h>
#include <android/performance_hint.h>
+#include <android/surface_control.h>
#include <android/trace.h>
#include <android_os.h>
#include <cutils/trace.h>
#include <fmq/AidlMessageQueue.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
#include <inttypes.h>
#include <jni_wrappers.h>
#include <performance_hint_private.h>
@@ -66,7 +72,12 @@
constexpr int64_t SEND_HINT_TIMEOUT = std::chrono::nanoseconds(100ms).count();
struct AWorkDuration : public hal::WorkDuration {};
-struct ASessionCreationConfig : public SessionCreationConfig {};
+struct ASessionCreationConfig : public SessionCreationConfig {
+ std::vector<wp<IBinder>> layers{};
+ bool hasMode(hal::SessionMode&& mode) {
+ return std::find(modesToEnable.begin(), modesToEnable.end(), mode) != modesToEnable.end();
+ }
+};
bool kForceGraphicsPipeline = false;
@@ -158,6 +169,11 @@
FMQWrapper& getFMQWrapper();
bool canSendLoadHints(std::vector<hal::SessionHint>& hints, int64_t now) REQUIRES(sHintMutex);
void initJava(JNIEnv* _Nonnull env);
+ ndk::ScopedAIBinder_Weak x;
+ template <class T>
+ static void layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls, int numSurfaceControls,
+ std::vector<T>& out);
private:
// Necessary to create an empty binder object
@@ -203,6 +219,8 @@
int setPreferPowerEfficiency(bool enabled);
int reportActualWorkDuration(AWorkDuration* workDuration);
bool isJava();
+ status_t setNativeSurfaces(ANativeWindow** windows, int numWindows, ASurfaceControl** controls,
+ int numSurfaceControls);
private:
friend struct APerformanceHintManager;
@@ -231,7 +249,7 @@
static int64_t sIDCounter GUARDED_BY(sHintMutex);
// The most recent set of thread IDs
std::vector<int32_t> mLastThreadIDs GUARDED_BY(sHintMutex);
- std::optional<hal::SessionConfig> mSessionConfig GUARDED_BY(sHintMutex);
+ std::optional<hal::SessionConfig> mSessionConfig;
// Tracing helpers
void traceThreads(const std::vector<int32_t>& tids) REQUIRES(sHintMutex);
void tracePowerEfficient(bool powerEfficient);
@@ -329,14 +347,12 @@
ndk::ScopedAStatus ret;
hal::SessionConfig sessionConfig{.id = -1};
- SessionCreationConfig creationConfig{
+ ASessionCreationConfig creationConfig{{
.tids = std::vector<int32_t>(threadIds, threadIds + size),
.targetWorkDurationNanos = initialTargetWorkDurationNanos,
- };
+ }};
- return APerformanceHintManager::createSessionUsingConfig(static_cast<ASessionCreationConfig*>(
- &creationConfig),
- tag, isJava);
+ return APerformanceHintManager::createSessionUsingConfig(&creationConfig, tag, isJava);
}
APerformanceHintSession* APerformanceHintManager::createSessionUsingConfig(
@@ -345,11 +361,29 @@
hal::SessionConfig sessionConfig{.id = -1};
ndk::ScopedAStatus ret;
+ // Hold the tokens weakly until we actually need them,
+ // then promote them, then drop all strong refs after
+ if (!sessionCreationConfig->layers.empty()) {
+ for (auto&& layerIter = sessionCreationConfig->layers.begin();
+ layerIter != sessionCreationConfig->layers.end();) {
+ sp<IBinder> promoted = layerIter->promote();
+ if (promoted == nullptr) {
+ layerIter = sessionCreationConfig->layers.erase(layerIter);
+ } else {
+ sessionCreationConfig->layerTokens.push_back(
+ ndk::SpAIBinder(AIBinder_fromPlatformBinder(promoted.get())));
+ ++layerIter;
+ }
+ }
+ }
+
ret = mHintManager->createHintSessionWithConfig(mToken, tag,
*static_cast<SessionCreationConfig*>(
sessionCreationConfig),
&sessionConfig, &session);
+ sessionCreationConfig->layerTokens.clear();
+
if (!ret.isOk() || !session) {
ALOGE("%s: PerformanceHint cannot create session. %s", __FUNCTION__, ret.getMessage());
return nullptr;
@@ -679,6 +713,57 @@
return 0;
}
+status_t APerformanceHintSession::setNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls) {
+ if (!mSessionConfig.has_value()) {
+ return ENOTSUP;
+ }
+
+ std::vector<sp<IBinder>> layerHandles;
+ APerformanceHintManager::layersFromNativeSurfaces<sp<IBinder>>(windows, numWindows, controls,
+ numSurfaceControls,
+ layerHandles);
+
+ std::vector<ndk::SpAIBinder> ndkLayerHandles;
+ for (auto&& handle : layerHandles) {
+ ndkLayerHandles.emplace_back(ndk::SpAIBinder(AIBinder_fromPlatformBinder(handle)));
+ }
+
+ mHintSession->associateToLayers(ndkLayerHandles);
+ return 0;
+}
+
+template <class T>
+void APerformanceHintManager::layersFromNativeSurfaces(ANativeWindow** windows, int numWindows,
+ ASurfaceControl** controls,
+ int numSurfaceControls,
+ std::vector<T>& out) {
+ std::scoped_lock lock(sHintMutex);
+ if (windows != nullptr) {
+ std::vector<ANativeWindow*> windowVec(windows, windows + numWindows);
+ for (auto&& window : windowVec) {
+ Surface* surface = static_cast<Surface*>(window);
+ if (Surface::isValid(surface)) {
+ const sp<IBinder>& handle = surface->getSurfaceControlHandle();
+ if (handle != nullptr) {
+ out.push_back(handle);
+ }
+ }
+ }
+ }
+
+ if (controls != nullptr) {
+ std::vector<ASurfaceControl*> controlVec(controls, controls + numSurfaceControls);
+ for (auto&& aSurfaceControl : controlVec) {
+ SurfaceControl* control = reinterpret_cast<SurfaceControl*>(aSurfaceControl);
+ if (control->isValid()) {
+ out.push_back(control->getHandle());
+ }
+ }
+ }
+}
+
// ===================================== FMQ wrapper implementation
bool FMQWrapper::isActive() {
@@ -963,8 +1048,7 @@
hal::SessionTag::APP, true);
}
-APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env,
- jobject sessionObj) {
+APerformanceHintSession* APerformanceHint_borrowSessionFromJava(JNIEnv* env, jobject sessionObj) {
VALIDATE_PTR(env)
VALIDATE_PTR(sessionObj)
return APerformanceHintManager::getInstance()->getSessionFromJava(env, sessionObj);
@@ -1065,6 +1149,14 @@
return session->notifyWorkloadReset(cpu, gpu, debugName);
}
+int APerformanceHint_setNativeSurfaces(APerformanceHintSession* session,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls, int surfaceControlsSize) {
+ VALIDATE_PTR(session)
+ return session->setNativeSurfaces(nativeWindows, nativeWindowsSize, surfaceControls,
+ surfaceControlsSize);
+}
+
AWorkDuration* AWorkDuration_create() {
return new AWorkDuration();
}
@@ -1180,6 +1272,11 @@
config->modesToEnable.push_back(hal::SessionMode::GRAPHICS_PIPELINE);
} else {
std::erase(config->modesToEnable, hal::SessionMode::GRAPHICS_PIPELINE);
+
+ // Remove automatic timing modes if we turn off GRAPHICS_PIPELINE,
+ // as it is a strict pre-requisite for these to run
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
}
return 0;
}
@@ -1197,3 +1294,48 @@
void APerformanceHint_setUseNewLoadHintBehaviorForTesting(bool newBehavior) {
kForceNewHintBehavior = newBehavior;
}
+
+int ASessionCreationConfig_setNativeSurfaces(ASessionCreationConfig* config,
+ ANativeWindow** nativeWindows, int nativeWindowsSize,
+ ASurfaceControl** surfaceControls,
+ int surfaceControlsSize) {
+ VALIDATE_PTR(config)
+
+ APerformanceHintManager::layersFromNativeSurfaces<wp<IBinder>>(nativeWindows, nativeWindowsSize,
+ surfaceControls,
+ surfaceControlsSize,
+ config->layers);
+
+ if (config->layers.empty()) {
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+int ASessionCreationConfig_setUseAutoTiming(ASessionCreationConfig* _Nonnull config, bool cpu,
+ bool gpu) {
+ VALIDATE_PTR(config)
+ if ((cpu || gpu) && !config->hasMode(hal::SessionMode::GRAPHICS_PIPELINE)) {
+ ALOGE("Automatic timing is not supported unless graphics pipeline mode is enabled first");
+ return ENOTSUP;
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_CPU)) {
+ if (!cpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_CPU);
+ }
+ } else if (cpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_CPU));
+ }
+
+ if (config->hasMode(hal::SessionMode::AUTO_GPU)) {
+ if (!gpu) {
+ std::erase(config->modesToEnable, hal::SessionMode::AUTO_GPU);
+ }
+ } else if (gpu) {
+ config->modesToEnable.push_back(static_cast<hal::SessionMode>(hal::SessionMode::AUTO_GPU));
+ }
+
+ return 0;
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index fc64e9b..6bca145 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -794,28 +794,6 @@
transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
}
-void ASurfaceTransaction_setFrameRateParams(
- ASurfaceTransaction* aSurfaceTransaction, ASurfaceControl* aSurfaceControl,
- float desiredMinRate, float desiredMaxRate, float fixedSourceRate,
- ANativeWindow_ChangeFrameRateStrategy changeFrameRateStrategy) {
- CHECK_NOT_NULL(aSurfaceTransaction);
- CHECK_NOT_NULL(aSurfaceControl);
- Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
-
- if (desiredMaxRate < desiredMinRate) {
- ALOGW("desiredMaxRate must be greater than or equal to desiredMinRate");
- return;
- }
- // TODO(b/362798998): Fix plumbing to send modern params
- int compatibility = fixedSourceRate == 0 ? ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
- : ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
- double frameRate = compatibility == ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
- ? fixedSourceRate
- : desiredMinRate;
- transaction->setFrameRate(surfaceControl, frameRate, compatibility, changeFrameRateStrategy);
-}
-
void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl) {
CHECK_NOT_NULL(aSurfaceTransaction);
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index b006580..b8f574f 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -83,6 +83,7 @@
(override));
MOCK_METHOD(ScopedAStatus, getGpuHeadroomMinIntervalMillis, (int64_t* _aidl_return),
(override));
+ MOCK_METHOD(ScopedAStatus, passSessionManagerBinder, (const SpAIBinder& sessionManager));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
@@ -99,6 +100,8 @@
MOCK_METHOD(ScopedAStatus, close, (), (override));
MOCK_METHOD(ScopedAStatus, reportActualWorkDuration2,
(const ::std::vector<hal::WorkDuration>& workDurations), (override));
+ MOCK_METHOD(ScopedAStatus, associateToLayers,
+ (const std::vector<::ndk::SpAIBinder>& in_layerTokens), (override));
MOCK_METHOD(SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
};
diff --git a/nfc/tests/src/android/nfc/NdefRecordTest.java b/nfc/tests/src/android/nfc/NdefRecordTest.java
new file mode 100644
index 0000000..231e939
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NdefRecordTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NdefRecordTest {
+
+ @Test
+ public void testNdefRecordConstructor() throws FormatException {
+ NdefRecord applicationRecord = NdefRecord
+ .createApplicationRecord("com.android.test");
+ NdefRecord ndefRecord = new NdefRecord(applicationRecord.toByteArray());
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.toByteArray().length).isGreaterThan(0);
+ assertThat(ndefRecord.getType()).isEqualTo("android.com:pkg".getBytes());
+ assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+ }
+
+ @Test
+ public void testCreateExternal() {
+ NdefRecord ndefRecord = NdefRecord.createExternal("test",
+ "android.com:pkg", "com.android.test".getBytes());
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getType()).isEqualTo("test:android.com:pkg".getBytes());
+ assertThat(ndefRecord.getPayload()).isEqualTo("com.android.test".getBytes());
+ }
+
+ @Test
+ public void testCreateUri() {
+ NdefRecord ndefRecord = NdefRecord.createUri("http://www.example.com");
+ assertThat(ndefRecord).isNotNull();
+ assertThat(ndefRecord.getTnf()).isEqualTo(NdefRecord.TNF_WELL_KNOWN);
+ assertThat(ndefRecord.getType()).isEqualTo(NdefRecord.RTD_URI);
+ }
+
+}
diff --git a/omapi/aidl/Android.bp b/omapi/aidl/Android.bp
index e71597a..3916bf3 100644
--- a/omapi/aidl/Android.bp
+++ b/omapi/aidl/Android.bp
@@ -24,6 +24,7 @@
backend: {
java: {
sdk_version: "module_current",
+ min_sdk_version: "35", // Make it 36 once available.
apex_available: [
"//apex_available:platform",
"com.android.nfcservices",
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 8f9730a..6fdd73a 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -50,6 +50,7 @@
android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
android:label="@string/action_bar_label"
android:exported="true"
+ android:enableOnBackInvokedCallback="false"
android:permission="android.permission.MODIFY_PHONE_STATE"
android:theme="@style/AppTheme"
android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index 68443a7..e029f3a 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -81,10 +81,12 @@
android:exported="false" />
<activity android:name=".PackageInstallerActivity"
- android:exported="false" />
+ android:exported="false"
+ android:enableOnBackInvokedCallback="false" />
<activity android:name=".InstallInstalling"
- android:exported="false" />
+ android:exported="false"
+ android:enableOnBackInvokedCallback="false" />
<receiver android:name=".common.InstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
@@ -138,6 +140,7 @@
<activity android:name=".UninstallUninstalling"
android:excludeFromRecents="true"
+ android:enableOnBackInvokedCallback="false"
android:exported="false" />
<receiver android:name=".UninstallFinish"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
index a3709c1..366d54b 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceStateProviders.kt
@@ -136,6 +136,13 @@
/** Returns the preference widget object associated with given key. */
abstract fun <T> findPreference(key: String): T?
+ /**
+ * Returns the preference widget object associated with given key.
+ *
+ * @throws NullPointerException if preference is not found
+ */
+ abstract fun <T : Any> requirePreference(key: String): T
+
/** Notifies that preference state of given key is changed and updates preference widget UI. */
abstract fun notifyPreferenceChange(key: String)
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index 7cec59c..153089e 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -60,6 +60,8 @@
override fun <T> findPreference(key: String) =
preferenceScreen.findPreference(key) as T?
+ override fun <T : Any> requirePreference(key: String) = findPreference<T>(key)!!
+
override fun notifyPreferenceChange(key: String) =
notifyChange(key, CHANGE_REASON_STATE)
diff --git a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
index 7e4460b..7580973 100644
--- a/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
+++ b/packages/SettingsLib/RestrictedLockUtils/res/values/strings.xml
@@ -17,9 +17,12 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Summary for switch preference to denote it is switched on [CHAR LIMIT=50] -->
+ <!-- Summary for switch preference to denote it is switched on by an admin [CHAR LIMIT=50] -->
<string name="enabled_by_admin">Enabled by admin</string>
- <!-- Summary for switch preference to denote it is switched off [CHAR LIMIT=50] -->
+ <!-- Summary for switch preference to denote it is switched off by an admin [CHAR LIMIT=50] -->
<string name="disabled_by_admin">Disabled by admin</string>
-
-</resources>
\ No newline at end of file
+ <!-- Summary for switch preference to denote it is switched on by Advanced protection [CHAR LIMIT=50] -->
+ <string name="enabled_by_advanced_protection">Enabled by Advanced Protection</string>
+ <!-- Summary for switch preference to denote it is switched off by Advanced protection [CHAR LIMIT=50] -->
+ <string name="disabled_by_advanced_protection">Disabled by Advanced Protection</string>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
index 0cd0b3c..19818e0 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_icon_frame.xml
@@ -22,7 +22,7 @@
android:minWidth="@dimen/settingslib_expressive_space_medium3"
android:minHeight="@dimen/settingslib_expressive_space_medium3"
android:gravity="center"
- android:layout_marginEnd="-4dp"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_extrasmall6"
android:filterTouchesWhenObscured="false">
<androidx.preference.internal.PreferenceImageView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
index 944bef6..c837ff4 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_expressive_preference_text_frame.xml
@@ -21,7 +21,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingVertical="@dimen/settingslib_expressive_space_small1"
- android:paddingHorizontal="@dimen/settingslib_expressive_space_small1"
+ android:paddingStart="@dimen/settingslib_expressive_space_none"
+ android:paddingEnd="@dimen/settingslib_expressive_space_small1"
android:filterTouchesWhenObscured="false">
<TextView
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
index 5baf7be..b5a6ffa 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/EnterpriseRepository.kt
@@ -22,10 +22,14 @@
import android.content.Context
import android.content.pm.UserInfo
import com.android.settingslib.R
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
interface IEnterpriseRepository {
fun getEnterpriseString(updatableStringId: String, resId: Int): String
+ fun getAdminSummaryString(advancedProtectionStringId: Int, updatableStringId: String,
+ resId: Int, enforcedAdmin: RestrictedLockUtils.EnforcedAdmin?, userId: Int): String
}
class EnterpriseRepository(private val context: Context) : IEnterpriseRepository {
@@ -34,6 +38,21 @@
override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
checkNotNull(resources.getString(updatableStringId) { context.getString(resId) })
+ override fun getAdminSummaryString(
+ advancedProtectionStringId: Int,
+ updatableStringId: String,
+ resId: Int,
+ enforcedAdmin: RestrictedLockUtils.EnforcedAdmin?,
+ userId: Int
+ ): String {
+ return if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
+ enforcedAdmin?.enforcedRestriction, userId)) {
+ context.getString(advancedProtectionStringId)
+ } else {
+ getEnterpriseString(updatableStringId, resId)
+ }
+ }
+
fun getProfileTitle(userInfo: UserInfo): String = if (userInfo.isManagedProfile) {
getEnterpriseString(WORK_CATEGORY_HEADER, R.string.category_work)
} else if (userInfo.isPrivateProfile) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
index b6d9242..a140eb8 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedMode.kt
@@ -37,21 +37,27 @@
fun showRestrictedSettingsDetails()
}
-
internal data class BlockedByAdminImpl(
private val context: Context,
private val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin,
+ private val userId: Int,
private val enterpriseRepository: IEnterpriseRepository = EnterpriseRepository(context),
) : BlockedByAdmin {
override fun getSummary(checked: Boolean?) = when (checked) {
- true -> enterpriseRepository.getEnterpriseString(
+ true -> enterpriseRepository.getAdminSummaryString(
+ advancedProtectionStringId = R.string.enabled_by_advanced_protection,
updatableStringId = Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY,
resId = R.string.enabled_by_admin,
+ enforcedAdmin = enforcedAdmin,
+ userId = userId,
)
- false -> enterpriseRepository.getEnterpriseString(
+ false -> enterpriseRepository.getAdminSummaryString(
+ advancedProtectionStringId = R.string.disabled_by_advanced_protection,
updatableStringId = Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY,
resId = R.string.disabled_by_admin,
+ enforcedAdmin = enforcedAdmin,
+ userId = userId,
)
else -> ""
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
index 6b1893c..3309faa 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -84,7 +84,11 @@
for (key in restrictions.keys) {
RestrictedLockUtilsInternal
.checkIfRestrictionEnforced(context, key, restrictions.userId)
- ?.let { return BlockedByAdminImpl(context = context, enforcedAdmin = it) }
+ ?.let { return BlockedByAdminImpl(
+ context = context,
+ enforcedAdmin = it,
+ userId = restrictions.userId
+ ) }
}
restrictions.enhancedConfirmation?.let { ec ->
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
index 8fd16b3..f3245c9 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictedModeTest.kt
@@ -16,19 +16,49 @@
package com.android.settingslib.spaprivileged.model.enterprise
+import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.app.admin.EnforcingAdmin
import android.content.Context
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.security.Flags
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
+import com.android.settingslib.spaprivileged.tests.testutils.getEnforcingAdminAdvancedProtection
+import com.android.settingslib.spaprivileged.tests.testutils.getEnforcingAdminNotAdvancedProtection
+import com.android.settingslib.widget.restricted.R
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class RestrictedModeTest {
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @get:Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Spy
private val context: Context = ApplicationProvider.getApplicationContext()
+ @Mock
+ private lateinit var devicePolicyManager: DevicePolicyManager
+
private val fakeEnterpriseRepository = object : IEnterpriseRepository {
override fun getEnterpriseString(updatableStringId: String, resId: Int): String =
when (updatableStringId) {
@@ -36,20 +66,123 @@
Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY -> DISABLED_BY_ADMIN
else -> ""
}
+
+ override fun getAdminSummaryString(
+ advancedProtectionStringId: Int,
+ updatableStringId: String,
+ resId: Int,
+ enforcedAdmin: RestrictedLockUtils.EnforcedAdmin?,
+ userId: Int
+ ): String {
+ if (RestrictedLockUtilsInternal.isPolicyEnforcedByAdvancedProtection(context,
+ RESTRICTION, userId)) {
+ return when (advancedProtectionStringId) {
+ R.string.enabled_by_advanced_protection -> ENABLED_BY_ADVANCED_PROTECTION
+ R.string.disabled_by_advanced_protection -> DISABLED_BY_ADVANCED_PROTECTION
+ else -> ""
+ }
+ }
+ return getEnterpriseString(updatableStringId, resId)
+ }
}
+ @Before
+ fun setUp() {
+ whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
+ }
+
+ @RequiresFlagsDisabled(Flags.FLAG_AAPM_API)
@Test
fun blockedByAdmin_getSummaryWhenChecked() {
- val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, USER_ID,
+ fakeEnterpriseRepository)
val summary = blockedByAdmin.getSummary(true)
assertThat(summary).isEqualTo(ENABLED_BY_ADMIN)
}
+ @RequiresFlagsDisabled(Flags.FLAG_AAPM_API)
@Test
fun blockedByAdmin_getSummaryNotWhenChecked() {
- val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, fakeEnterpriseRepository)
+ val blockedByAdmin = BlockedByAdminImpl(context, ENFORCED_ADMIN, USER_ID,
+ fakeEnterpriseRepository)
+
+ val summary = blockedByAdmin.getSummary(false)
+
+ assertThat(summary).isEqualTo(DISABLED_BY_ADMIN)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun blockedByAdmin_disabledByAdvancedProtection_getSummaryWhenChecked() {
+ val blockedByAdmin =
+ BlockedByAdminImpl(
+ context = context,
+ enforcedAdmin = ENFORCED_ADMIN,
+ enterpriseRepository = fakeEnterpriseRepository,
+ userId = USER_ID,
+ )
+
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_ADVANCED_PROTECTION)
+
+ val summary = blockedByAdmin.getSummary(true)
+
+ assertThat(summary).isEqualTo(ENABLED_BY_ADVANCED_PROTECTION)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun blockedByAdmin_disabledByAdvancedProtection_getSummaryWhenNotChecked() {
+ val blockedByAdmin =
+ BlockedByAdminImpl(
+ context = context,
+ enforcedAdmin = ENFORCED_ADMIN,
+ enterpriseRepository = fakeEnterpriseRepository,
+ userId = USER_ID,
+ )
+
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_ADVANCED_PROTECTION)
+
+ val summary = blockedByAdmin.getSummary(false)
+
+ assertThat(summary).isEqualTo(DISABLED_BY_ADVANCED_PROTECTION)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun blockedByAdmin_notDisabledByAdvancedProtection_getSummaryWhenChecked() {
+ val blockedByAdmin =
+ BlockedByAdminImpl(
+ context = context,
+ enforcedAdmin = ENFORCED_ADMIN,
+ enterpriseRepository = fakeEnterpriseRepository,
+ userId = USER_ID,
+ )
+
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_NOT_ADVANCED_PROTECTION)
+
+ val summary = blockedByAdmin.getSummary(true)
+
+ assertThat(summary).isEqualTo(ENABLED_BY_ADMIN)
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun blockedByAdmin_notDisabledByAdvancedProtection_getSummaryWhenNotChecked() {
+ val blockedByAdmin =
+ BlockedByAdminImpl(
+ context = context,
+ enforcedAdmin = ENFORCED_ADMIN,
+ enterpriseRepository = fakeEnterpriseRepository,
+ userId = USER_ID,
+ )
+
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_NOT_ADVANCED_PROTECTION)
val summary = blockedByAdmin.getSummary(false)
@@ -57,11 +190,19 @@
}
private companion object {
+ const val PACKAGE_NAME = "package.name"
const val RESTRICTION = "restriction"
+ const val USER_ID = 0
val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+ val ENFORCING_ADMIN_ADVANCED_PROTECTION: EnforcingAdmin =
+ getEnforcingAdminAdvancedProtection(PACKAGE_NAME, USER_ID)
+ val ENFORCING_ADMIN_NOT_ADVANCED_PROTECTION: EnforcingAdmin =
+ getEnforcingAdminNotAdvancedProtection(PACKAGE_NAME, USER_ID)
const val ENABLED_BY_ADMIN = "Enabled by admin"
const val DISABLED_BY_ADMIN = "Disabled by admin"
+ const val ENABLED_BY_ADVANCED_PROTECTION = "Enabled by advanced protection"
+ const val DISABLED_BY_ADVANCED_PROTECTION = "Disabled by advanced protection"
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
index e736115..79085af 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPageTest.kt
@@ -16,8 +16,17 @@
package com.android.settingslib.spaprivileged.template.app
+import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.app.admin.DevicePolicyResourcesManager
+import android.app.admin.EnforcingAdmin
import android.content.Context
import android.content.pm.ApplicationInfo
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.platform.test.flag.junit.CheckFlagsRule
+import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.security.Flags
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
@@ -29,28 +38,59 @@
import com.android.settingslib.RestrictedLockUtils
import com.android.settingslib.spa.testutils.FakeNavControllerWrapper
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdminImpl
import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
import com.android.settingslib.spaprivileged.tests.testutils.FakeRestrictionsProvider
import com.android.settingslib.spaprivileged.tests.testutils.TestAppRecord
import com.android.settingslib.spaprivileged.tests.testutils.TestTogglePermissionAppListModel
+import com.android.settingslib.spaprivileged.tests.testutils.getEnforcingAdminAdvancedProtection
+import com.android.settingslib.spaprivileged.tests.testutils.getEnforcingAdminNotAdvancedProtection
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class TogglePermissionAppListPageTest {
+ @Rule
+ @JvmField
+ val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
@get:Rule
val composeTestRule = createComposeRule()
+ @get:Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var devicePolicyManager: DevicePolicyManager
+
+ @Mock
+ private lateinit var devicePolicyResourcesManager: DevicePolicyResourcesManager
+
+ @Spy
private val context: Context = ApplicationProvider.getApplicationContext()
private val fakeNavControllerWrapper = FakeNavControllerWrapper()
private val fakeRestrictionsProvider = FakeRestrictionsProvider()
+ @Before
+ fun setUp() {
+ whenever(context.devicePolicyManager).thenReturn(devicePolicyManager)
+ whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
+ }
+
@Test
fun pageTitle() {
val listModel = TestTogglePermissionAppListModel()
@@ -98,10 +138,65 @@
assertThat(summary).isEqualTo(context.getPlaceholder())
}
+ @RequiresFlagsDisabled(Flags.FLAG_AAPM_API)
@Test
fun summary_whenAllowedButAdminOverrideToNotAllowed() {
fakeRestrictionsProvider.restrictedMode =
- BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN)
+ BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN, userId = USER_ID)
+ val listModel =
+ TestTogglePermissionAppListModel(
+ isAllowed = true,
+ switchifBlockedByAdminOverrideCheckedValueTo = false,
+ )
+
+ val summary = getSummary(listModel)
+
+ assertThat(summary)
+ .isEqualTo(
+ context.getString(
+ com.android.settingslib.widget.restricted.R.string.disabled_by_admin
+ )
+ )
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun summary_disabledByAdvancedProtection_whenAllowedButAdminOverrideToNotAllowed() {
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_ADVANCED_PROTECTION)
+
+ fakeRestrictionsProvider.restrictedMode =
+ BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN, userId = USER_ID)
+ val listModel =
+ TestTogglePermissionAppListModel(
+ isAllowed = true,
+ switchifBlockedByAdminOverrideCheckedValueTo = false,
+ )
+
+ val summary = getSummary(listModel)
+
+ assertThat(summary)
+ .isEqualTo(
+ context.getString(
+ com.android.settingslib.widget.restricted.R.string
+ .disabled_by_advanced_protection
+ )
+ )
+ }
+
+ @RequiresFlagsEnabled(Flags.FLAG_AAPM_API)
+ @Test
+ fun summary_notDisabledByAdvancedProtection_whenAllowedButAdminOverrideToNotAllowed() {
+ val disabledByAdminText = context.getString(
+ com.android.settingslib.widget.restricted.R.string.disabled_by_admin
+ )
+ whenever(devicePolicyManager.getEnforcingAdmin(USER_ID, RESTRICTION))
+ .thenReturn(ENFORCING_ADMIN_NOT_ADVANCED_PROTECTION)
+ whenever(devicePolicyResourcesManager.getString(
+ eq(Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY), any())).thenReturn(disabledByAdminText)
+
+ fakeRestrictionsProvider.restrictedMode =
+ BlockedByAdminImpl(context = context, enforcedAdmin = ENFORCED_ADMIN, userId = USER_ID)
val listModel =
TestTogglePermissionAppListModel(
isAllowed = true,
@@ -186,7 +281,12 @@
const val SUMMARY = "Summary"
val APP = ApplicationInfo().apply { packageName = PACKAGE_NAME }
const val RESTRICTION = "restriction"
+ const val USER_ID = 0
val ENFORCED_ADMIN: RestrictedLockUtils.EnforcedAdmin =
RestrictedLockUtils.EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+ val ENFORCING_ADMIN_ADVANCED_PROTECTION: EnforcingAdmin =
+ getEnforcingAdminAdvancedProtection(PACKAGE_NAME, USER_ID)
+ val ENFORCING_ADMIN_NOT_ADVANCED_PROTECTION: EnforcingAdmin =
+ getEnforcingAdminNotAdvancedProtection(PACKAGE_NAME, USER_ID)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
index f8ca2a0..d5e8d6a 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/RestrictedTestUtils.kt
@@ -16,6 +16,10 @@
package com.android.settingslib.spaprivileged.tests.testutils
+import android.app.admin.EnforcingAdmin
+import android.app.admin.UnknownAuthority
+import android.os.UserHandle
+import android.security.advancedprotection.AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY
import androidx.compose.runtime.Composable
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
@@ -55,3 +59,10 @@
@Composable
override fun restrictedModeState() = stateOf(restrictedMode)
}
+
+fun getEnforcingAdminAdvancedProtection(packageName: String, userId: Int): EnforcingAdmin =
+ EnforcingAdmin(packageName, UnknownAuthority(ADVANCED_PROTECTION_SYSTEM_ENTITY),
+ UserHandle.of(userId))
+
+fun getEnforcingAdminNotAdvancedProtection(packageName: String, userId: Int): EnforcingAdmin =
+ EnforcingAdmin(packageName, UnknownAuthority.UNKNOWN_AUTHORITY, UserHandle.of(userId))
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index fc163ce..4de6476 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -27,8 +27,10 @@
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
-import android.app.ecm.EnhancedConfirmationManager;
+import android.app.admin.EnforcingAdmin;
import android.app.admin.PackagePolicy;
+import android.app.admin.UnknownAuthority;
+import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
@@ -43,6 +45,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager.EnforcingUser;
+import android.security.advancedprotection.AdvancedProtectionManager;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
@@ -202,6 +205,14 @@
return null;
}
+ if (android.security.Flags.aapmApi()) {
+ EnforcingAdmin admin = dpm.getEnforcingAdmin(userId, userRestriction);
+ if (admin != null) {
+ return new EnforcedAdmin(admin.getComponentName(), userRestriction,
+ admin.getUserHandle());
+ }
+ }
+
final EnforcedAdmin admin =
getProfileOrDeviceOwner(context, userRestriction, enforcingUser.getUserHandle());
if (admin != null) {
@@ -838,6 +849,22 @@
}
/**
+ * Checks if the identifier is enforced by advanced protection.
+ */
+ @RequiresApi(Build.VERSION_CODES.BAKLAVA)
+ public static boolean isPolicyEnforcedByAdvancedProtection(Context context, String identifier,
+ int userId) {
+ if (!android.security.Flags.aapmApi()) return false;
+ if (identifier == null) return false;
+ EnforcingAdmin admin = context.getSystemService(DevicePolicyManager.class)
+ .getEnforcingAdmin(userId, identifier);
+ if (admin == null) return false;
+ return admin.getAuthority() instanceof UnknownAuthority authority
+ && AdvancedProtectionManager.ADVANCED_PROTECTION_SYSTEM_ENTITY.equals(
+ authority.getName());
+ }
+
+ /**
* Check if there are restrictions on an application from being a Credential Manager provider.
*
* @return EnforcedAdmin Object containing the enforced admin component and admin user details,
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 6ca9279..25628fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -33,7 +33,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
@@ -118,10 +117,7 @@
if (mDisabledSummary) {
final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
if (summaryView != null) {
- final CharSequence disabledText =
- (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
- ? getDisabledByAdminUpdatableString()
- : mContext.getString(R.string.disabled_by_admin_summary_text);
+ final CharSequence disabledText = getDisabledByAdminSummaryString();
if (mDisabledByAdmin) {
summaryView.setText(disabledText);
} else if (mDisabledByEcm) {
@@ -134,11 +130,23 @@
}
}
- @RequiresApi(Build.VERSION_CODES.TIRAMISU)
- private String getDisabledByAdminUpdatableString() {
- return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
- CONTROLLED_BY_ADMIN_SUMMARY,
- () -> mContext.getString(R.string.disabled_by_admin_summary_text));
+ private String getDisabledByAdminSummaryString() {
+ if (isRestrictionEnforcedByAdvancedProtection()) {
+ return mContext.getString(com.android.settingslib.widget.restricted
+ .R.string.disabled_by_advanced_protection);
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ return mContext.getSystemService(DevicePolicyManager.class).getResources().getString(
+ CONTROLLED_BY_ADMIN_SUMMARY,
+ () -> mContext.getString(R.string.disabled_by_admin_summary_text));
+ }
+ return mContext.getString(R.string.disabled_by_admin_summary_text);
+ }
+
+ public boolean isRestrictionEnforcedByAdvancedProtection() {
+ return mEnforcedAdmin != null && RestrictedLockUtilsInternal
+ .isPolicyEnforcedByAdvancedProtection(mContext, mEnforcedAdmin.enforcedRestriction,
+ UserHandle.myUserId());
}
public void useAdminDisabledSummary(boolean useSummary) {
@@ -226,17 +234,24 @@
*/
public boolean setDisabledByAdmin(EnforcedAdmin admin) {
boolean disabled = false;
+ boolean changed = false;
+ EnforcedAdmin previousAdmin = mEnforcedAdmin;
mEnforcedAdmin = null;
if (admin != null) {
disabled = true;
// Copy the received instance to prevent pass be reference being overwritten.
mEnforcedAdmin = new EnforcedAdmin(admin);
+ if (android.security.Flags.aapmApi()) {
+ changed = previousAdmin == null || !previousAdmin.equals(admin);
+ }
}
- boolean changed = false;
if (mDisabledByAdmin != disabled) {
mDisabledByAdmin = disabled;
changed = true;
+ }
+
+ if (changed) {
updateDisabledState();
}
@@ -286,6 +301,10 @@
((PrimarySwitchPreference) mPreference).setSwitchEnabled(isEnabled);
}
+ if (android.security.Flags.aapmApi() && !isEnabled && mDisabledByAdmin) {
+ mPreference.setSummary(getDisabledByAdminSummaryString());
+ }
+
if (!isEnabled && mDisabledByEcm) {
mPreference.setSummary(R.string.disabled_by_app_ops_text);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 727dbe1..0aac9a11 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -126,13 +126,7 @@
CharSequence switchSummary;
if (mRestrictedSwitchSummary == null) {
- switchSummary = isChecked()
- ? getUpdatableEnterpriseString(
- getContext(), ENABLED_BY_ADMIN_SWITCH_SUMMARY,
- com.android.settingslib.widget.restricted.R.string.enabled_by_admin)
- : getUpdatableEnterpriseString(
- getContext(), DISABLED_BY_ADMIN_SWITCH_SUMMARY,
- com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
+ switchSummary = getRestrictedSwitchSummary();
} else {
switchSummary = mRestrictedSwitchSummary;
}
@@ -177,6 +171,25 @@
() -> context.getString(resId));
}
+ private String getRestrictedSwitchSummary() {
+ if (mHelper.isRestrictionEnforcedByAdvancedProtection()) {
+ final int apmResId = isChecked()
+ ? com.android.settingslib.widget.restricted.R.string
+ .enabled_by_advanced_protection
+ : com.android.settingslib.widget.restricted.R.string
+ .disabled_by_advanced_protection;
+ return getContext().getString(apmResId);
+ }
+
+ return isChecked()
+ ? getUpdatableEnterpriseString(
+ getContext(), ENABLED_BY_ADMIN_SWITCH_SUMMARY,
+ com.android.settingslib.widget.restricted.R.string.enabled_by_admin)
+ : getUpdatableEnterpriseString(
+ getContext(), DISABLED_BY_ADMIN_SWITCH_SUMMARY,
+ com.android.settingslib.widget.restricted.R.string.disabled_by_admin);
+ }
+
@Override
public void performClick() {
if (!mHelper.performClick()) {
diff --git a/packages/SettingsProvider/res/xml/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml
index cb38b53..645b275 100644
--- a/packages/SettingsProvider/res/xml/bookmarks.xml
+++ b/packages/SettingsProvider/res/xml/bookmarks.xml
@@ -20,10 +20,14 @@
Typical shortcuts (not necessarily defined here):
'b': Browser
- 'p': Contacts
+ 'c': Contacts
'e': Email
- 'c': Calendar
+ 'g': GMail
+ 'k': Calendar
'm': Maps
+ 'p': Music
+ 's': SMS
+ 't': Talk
'u': Calculator
'y': YouTube
-->
@@ -36,17 +40,23 @@
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="p" />
+ shortcut="c" />
<bookmark
category="android.intent.category.APP_EMAIL"
shortcut="e" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="c" />
+ shortcut="k" />
<bookmark
category="android.intent.category.APP_MAPS"
shortcut="m" />
<bookmark
+ category="android.intent.category.APP_MUSIC"
+ shortcut="p" />
+ <bookmark
+ role="android.app.role.SMS"
+ shortcut="s" />
+ <bookmark
category="android.intent.category.APP_CALCULATOR"
shortcut="u" />
</bookmarks>
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 9938139..63f401c 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -230,6 +230,7 @@
VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.TOUCHPAD_TAP_DRAGGING, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.TOUCHPAD_RIGHT_CLICK_ZONE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(System.TOUCHPAD_SYSTEM_GESTURES, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.LOCK_TO_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(
System.EGG_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 5ae11ba..37eda3e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -3049,6 +3049,9 @@
dumpSetting(s, p,
Settings.System.TOUCHPAD_THREE_FINGER_TAP_CUSTOMIZATION,
SystemSettingsProto.Touchpad.THREE_FINGER_TAP_CUSTOMIZATION);
+ dumpSetting(s, p,
+ Settings.System.TOUCHPAD_SYSTEM_GESTURES,
+ SystemSettingsProto.Touchpad.SYSTEM_GESTURES);
p.end(touchpadToken);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a62b7fd..9004488 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -947,7 +947,9 @@
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
Settings.System.WEAR_TTS_PREWARM_ENABLED,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
- Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
+ Settings.System.MULTI_AUDIO_FOCUS_ENABLED, // form-factor/OEM specific
+ // Potentially disruptive to on-boarding flow on new devices
+ Settings.System.TOUCHPAD_SYSTEM_GESTURES
);
if (!Flags.backUpSmoothDisplayAndForcePeakRefreshRate()) {
settings.add(Settings.System.MIN_REFRESH_RATE);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e03afec..0ec5571 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -990,9 +990,6 @@
<uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE"
android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
- <!-- Permissions required for CTS test - BugreportManagerTest -->
- <uses-permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" />
-
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index a7b91c2..0f210e7 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -41,7 +41,7 @@
android:exported="true"
android:label="@string/accessibility_menu_settings_name"
android:launchMode="singleTop"
- android:theme="@style/SettingsTheme">
+ android:theme="@style/Theme.SettingsBase">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
index a138fa9..4169155 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values/styles.xml
@@ -21,11 +21,6 @@
<item name="android:colorControlNormal">@color/colorControlNormal</item>
</style>
- <style name="SettingsTheme" parent="Theme.SettingsBase">
- <!-- Quick fix so that the preference page doesn't render under its parent header. -->
- <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
- </style>
-
<!--The basic theme for service and test case only-->
<style name="A11yMenuBaseTheme" parent="android:Theme.DeviceDefault.Light">
<item name="android:windowActionBar">false</item>
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 129dd9b..3f7ce2c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -21,15 +21,18 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.graphics.Insets;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.provider.Settings;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.widget.TextView;
import android.window.OnBackInvokedCallback;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
@@ -94,6 +97,18 @@
super.onViewCreated(view, savedInstanceState);
view.setLayoutDirection(
view.getResources().getConfiguration().getLayoutDirection());
+ view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @NonNull
+ @Override
+ public WindowInsets onApplyWindowInsets(@NonNull View v,
+ @NonNull WindowInsets windowInsets) {
+ Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars()
+ | WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.displayCutout());
+ v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ return WindowInsets.CONSUMED;
+ }
+ });
}
/**
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 0d654d9..02e7b5f 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -156,13 +156,6 @@
}
flag {
- name: "notifications_improved_hun_animation"
- namespace: "systemui"
- description: "Adds a translateY animation, and other improvements to match the motion specs of the HUN Intro + Outro animations."
- bug: "243302608"
-}
-
-flag {
name: "notification_content_alpha_optimization"
namespace: "systemui"
description: "Only reset alpha values of needed content views"
@@ -1317,6 +1310,13 @@
}
flag {
+ name: "media_controls_ui_update"
+ namespace: "systemui"
+ description: "Enables media visuals update"
+ bug: "380053768"
+}
+
+flag {
namespace: "systemui"
name: "enable_view_capture_tracing"
description: "Enables view capture tracing in System UI."
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 0f5e367..ca2b957 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -16,6 +16,8 @@
package com.android.systemui.animation;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
@@ -39,6 +41,7 @@
import com.android.wm.shell.shared.TransitionUtil;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -90,7 +93,7 @@
() -> {
mStartTransaction = t;
mFinishCallback = finishCallback;
- startAnimationInternal(info);
+ startAnimationInternal(info, /* states= */ null);
});
}
@@ -112,7 +115,13 @@
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback,
WindowAnimationState[] states) {
- logD("takeOverAnimation - " + info);
+ logD("takeOverAnimation - info=" + info + ", states=" + Arrays.toString(states));
+ mHandler.post(
+ () -> {
+ mStartTransaction = t;
+ mFinishCallback = finishCallback;
+ startAnimationInternal(info, states);
+ });
}
@Override
@@ -121,14 +130,19 @@
mHandler.post(this::cancel);
}
- private void startAnimationInternal(TransitionInfo info) {
+ private void startAnimationInternal(
+ TransitionInfo info, @Nullable WindowAnimationState[] states) {
if (!prepareUIs(info)) {
logE("Unable to prepare UI!");
finishAnimation(/* finished= */ false);
return;
}
// Notify player that we are starting.
- mPlayer.onStart(info, mStartTransaction, mOrigin, mOriginTransaction);
+ mPlayer.onStart(info, states, mStartTransaction, mOrigin, mOriginTransaction);
+
+ // Apply the initial transactions in case the player forgot to apply them.
+ mOriginTransaction.commit();
+ mStartTransaction.apply();
// Start the animator.
mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
@@ -205,7 +219,8 @@
.setCornerRadius(leash, windowRadius)
.setWindowCrop(leash, bounds.width(), bounds.height());
}
- } else if (TransitionUtil.isClosingMode(mode)) {
+ } else if (TransitionUtil.isClosingMode(mode) || mode == TRANSIT_CHANGE) {
+ // TRANSIT_CHANGE refers to the closing window in predictive back animation.
closingSurfaces.add(change.getLeash());
// For closing surfaces, starting bounds are base bounds. Apply corner radius if
// it's full screen.
@@ -236,13 +251,8 @@
// Attach origin UIComponent to origin leash.
mOriginTransaction = mOrigin.newTransaction();
- mOriginTransaction
- .attachToTransitionLeash(
- mOrigin, mOriginLeash, displayBounds.width(), displayBounds.height())
- .commit();
-
- // Apply all surface changes.
- mStartTransaction.apply();
+ mOriginTransaction.attachToTransitionLeash(
+ mOrigin, mOriginLeash, displayBounds.width(), displayBounds.height());
return true;
}
@@ -328,6 +338,58 @@
/* baseBounds= */ maxBounds);
}
+ private static void applyWindowAnimationStates(
+ TransitionInfo info,
+ @Nullable WindowAnimationState[] states,
+ UIComponent closingApp,
+ UIComponent openingApp) {
+ if (states == null) {
+ // Nothing to apply.
+ return;
+ }
+ // Calculate bounds.
+ Rect maxClosingBounds = new Rect();
+ Rect maxOpeningBounds = new Rect();
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ Rect bound = getBounds(states[i]);
+ if (bound == null) {
+ continue;
+ }
+ int mode = info.getChanges().get(i).getMode();
+ if (TransitionUtil.isOpeningMode(mode)) {
+ maxOpeningBounds.union(bound);
+ } else if (TransitionUtil.isClosingMode(mode) || mode == TRANSIT_CHANGE) {
+ // TRANSIT_CHANGE refers to the closing window in predictive back animation.
+ maxClosingBounds.union(bound);
+ }
+ }
+
+ // Intentionally use a new transaction instead of reusing the existing transaction since we
+ // want to apply window animation states first without committing any other pending changes
+ // in the existing transaction. The existing transaction is expected to be committed by the
+ // onStart() client callback together with client's custom transformation.
+ UIComponent.Transaction transaction = closingApp.newTransaction();
+ if (!maxClosingBounds.isEmpty()) {
+ logD("Applying closing window bounds: " + maxClosingBounds);
+ transaction.setBounds(closingApp, maxClosingBounds);
+ }
+ if (!maxOpeningBounds.isEmpty()) {
+ logD("Applying opening window bounds: " + maxOpeningBounds);
+ transaction.setBounds(openingApp, maxOpeningBounds);
+ }
+ transaction.commit();
+ }
+
+ @Nullable
+ private static Rect getBounds(@Nullable WindowAnimationState state) {
+ if (state == null || state.bounds == null) {
+ return null;
+ }
+ Rect out = new Rect();
+ state.bounds.roundOut(out);
+ return out;
+ }
+
/**
* An interface that represents an origin transitions.
*
@@ -338,9 +400,14 @@
/**
* Called when an origin transition starts. This method exposes the raw {@link
* TransitionInfo} so that clients can extract more information from it.
+ *
+ * <p>Note: if this transition is taking over a predictive back animation, the {@link
+ * WindowAnimationState} will be passed to this method. The concrete implementation is
+ * expected to apply the {@link WindowAnimationState} before continuing the transition.
*/
default void onStart(
TransitionInfo transitionInfo,
+ @Nullable WindowAnimationState[] states,
SurfaceControl.Transaction sfTransaction,
UIComponent origin,
UIComponent.Transaction uiTransaction) {
@@ -351,12 +418,15 @@
.registerTransactionForClass(
SurfaceUIComponent.class,
new SurfaceUIComponent.Transaction(sfTransaction));
- // Wrap surfaces and start.
- onStart(
- transactions,
- origin,
- wrapSurfaces(transitionInfo, /* isOpening= */ false),
- wrapSurfaces(transitionInfo, /* isOpening= */ true));
+ // Wrap surfaces.
+ UIComponent closingApp = wrapSurfaces(transitionInfo, /* isOpening= */ false);
+ UIComponent openingApp = wrapSurfaces(transitionInfo, /* isOpening= */ true);
+
+ // Restore the pending animation states coming from predictive back transition.
+ applyWindowAnimationStates(transitionInfo, states, closingApp, openingApp);
+
+ // Start.
+ onStart(transactions, origin, closingApp, openingApp);
}
/**
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index 9cef43c..cec740a 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -89,7 +89,6 @@
mSurfaceControl =
new SurfaceControl.Builder().setName("ViewUIComponent").setBufferSize(w, h).build();
mSurface = new Surface(mSurfaceControl);
- forceDraw();
// Attach surface to transition leash
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
@@ -99,7 +98,13 @@
mView.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
// Make the view invisible AFTER the surface is shown.
- t.addTransactionCommittedListener(mView::post, () -> mView.setVisibility(View.INVISIBLE))
+ t.addTransactionCommittedListener(
+ mView::post,
+ () -> {
+ logD("Surface attached!");
+ forceDraw();
+ mView.setVisibility(View.INVISIBLE);
+ })
.apply();
}
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
index 3cbb688..6b26ac5 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/server/IOriginTransitionsImpl.java
@@ -16,8 +16,10 @@
package com.android.systemui.animation.server;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -26,6 +28,7 @@
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
@@ -51,8 +54,8 @@
/** An implementation of the {@link IOriginTransitions}. */
public class IOriginTransitionsImpl extends IOriginTransitions.Stub {
- private static final boolean DEBUG = true;
private static final String TAG = "OriginTransitions";
+ private static final boolean DEBUG = Build.IS_USERDEBUG || Log.isLoggable(TAG, Log.DEBUG);
private final Object mLock = new Object();
private final ShellTransitions mShellTransitions;
@@ -149,18 +152,7 @@
if (DEBUG) {
Log.d(TAG, "startAnimation: " + info);
}
- if (!mOnStarting.test(info)) {
- Log.w(TAG, "Skipping cancelled transition " + mTransition);
- t.addTransactionCommittedListener(
- mExecutor,
- () -> {
- try {
- finishCallback.onTransitionFinished(null, null);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to report finish.", e);
- }
- })
- .apply();
+ if (maybeInterceptTransition(info, t, finishCallback)) {
return;
}
mTransition.startAnimation(token, info, t, finishCallback);
@@ -191,6 +183,9 @@
if (DEBUG) {
Log.d(TAG, "takeOverAnimation: " + info);
}
+ if (maybeInterceptTransition(info, t, finishCallback)) {
+ return;
+ }
mTransition.takeOverAnimation(transition, info, t, finishCallback, states);
}
@@ -207,6 +202,27 @@
public String toString() {
return "RemoteTransitionDelegate{transition=" + mTransition + "}";
}
+
+ private boolean maybeInterceptTransition(
+ TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) {
+ if (!mOnStarting.test(info)) {
+ Log.w(TAG, "Intercepting cancelled transition " + mTransition);
+ t.addTransactionCommittedListener(
+ mExecutor,
+ () -> {
+ try {
+ finishCallback.onTransitionFinished(null, null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to report finish.", e);
+ }
+ })
+ .apply();
+ return true;
+ }
+ return false;
+ }
}
/** A data record containing the origin transition pieces. */
@@ -229,13 +245,25 @@
if (mDestroyed) {
return false;
}
- TransitionFilter filter = createFilterForReverseTransition(info);
+ TransitionFilter filter =
+ createFilterForReverseTransition(
+ info, /* forPredictiveBackTakeover= */ false);
if (filter != null) {
if (DEBUG) {
Log.d(TAG, "Registering filter " + filter);
}
mShellTransitions.registerRemote(filter, mWrappedReturnTransition);
}
+ TransitionFilter takeoverFilter =
+ createFilterForReverseTransition(
+ info, /* forPredictiveBackTakeover= */ true);
+ if (takeoverFilter != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Registering filter for takeover " + takeoverFilter);
+ }
+ mShellTransitions.registerRemoteForTakeover(
+ takeoverFilter, mWrappedReturnTransition);
+ }
return true;
}
}
@@ -331,7 +359,8 @@
}
@Nullable
- private static TransitionFilter createFilterForReverseTransition(TransitionInfo info) {
+ private static TransitionFilter createFilterForReverseTransition(
+ TransitionInfo info, boolean forPredictiveBackTakeover) {
TaskInfo launchingTaskInfo = null;
TaskInfo launchedTaskInfo = null;
ComponentName launchingActivity = null;
@@ -365,7 +394,9 @@
if (DEBUG) {
Log.d(
TAG,
- "createFilterForReverseTransition: launchingTaskInfo="
+ "createFilterForReverseTransition: forPredictiveBackTakeover="
+ + forPredictiveBackTakeover
+ + ", launchingTaskInfo="
+ launchingTaskInfo
+ ", launchedTaskInfo="
+ launchedTaskInfo
@@ -395,8 +426,20 @@
+ " cookie!");
return null;
}
+ if (forPredictiveBackTakeover && launchedTaskInfo == null) {
+ // Predictive back take over currently only support cross-task transition.
+ Log.d(
+ TAG,
+ "createFilterForReverseTransition: skipped - unable to find launched task"
+ + " for predictive back takeover");
+ return null;
+ }
TransitionFilter filter = new TransitionFilter();
- filter.mTypeSet = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ if (forPredictiveBackTakeover) {
+ filter.mTypeSet = new int[] {TRANSIT_PREPARE_BACK_NAVIGATION};
+ } else {
+ filter.mTypeSet = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ }
// The opening activity of the return transition must match the activity we just closed.
TransitionFilter.Requirement req1 = new TransitionFilter.Requirement();
@@ -405,15 +448,18 @@
launchingActivity == null ? launchingTaskInfo.topActivity : launchingActivity;
TransitionFilter.Requirement req2 = new TransitionFilter.Requirement();
- req2.mModes = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ if (forPredictiveBackTakeover) {
+ req2.mModes = new int[] {TRANSIT_CHANGE};
+ } else {
+ req2.mModes = new int[] {TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ }
if (launchedTaskInfo != null) {
// For task transitions, the closing task's cookie must match the task we just
// launched.
req2.mLaunchCookie = launchedTaskInfo.launchCookies.get(0);
} else {
// For activity transitions, the closing activity of the return transition must
- // match
- // the activity we just launched.
+ // match the activity we just launched.
req2.mTopActivity = launchedActivity;
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index a266e7e..c3dc84d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -39,6 +39,7 @@
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.compose.animation.scene.SceneTransitions
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.observableTransitionState
@@ -78,6 +79,7 @@
sceneByKey: Map<SceneKey, Scene>,
overlayByKey: Map<OverlayKey, Overlay>,
initialSceneKey: SceneKey,
+ sceneTransitions: SceneTransitions,
dataSourceDelegator: SceneDataSourceDelegator,
qsSceneAdapter: Provider<QSSceneAdapter>,
modifier: Modifier = Modifier,
@@ -87,7 +89,7 @@
MutableSceneTransitionLayoutState(
initialScene = initialSceneKey,
canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
- transitions = SceneContainerTransitions,
+ transitions = sceneTransitions,
)
}
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 cf0ba51..7a8d20a 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
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:Suppress("NOTHING_TO_INLINE")
-
package com.android.compose.animation.scene
import androidx.compose.foundation.gestures.Orientation
@@ -97,68 +95,11 @@
internal val positionalThreshold
get() = with(layoutImpl.density) { 56.dp.toPx() }
- /**
- * Whether we should immediately intercept a gesture.
- *
- * Note: if this returns true, then [onDragStarted] will be called with overSlop equal to 0f,
- * indicating that the transition should be intercepted.
- */
- internal fun shouldImmediatelyIntercept(pointersDown: PointersInfo.PointersDown?): Boolean {
- // We don't intercept the touch if we are not currently driving the transition.
- val dragController = dragController
- if (dragController?.isDrivingTransition != true) {
- return false
- }
-
- val swipeAnimation = dragController.swipeAnimation
-
- // Only intercept the current transition if one of the 2 swipes results is also a transition
- // between the same pair of contents.
- val swipes = computeSwipes(pointersDown)
- val fromContent = layoutImpl.content(swipeAnimation.currentContent)
- val (upOrLeft, downOrRight) = swipes.computeSwipesResults(fromContent)
- val currentScene = layoutImpl.state.currentScene
- val contentTransition = swipeAnimation.contentTransition
- return (upOrLeft != null &&
- contentTransition.isTransitioningBetween(
- fromContent.key,
- upOrLeft.toContent(currentScene),
- )) ||
- (downOrRight != null &&
- contentTransition.isTransitioningBetween(
- fromContent.key,
- downOrRight.toContent(currentScene),
- ))
- }
-
override fun onDragStarted(
pointersDown: PointersInfo.PointersDown?,
overSlop: Float,
): DragController {
- if (overSlop == 0f) {
- val oldDragController = dragController
- check(oldDragController != null && oldDragController.isDrivingTransition) {
- val isActive = oldDragController?.isDrivingTransition
- "onDragStarted(overSlop=0f) requires an active dragController, but was $isActive"
- }
-
- // This [transition] was already driving the animation: simply take over it.
- // Stop animating and start from the current offset.
- val oldSwipeAnimation = oldDragController.swipeAnimation
-
- // We need to recompute the swipe results since this is a new gesture, and the
- // fromScene.userActions may have changed.
- val swipes = oldDragController.swipes
- swipes.updateSwipesResults(
- fromContent = layoutImpl.content(oldSwipeAnimation.fromContent)
- )
-
- // A new gesture should always create a new SwipeAnimation. This way there cannot be
- // different gestures controlling the same transition.
- val swipeAnimation = createSwipeAnimation(oldSwipeAnimation)
- return updateDragController(swipes, swipeAnimation)
- }
-
+ check(overSlop != 0f)
val swipes = computeSwipes(pointersDown)
val fromContent = layoutImpl.contentForUserActions()
@@ -196,7 +137,7 @@
return createSwipeAnimation(layoutImpl, result, isUpOrLeft, orientation)
}
- internal fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
+ private fun resolveSwipeSource(startedPosition: Offset): SwipeSource.Resolved? {
return layoutImpl.swipeSourceDetector.source(
layoutSize = layoutImpl.lastSize,
position = startedPosition.round(),
@@ -291,71 +232,25 @@
return 0f
}
- val toContent = swipeAnimation.toContent
val distance = swipeAnimation.distance()
val previousOffset = swipeAnimation.dragOffset
val desiredOffset = previousOffset + delta
+ val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
- fun hasReachedToSceneUpOrLeft() =
- distance < 0 &&
- desiredOffset <= distance &&
- swipes.upOrLeftResult?.toContent(layoutState.currentScene) == toContent
+ // Note: the distance could be negative if fromContent is above or to the left of
+ // toContent.
+ val newOffset =
+ when {
+ distance == DistanceUnspecified ||
+ swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
+ desiredOffset
+ distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
+ else -> desiredOffset.fastCoerceIn(distance, 0f)
+ }
- fun hasReachedToSceneDownOrRight() =
- distance > 0 &&
- desiredOffset >= distance &&
- swipes.downOrRightResult?.toContent(layoutState.currentScene) == toContent
+ val consumedDelta = newOffset - previousOffset
- // Considering accelerated swipe: Change fromContent in the case where the user quickly
- // swiped multiple times in the same direction to accelerate the transition from A => B then
- // B => C.
- //
- // TODO(b/290184746): the second drag needs to pass B to work. Add support for flinging
- // twice before B has been reached
- val hasReachedToContent =
- swipeAnimation.currentContent == toContent &&
- (hasReachedToSceneUpOrLeft() || hasReachedToSceneDownOrRight())
-
- val fromContent: ContentKey
- val currentTransitionOffset: Float
- val newOffset: Float
- val consumedDelta: Float
- if (hasReachedToContent) {
- // The new transition will start from the current toContent.
- fromContent = toContent
-
- // The current transition is completed (we have reached the full swipe distance).
- currentTransitionOffset = distance
-
- // The next transition will start with the remaining offset.
- newOffset = desiredOffset - distance
- consumedDelta = delta
- } else {
- fromContent = swipeAnimation.fromContent
- val desiredProgress = swipeAnimation.computeProgress(desiredOffset)
-
- // Note: the distance could be negative if fromContent is above or to the left of
- // toContent.
- currentTransitionOffset =
- when {
- distance == DistanceUnspecified ||
- swipeAnimation.contentTransition.isWithinProgressRange(desiredProgress) ->
- desiredOffset
-
- distance > 0f -> desiredOffset.fastCoerceIn(0f, distance)
- else -> desiredOffset.fastCoerceIn(distance, 0f)
- }
-
- // If there is a new transition, we will use the same offset
- newOffset = currentTransitionOffset
- consumedDelta = newOffset - previousOffset
- }
-
- swipeAnimation.dragOffset = currentTransitionOffset
-
- if (hasReachedToContent) {
- swipes.updateSwipesResults(draggableHandler.layoutImpl.content(fromContent))
- }
+ swipeAnimation.dragOffset = newOffset
val result = swipes.findUserActionResult(directionOffset = newOffset)
if (result == null) {
@@ -372,13 +267,12 @@
val needNewTransition =
!currentTransitionIrreversible &&
- (hasReachedToContent ||
- result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
+ (result.toContent(layoutState.currentScene) != swipeAnimation.toContent ||
result.transitionKey != swipeAnimation.contentTransition.key)
if (needNewTransition) {
// Make sure the current transition will finish to the right current scene.
- swipeAnimation.currentContent = fromContent
+ swipeAnimation.currentContent = swipeAnimation.fromContent
val newSwipeAnimation = draggableHandler.createSwipeAnimation(swipes, result)
newSwipeAnimation.dragOffset = newOffset
@@ -499,7 +393,9 @@
var upOrLeftResult: UserActionResult? = null
var downOrRightResult: UserActionResult? = null
- fun computeSwipesResults(fromContent: Content): Pair<UserActionResult?, UserActionResult?> {
+ private fun computeSwipesResults(
+ fromContent: Content
+ ): Pair<UserActionResult?, UserActionResult?> {
val upOrLeftResult = fromContent.findActionResultBestMatch(swipe = upOrLeft)
val downOrRightResult = fromContent.findActionResultBestMatch(swipe = downOrRight)
return upOrLeftResult to downOrRightResult
@@ -564,48 +460,9 @@
.shouldEnableSwipes(draggableHandler.orientation)
}
- var isIntercepting = false
-
return PriorityNestedScrollConnection(
orientation = draggableHandler.orientation,
- canStartPreScroll = { offsetAvailable, offsetBeforeStart, _ ->
- val pointersDown: PointersInfo.PointersDown? =
- when (val info = pointersInfoOwner.pointersInfo()) {
- PointersInfo.MouseWheel -> {
- // Do not support mouse wheel interactions
- return@PriorityNestedScrollConnection false
- }
-
- is PointersInfo.PointersDown -> info
- null -> null
- }
-
- canChangeScene =
- if (isExternalOverscrollGesture()) false else offsetBeforeStart == 0f
-
- val canInterceptSwipeTransition =
- canChangeScene &&
- offsetAvailable != 0f &&
- draggableHandler.shouldImmediatelyIntercept(pointersDown)
- if (!canInterceptSwipeTransition) return@PriorityNestedScrollConnection false
-
- val layoutImpl = draggableHandler.layoutImpl
- val threshold = layoutImpl.transitionInterceptionThreshold
- val hasSnappedToIdle = layoutImpl.state.snapToIdleIfClose(threshold)
- if (hasSnappedToIdle) {
- // If the current swipe transition is closed to 0f or 1f, then we want to
- // interrupt the transition (snapping it to Idle) and scroll the list.
- return@PriorityNestedScrollConnection false
- }
-
- lastPointersDown = pointersDown
-
- // If the current swipe transition is *not* closed to 0f or 1f, then we want the
- // scroll events to intercept the current transition to continue the scene
- // transition.
- isIntercepting = true
- true
- },
+ canStartPreScroll = { _, _, _ -> false },
canStartPostScroll = { offsetAvailable, offsetBeforeStart, _ ->
val behavior: NestedScrollBehavior =
when {
@@ -629,29 +486,22 @@
}
lastPointersDown = pointersDown
- val canStart =
- when (behavior) {
- NestedScrollBehavior.EdgeNoPreview -> {
- canChangeScene = isZeroOffset
- isZeroOffset && shouldEnableSwipes()
- }
-
- NestedScrollBehavior.EdgeWithPreview -> {
- canChangeScene = isZeroOffset
- shouldEnableSwipes()
- }
-
- NestedScrollBehavior.EdgeAlways -> {
- canChangeScene = true
- shouldEnableSwipes()
- }
+ when (behavior) {
+ NestedScrollBehavior.EdgeNoPreview -> {
+ canChangeScene = isZeroOffset
+ isZeroOffset && shouldEnableSwipes()
}
- if (canStart) {
- isIntercepting = false
- }
+ NestedScrollBehavior.EdgeWithPreview -> {
+ canChangeScene = isZeroOffset
+ shouldEnableSwipes()
+ }
- canStart
+ NestedScrollBehavior.EdgeAlways -> {
+ canChangeScene = true
+ shouldEnableSwipes()
+ }
+ }
},
canStartPostFling = { velocityAvailable ->
val behavior: NestedScrollBehavior =
@@ -676,19 +526,14 @@
}
lastPointersDown = pointersDown
- val canStart = behavior.canStartOnPostFling && shouldEnableSwipes()
- if (canStart) {
- isIntercepting = false
- }
-
- canStart
+ behavior.canStartOnPostFling && shouldEnableSwipes()
},
onStart = { firstScroll ->
scrollController(
dragController =
draggableHandler.onDragStarted(
pointersDown = lastPointersDown,
- overSlop = if (isIntercepting) 0f else firstScroll,
+ overSlop = firstScroll,
),
canChangeScene = canChangeScene,
pointersInfoOwner = pointersInfoOwner,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 59ac68b..f5f01d4 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -80,7 +80,6 @@
@Stable
internal fun Modifier.multiPointerDraggable(
orientation: Orientation,
- startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onFirstPointerDown: () -> Unit = {},
swipeDetector: SwipeDetector = DefaultSwipeDetector,
@@ -89,7 +88,6 @@
this.then(
MultiPointerDraggableElement(
orientation,
- startDragImmediately,
onDragStarted,
onFirstPointerDown,
swipeDetector,
@@ -99,7 +97,6 @@
private data class MultiPointerDraggableElement(
private val orientation: Orientation,
- private val startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
private val onDragStarted:
(pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
private val onFirstPointerDown: () -> Unit,
@@ -109,7 +106,6 @@
override fun create(): MultiPointerDraggableNode =
MultiPointerDraggableNode(
orientation = orientation,
- startDragImmediately = startDragImmediately,
onDragStarted = onDragStarted,
onFirstPointerDown = onFirstPointerDown,
swipeDetector = swipeDetector,
@@ -118,7 +114,6 @@
override fun update(node: MultiPointerDraggableNode) {
node.orientation = orientation
- node.startDragImmediately = startDragImmediately
node.onDragStarted = onDragStarted
node.onFirstPointerDown = onFirstPointerDown
node.swipeDetector = swipeDetector
@@ -127,16 +122,11 @@
internal class MultiPointerDraggableNode(
orientation: Orientation,
- var startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
var onDragStarted: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
var onFirstPointerDown: () -> Unit,
swipeDetector: SwipeDetector = DefaultSwipeDetector,
private val dispatcher: NestedScrollDispatcher,
-) :
- DelegatingNode(),
- PointerInputModifierNode,
- CompositionLocalConsumerModifierNode,
- SpaceVectorConverter {
+) : DelegatingNode(), PointerInputModifierNode, CompositionLocalConsumerModifierNode {
private val pointerTracker = delegate(SuspendingPointerInputModifierNode { pointerTracker() })
private val pointerInput = delegate(SuspendingPointerInputModifierNode { pointerInput() })
private val velocityTracker = VelocityTracker()
@@ -151,13 +141,13 @@
private var converter = SpaceVectorConverter(orientation)
- override fun Offset.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
+ fun Offset.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
- override fun Velocity.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
+ fun Velocity.toFloat(): Float = with(converter) { this@toFloat.toFloat() }
- override fun Float.toOffset(): Offset = with(converter) { this@toOffset.toOffset() }
+ fun Float.toOffset(): Offset = with(converter) { this@toOffset.toOffset() }
- override fun Float.toVelocity(): Velocity = with(converter) { this@toVelocity.toVelocity() }
+ fun Float.toVelocity(): Velocity = with(converter) { this@toVelocity.toVelocity() }
var orientation: Orientation = orientation
set(value) {
@@ -297,7 +287,6 @@
try {
detectDragGestures(
orientation = orientation,
- startDragImmediately = startDragImmediately,
onDragStart = { pointersDown, overSlop ->
onDragStarted(pointersDown, overSlop)
},
@@ -438,13 +427,11 @@
* Detect drag gestures in the given [orientation].
*
* This function is a mix of [androidx.compose.foundation.gestures.awaitDownAndSlop] and
- * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for:
- * 1) starting the gesture immediately without requiring a drag >= touch slope;
- * 2) passing the number of pointers down to [onDragStart].
+ * [androidx.compose.foundation.gestures.detectVerticalDragGestures] to add support for passing
+ * the number of pointers down to [onDragStart].
*/
private suspend fun AwaitPointerEventScope.detectDragGestures(
orientation: Orientation,
- startDragImmediately: (pointersDown: PointersInfo.PointersDown) -> Boolean,
onDragStart: (pointersDown: PointersInfo.PointersDown, overSlop: Float) -> DragController,
onDrag: (controller: DragController, dragAmount: Float) -> Unit,
onDragEnd: (controller: DragController) -> Unit,
@@ -470,71 +457,49 @@
.first()
var overSlop = 0f
- var lastPointersDown: PointersInfo.PointersDown =
+ val onSlopReached = { change: PointerInputChange, over: Float ->
+ if (swipeDetector.detectSwipe(change)) {
+ change.consume()
+ overSlop = over
+ }
+ }
+
+ // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
+ // is public.
+ val drag =
+ when (orientation) {
+ Orientation.Horizontal ->
+ awaitHorizontalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
+ Orientation.Vertical ->
+ awaitVerticalTouchSlopOrCancellation(consumablePointer.id, onSlopReached)
+ } ?: return
+
+ val lastPointersDown =
checkNotNull(pointersInfo()) {
"We should have pointers down, last event: $currentEvent"
}
as PointersInfo.PointersDown
-
- val drag =
- if (startDragImmediately(lastPointersDown)) {
- consumablePointer.consume()
- consumablePointer
- } else {
- val onSlopReached = { change: PointerInputChange, over: Float ->
- if (swipeDetector.detectSwipe(change)) {
- change.consume()
- overSlop = over
- }
+ // Make sure that overSlop is not 0f. This can happen when the user drags by exactly
+ // the touch slop. However, the overSlop we pass to onDragStarted() is used to
+ // compute the direction we are dragging in, so overSlop should never be 0f.
+ if (overSlop == 0f) {
+ // If the user drags in the opposite direction, the delta becomes zero because
+ // we return to the original point. Therefore, we should use the previous event
+ // to calculate the direction.
+ val delta = (drag.position - drag.previousPosition).toFloat()
+ check(delta != 0f) {
+ buildString {
+ append("delta is equal to 0 ")
+ append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
+ append("consumablePointer.position ${consumablePointer.position} ")
+ append("drag.position ${drag.position} ")
+ append("drag.previousPosition ${drag.previousPosition}")
}
-
- // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
- // is public.
- val drag =
- when (orientation) {
- Orientation.Horizontal ->
- awaitHorizontalTouchSlopOrCancellation(
- consumablePointer.id,
- onSlopReached,
- )
- Orientation.Vertical ->
- awaitVerticalTouchSlopOrCancellation(
- consumablePointer.id,
- onSlopReached,
- )
- } ?: return
-
- lastPointersDown =
- checkNotNull(pointersInfo()) {
- "We should have pointers down, last event: $currentEvent"
- }
- as PointersInfo.PointersDown
- // Make sure that overSlop is not 0f. This can happen when the user drags by exactly
- // the touch slop. However, the overSlop we pass to onDragStarted() is used to
- // compute the direction we are dragging in, so overSlop should never be 0f unless
- // we intercept an ongoing swipe transition (i.e. startDragImmediately() returned
- // true).
- if (overSlop == 0f) {
- // If the user drags in the opposite direction, the delta becomes zero because
- // we return to the original point. Therefore, we should use the previous event
- // to calculate the direction.
- val delta = (drag.position - drag.previousPosition).toFloat()
- check(delta != 0f) {
- buildString {
- append("delta is equal to 0 ")
- append("touchSlop ${currentValueOf(LocalViewConfiguration).touchSlop} ")
- append("consumablePointer.position ${consumablePointer.position} ")
- append("drag.position ${drag.position} ")
- append("drag.previousPosition ${drag.previousPosition}")
- }
- }
- overSlop = delta.sign
- }
- drag
}
+ overSlop = delta.sign
+ }
val controller = onDragStart(lastPointersDown, overSlop)
-
val successful: Boolean
try {
onDrag(controller, overSlop)
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index a91a505..759100b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -276,6 +276,7 @@
)
}
+@Deprecated("Use ContentScope instead", ReplaceWith("ContentScope"))
typealias SceneScope = ContentScope
@Stable
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 5ab306a..6ef8b86 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -149,7 +149,6 @@
delegate(
MultiPointerDraggableNode(
orientation = draggableHandler.orientation,
- startDragImmediately = ::startDragImmediately,
onDragStarted = draggableHandler::onDragStarted,
onFirstPointerDown = ::onFirstPointerDown,
swipeDetector = swipeDetector,
@@ -198,21 +197,6 @@
) = multiPointerDraggableNode.onPointerEvent(pointerEvent, pass, bounds)
override fun onCancelPointerInput() = multiPointerDraggableNode.onCancelPointerInput()
-
- private fun startDragImmediately(pointersDown: PointersInfo.PointersDown): Boolean {
- // Immediately start the drag if the user can't swipe in the other direction and the gesture
- // handler can intercept it.
- return !canOppositeSwipe() && draggableHandler.shouldImmediatelyIntercept(pointersDown)
- }
-
- private fun canOppositeSwipe(): Boolean {
- val oppositeOrientation =
- when (draggableHandler.orientation) {
- Orientation.Vertical -> Orientation.Horizontal
- Orientation.Horizontal -> Orientation.Vertical
- }
- return draggableHandler.contentForSwipes().shouldEnableSwipes(oppositeOrientation)
- }
}
/** Find the [ScrollBehaviorOwner] for the current orientation. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
index f08a180..ca50e77 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
@@ -18,6 +18,7 @@
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
interface SpaceVectorConverter {
@@ -25,9 +26,13 @@
fun Velocity.toFloat(): Float
+ fun IntOffset.toInt(): Int
+
fun Float.toOffset(): Offset
fun Float.toVelocity(): Velocity
+
+ fun Int.toIntOffset(): IntOffset
}
fun SpaceVectorConverter(orientation: Orientation) =
@@ -36,24 +41,30 @@
Orientation.Vertical -> VerticalConverter
}
-private val HorizontalConverter =
- object : SpaceVectorConverter {
- override fun Offset.toFloat() = x
+private data object HorizontalConverter : SpaceVectorConverter {
+ override fun Offset.toFloat() = x
- override fun Velocity.toFloat() = x
+ override fun Velocity.toFloat() = x
- override fun Float.toOffset() = Offset(this, 0f)
+ override fun IntOffset.toInt() = x
- override fun Float.toVelocity() = Velocity(this, 0f)
- }
+ override fun Float.toOffset() = Offset(this, 0f)
-private val VerticalConverter =
- object : SpaceVectorConverter {
- override fun Offset.toFloat() = y
+ override fun Float.toVelocity() = Velocity(this, 0f)
- override fun Velocity.toFloat() = y
+ override fun Int.toIntOffset() = IntOffset(this, 0)
+}
- override fun Float.toOffset() = Offset(0f, this)
+private data object VerticalConverter : SpaceVectorConverter {
+ override fun Offset.toFloat() = y
- override fun Float.toVelocity() = Velocity(0f, this)
- }
+ override fun Velocity.toFloat() = y
+
+ override fun IntOffset.toInt() = y
+
+ override fun Float.toOffset() = Offset(0f, this)
+
+ override fun Float.toVelocity() = Velocity(0f, this)
+
+ override fun Int.toIntOffset() = IntOffset(0, this)
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
index a1077cf..394568d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/DraggableHandlerTest.kt
@@ -234,12 +234,6 @@
)
}
- fun onDragStartedImmediately(
- pointersInfo: PointersInfo.PointersDown = pointersDown()
- ): DragController {
- return onDragStarted(draggableHandler, pointersInfo, overSlop = 0f)
- }
-
fun onDragStarted(
draggableHandler: DraggableHandler,
pointersInfo: PointersInfo.PointersDown = pointersDown(),
@@ -602,82 +596,6 @@
}
@Test
- fun onAcceleratedScroll_scrollToThirdScene() = runGestureTest {
- // Drag A -> B with progress 0.2
- val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
- assertTransition(
- currentScene = SceneA,
- fromScene = SceneA,
- toScene = SceneB,
- progress = 0.2f,
- )
-
- // Start animation A -> B with progress 0.2 -> 1.0
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- // While at A -> B do a 100% screen drag (progress 1.2). This should go past B and change
- // the transition to B -> C with progress 0.2
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 1f))
- assertTransition(
- currentScene = SceneB,
- fromScene = SceneB,
- toScene = SceneC,
- progress = 0.2f,
- )
-
- // After the drag stopped scene C should be committed
- dragController2.onDragStoppedAnimateNow(
- velocity = -velocityThreshold,
- onAnimationStart = {
- assertTransition(currentScene = SceneC, fromScene = SceneB, toScene = SceneC)
- },
- expectedConsumedVelocity = -velocityThreshold,
- )
- assertIdle(currentScene = SceneC)
- }
-
- @Test
- fun onAcceleratedScrollBothTargetsBecomeNull_settlesToIdle() = runGestureTest {
- val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.2f))
- dragController1.onDragDelta(pixels = up(fractionOfScreen = 0.2f))
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- mutableUserActionsA = emptyMap()
- mutableUserActionsB = emptyMap()
-
- // start acceleratedScroll and scroll over to B -> null
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
-
- // here onDragStopped is already triggered, but subsequent onDelta/onDragStopped calls may
- // still be called. Make sure that they don't crash or change the scene
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = {
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
- },
- expectedConsumedVelocity = 0f,
- )
-
- advanceUntilIdle()
- assertIdle(SceneB)
-
- // These events can still come in after the animation has settled
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.5f), expectedConsumed = 0f)
- dragController2.onDragStoppedAnimateNow(
- velocity = 0f,
- onAnimationStart = { assertIdle(SceneB) },
- expectedConsumedVelocity = 0f,
- )
- assertIdle(SceneB)
- }
-
- @Test
fun onDragTargetsChanged_targetStaysTheSame() = runGestureTest {
val dragController1 = onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertTransition(fromScene = SceneA, toScene = SceneB, progress = 0.1f)
@@ -711,9 +629,8 @@
dragController1.onDragStoppedAnimateLater(velocity = down(fractionOfScreen = 0.1f))
// now target changed to C for new drag that started before previous drag settled to Idle
- val dragController2 = onDragStartedImmediately()
- dragController2.onDragDelta(pixels = up(fractionOfScreen = 0.1f))
- assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.3f)
+ onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
+ assertTransition(fromScene = SceneA, toScene = SceneC, progress = 0.1f)
}
@Test
@@ -728,7 +645,7 @@
assertThat(isUserInputOngoing).isFalse()
// Start a new gesture while the offset is animating
- onDragStartedImmediately()
+ onDragStarted(overSlop = up(fractionOfScreen = 0.1f))
assertThat(isUserInputOngoing).isTrue()
}
@@ -812,36 +729,6 @@
}
@Test
- fun scrollAndFling_scrollLessThanInterceptable_goToIdleOnCurrentScene() = runGestureTest {
- val firstScroll = (transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE
- val secondScroll = 1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertIdle(SceneA)
- }
-
- @Test
- fun scrollAndFling_scrollMinInterceptable_interceptPreScrollEvents() = runGestureTest {
- val firstScroll = (transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE
- val secondScroll = 1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertTransition(progress = (firstScroll + secondScroll) / SCREEN_SIZE)
- }
-
- @Test
- fun scrollAndFling_scrollMaxInterceptable_interceptPreScrollEvents() = runGestureTest {
- val firstScroll = -(1f - transitionInterceptionThreshold - 0.0001f) * SCREEN_SIZE
- val secondScroll = -1f
-
- preScrollAfterSceneTransition(firstScroll = firstScroll, secondScroll = secondScroll)
-
- assertTransition(progress = -(firstScroll + secondScroll) / SCREEN_SIZE)
- }
-
- @Test
fun scrollAndFling_scrollMoreThanInterceptable_goToIdleOnNextScene() = runGestureTest {
val firstScroll = -(1f - transitionInterceptionThreshold + 0.0001f) * SCREEN_SIZE
val secondScroll = -0.01f
@@ -1025,7 +912,7 @@
// now we can intercept the scroll events
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.2f)
+ assertThat(progress).isEqualTo(0.1f)
// this should be ignored, we are scrolling now!
dragController.onDragStoppedAnimateNow(
@@ -1036,10 +923,10 @@
assertTransition(currentScene = SceneA)
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.3f)
+ assertThat(progress).isEqualTo(0.2f)
nestedScroll.scroll(available = -offsetY10)
- assertThat(progress).isEqualTo(0.4f)
+ assertThat(progress).isEqualTo(0.3f)
nestedScroll.preFling(available = Velocity(0f, -velocityThreshold))
assertTransition(currentScene = SceneB)
@@ -1050,57 +937,6 @@
}
@Test
- fun interceptTransition() = runGestureTest {
- // Start at scene C.
- navigateToSceneC()
-
- // Swipe up from the middle to transition to scene B.
- val middle = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2f, SCREEN_SIZE / 2f))
- onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.1f,
- isUserInputOngoing = true,
- )
-
- val firstTransition = transitionState
-
- // During the current gesture, start a new gesture, still in the middle of the screen. We
- // should intercept it. Because it is intercepted, the overSlop passed to onDragStarted()
- // should be 0f.
- assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
- onDragStartedImmediately(pointersInfo = middle)
-
- // We should have intercepted the transition, so the transition should be the same object.
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneB,
- progress = 0.1f,
- isUserInputOngoing = true,
- )
- // We should have a new transition
- assertThat(transitionState).isNotSameInstanceAs(firstTransition)
-
- // Start a new gesture from the bottom of the screen. Because swiping up from the bottom of
- // C leads to scene A (and not B), the previous transitions is *not* intercepted and we
- // instead animate from C to A.
- val bottom = pointersDown(startedPosition = Offset(SCREEN_SIZE / 2, SCREEN_SIZE))
- assertThat(draggableHandler.shouldImmediatelyIntercept(bottom)).isFalse()
- onDragStarted(pointersInfo = bottom, overSlop = up(0.1f))
-
- assertTransition(
- currentScene = SceneC,
- fromScene = SceneC,
- toScene = SceneA,
- isUserInputOngoing = true,
- )
- assertThat(transitionState).isNotSameInstanceAs(firstTransition)
- }
-
- @Test
fun freezeAndAnimateToCurrentState() = runGestureTest {
// Start at scene C.
navigateToSceneC()
@@ -1110,9 +946,6 @@
onDragStarted(pointersInfo = middle, overSlop = up(0.1f))
assertTransition(fromScene = SceneC, toScene = SceneB, isUserInputOngoing = true)
- // The current transition can be intercepted.
- assertThat(draggableHandler.shouldImmediatelyIntercept(middle)).isTrue()
-
// Freeze the transition.
val transition = transitionState as Transition
transition.freezeAndAnimateToCurrentState()
@@ -1123,19 +956,6 @@
}
@Test
- fun interruptedTransitionCanNotBeImmediatelyIntercepted() = runGestureTest {
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
- onDragStarted(overSlop = up(0.1f))
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
-
- layoutState.startTransitionImmediately(
- animationScope = testScope.backgroundScope,
- transition(SceneA, SceneB),
- )
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isFalse()
- }
-
- @Test
fun blockTransition() = runGestureTest {
assertIdle(SceneA)
@@ -1154,30 +974,6 @@
}
@Test
- fun blockInterceptedTransition() = runGestureTest {
- assertIdle(SceneA)
-
- // Swipe up to B.
- val dragController1 = onDragStarted(overSlop = up(0.1f))
- assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
- dragController1.onDragStoppedAnimateLater(velocity = -velocityThreshold)
- assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
-
- // Intercept the transition and swipe down back to scene A.
- assertThat(draggableHandler.shouldImmediatelyIntercept(pointersDown = null)).isTrue()
- val dragController2 = onDragStartedImmediately()
-
- // Block the transition when the user release their finger.
- canChangeScene = { false }
- dragController2.onDragStoppedAnimateNow(
- velocity = velocityThreshold,
- onAnimationStart = { assertTransition(fromScene = SceneA, toScene = SceneB) },
- expectedConsumedVelocity = velocityThreshold,
- )
- assertIdle(SceneB)
- }
-
- @Test
fun scrollFromIdleWithNoTargetScene_shouldUseOverscrollSpecIfAvailable() = runGestureTest {
layoutState.transitions = transitions {
overscroll(SceneC, Orientation.Vertical) { fade(TestElements.Foo) }
@@ -1531,25 +1327,6 @@
}
@Test
- fun interceptingTransitionKeepsDistance() = runGestureTest {
- var swipeDistance = 75f
- layoutState.transitions = transitions {
- from(SceneA, to = SceneB) { distance = UserActionDistance { _, _, _ -> swipeDistance } }
- }
-
- // Start transition.
- val controller = onDragStarted(overSlop = -50f)
- assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
-
- // Intercept the transition and change the swipe distance. The original distance and
- // progress should be the same.
- swipeDistance = 50f
- controller.onDragStoppedAnimateLater(0f)
- onDragStartedImmediately()
- assertTransition(fromScene = SceneA, toScene = SceneB, progress = 50f / 75f)
- }
-
- @Test
fun requireFullDistanceSwipe() = runGestureTest {
mutableUserActionsA +=
Swipe.Up to UserActionResult(SceneB, requiresFullDistanceSwipe = true)
@@ -1579,19 +1356,6 @@
}
@Test
- fun interceptingTransitionReplacesCurrentTransition() = runGestureTest {
- val controller = onDragStarted(overSlop = up(fractionOfScreen = 0.5f))
- val transition = assertThat(layoutState.transitionState).isSceneTransition()
- controller.onDragStoppedAnimateLater(velocity = 0f)
-
- // Intercept the transition.
- onDragStartedImmediately()
- val newTransition = assertThat(layoutState.transitionState).isSceneTransition()
- assertThat(newTransition).isNotSameInstanceAs(transition)
- assertThat(newTransition.replacedTransition).isSameInstanceAs(transition)
- }
-
- @Test
fun showOverlay() = runGestureTest {
mutableUserActionsA = mapOf(Swipe.Down to UserActionResult.ShowOverlay(OverlayA))
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index cb3e433..4153350 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -101,7 +101,6 @@
.thenIf(enabled) {
Modifier.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -169,8 +168,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- // We want to start a drag gesture immediately
- startDragImmediately = { true },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -242,7 +239,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -361,7 +357,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
started = true
SimpleDragController(
@@ -466,7 +461,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
verticalStarted = true
SimpleDragController(
@@ -478,7 +472,6 @@
)
.multiPointerDraggable(
orientation = Orientation.Horizontal,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
horizontalStarted = true
SimpleDragController(
@@ -570,7 +563,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
swipeDetector =
object : SwipeDetector {
override fun detectSwipe(change: PointerInputChange): Boolean {
@@ -636,7 +628,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
swipeDetector =
object : SwipeDetector {
override fun detectSwipe(change: PointerInputChange): Boolean {
@@ -738,7 +729,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { consumedOnDrag = it },
@@ -809,7 +799,6 @@
.nestedScrollDispatcher()
.multiPointerDraggable(
orientation = Orientation.Vertical,
- startDragImmediately = { false },
onDragStarted = { _, _ ->
SimpleDragController(
onDrag = { /* do nothing */ },
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
index 34c4dfb..48af2d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderClient.kt
@@ -25,6 +25,7 @@
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.net.Uri
+import android.os.Bundle
import android.util.Log
import androidx.annotation.DrawableRes
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
@@ -51,10 +52,7 @@
* selected affordances on the slot will move the selected affordance to the newest location in
* the slot.
*/
- suspend fun insertSelection(
- slotId: String,
- affordanceId: String,
- )
+ suspend fun insertSelection(slotId: String, affordanceId: String)
/** Returns all available slots supported by the device. */
suspend fun querySlots(): List<Slot>
@@ -63,6 +61,11 @@
suspend fun queryFlags(): List<Flag>
/**
+ * Returns [Bundle] where the keys are from [CustomizationProviderContract.RuntimeValuesTable]
+ */
+ suspend fun queryRuntimeValues(): Bundle
+
+ /**
* Returns [Flow] for observing the collection of slots.
*
* @see [querySlots]
@@ -77,6 +80,13 @@
fun observeFlags(): Flow<List<Flag>>
/**
+ * Returns [Flow] for observing the variables from the System UI.
+ *
+ * @see [queryRuntimeValues]
+ */
+ fun observeRuntimeValues(): Flow<Bundle>
+
+ /**
* Returns all available affordances supported by the device, regardless of current slot
* placement.
*/
@@ -100,15 +110,10 @@
fun observeSelections(): Flow<List<Selection>>
/** Unselects an affordance with the given ID from the slot with the given ID. */
- suspend fun deleteSelection(
- slotId: String,
- affordanceId: String,
- )
+ suspend fun deleteSelection(slotId: String, affordanceId: String)
/** Unselects all affordances from the slot with the given ID. */
- suspend fun deleteAllSelections(
- slotId: String,
- )
+ suspend fun deleteAllSelections(slotId: String)
/** Returns a [Drawable] with the given ID, loaded from the system UI package. */
suspend fun getAffordanceIcon(
@@ -200,10 +205,7 @@
private val backgroundDispatcher: CoroutineDispatcher,
) : CustomizationProviderClient {
- override suspend fun insertSelection(
- slotId: String,
- affordanceId: String,
- ) {
+ override suspend fun insertSelection(slotId: String, affordanceId: String) {
withContext(backgroundDispatcher) {
context.contentResolver.insert(
Contract.LockScreenQuickAffordances.SelectionTable.URI,
@@ -211,9 +213,9 @@
put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
put(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
- affordanceId
+ affordanceId,
)
- }
+ },
)
}
}
@@ -221,13 +223,7 @@
override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
return withContext(backgroundDispatcher) {
context.contentResolver
- .query(
- Contract.LockScreenQuickAffordances.SlotTable.URI,
- null,
- null,
- null,
- null,
- )
+ .query(Contract.LockScreenQuickAffordances.SlotTable.URI, null, null, null, null)
?.use { cursor ->
buildList {
val idColumnIndex =
@@ -252,42 +248,55 @@
}
}
}
- }
- ?: emptyList()
+ } ?: emptyList()
}
override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
return withContext(backgroundDispatcher) {
- context.contentResolver
- .query(
- Contract.FlagsTable.URI,
- null,
- null,
- null,
- null,
- )
- ?.use { cursor ->
- buildList {
+ context.contentResolver.query(Contract.FlagsTable.URI, null, null, null, null)?.use {
+ cursor ->
+ buildList {
+ val nameColumnIndex = cursor.getColumnIndex(Contract.FlagsTable.Columns.NAME)
+ val valueColumnIndex = cursor.getColumnIndex(Contract.FlagsTable.Columns.VALUE)
+ if (nameColumnIndex == -1 || valueColumnIndex == -1) {
+ return@buildList
+ }
+
+ while (cursor.moveToNext()) {
+ add(
+ CustomizationProviderClient.Flag(
+ name = cursor.getString(nameColumnIndex),
+ value = cursor.getInt(valueColumnIndex) == 1,
+ )
+ )
+ }
+ }
+ }
+ } ?: emptyList()
+ }
+
+ override suspend fun queryRuntimeValues(): Bundle {
+ return withContext(backgroundDispatcher) {
+ Bundle().apply {
+ context.contentResolver
+ .query(Contract.RuntimeValuesTable.URI, null, null, null, null)
+ ?.use { cursor ->
val nameColumnIndex =
cursor.getColumnIndex(Contract.FlagsTable.Columns.NAME)
val valueColumnIndex =
cursor.getColumnIndex(Contract.FlagsTable.Columns.VALUE)
- if (nameColumnIndex == -1 || valueColumnIndex == -1) {
- return@buildList
- }
-
- while (cursor.moveToNext()) {
- add(
- CustomizationProviderClient.Flag(
- name = cursor.getString(nameColumnIndex),
- value = cursor.getInt(valueColumnIndex) == 1,
- )
- )
+ if (nameColumnIndex >= 0 && valueColumnIndex >= 0) {
+ while (cursor.moveToNext()) {
+ when (val name = cursor.getString(nameColumnIndex)) {
+ Contract.RuntimeValuesTable.KEY_IS_SHADE_LAYOUT_WIDE -> {
+ putBoolean(name, cursor.getInt(valueColumnIndex) == 1)
+ }
+ }
+ }
}
}
- }
+ }
}
- ?: emptyList()
}
override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
@@ -298,6 +307,10 @@
return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
}
+ override fun observeRuntimeValues(): Flow<Bundle> {
+ return observeUri(Contract.RuntimeValuesTable.URI).map { queryRuntimeValues() }
+ }
+
override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return withContext(backgroundDispatcher) {
context.contentResolver
@@ -375,22 +388,17 @@
enablementActionIntent =
cursor
.getString(enablementActionIntentColumnIndex)
- ?.toIntent(
- affordanceId = affordanceId,
- ),
+ ?.toIntent(affordanceId = affordanceId),
configureIntent =
cursor
.getString(configureIntentColumnIndex)
- ?.toIntent(
- affordanceId = affordanceId,
- ),
+ ?.toIntent(affordanceId = affordanceId),
)
)
}
}
}
- }
- ?: emptyList()
+ } ?: emptyList()
}
override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
@@ -444,8 +452,7 @@
}
}
}
- }
- ?: emptyList()
+ } ?: emptyList()
}
override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
@@ -454,34 +461,24 @@
}
}
- override suspend fun deleteSelection(
- slotId: String,
- affordanceId: String,
- ) {
+ override suspend fun deleteSelection(slotId: String, affordanceId: String) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
Contract.LockScreenQuickAffordances.SelectionTable.URI,
"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
" ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
" = ?",
- arrayOf(
- slotId,
- affordanceId,
- ),
+ arrayOf(slotId, affordanceId),
)
}
}
- override suspend fun deleteAllSelections(
- slotId: String,
- ) {
+ override suspend fun deleteAllSelections(slotId: String) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
Contract.LockScreenQuickAffordances.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
- arrayOf(
- slotId,
- ),
+ arrayOf(slotId),
)
}
}
@@ -499,9 +496,7 @@
}
}
- private fun observeUri(
- uri: Uri,
- ): Flow<Unit> {
+ private fun observeUri(uri: Uri): Flow<Unit> {
return callbackFlow {
val observer =
object : ContentObserver(null) {
@@ -522,9 +517,7 @@
.flowOn(backgroundDispatcher)
}
- private fun String.toIntent(
- affordanceId: String,
- ): Intent? {
+ private fun String.toIntent(affordanceId: String): Intent? {
return try {
Intent.parseUri(this, Intent.URI_INTENT_SCHEME)
} catch (e: URISyntaxException) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index 8721c78..cb167ed 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -198,4 +198,27 @@
const val VALUE = "value"
}
}
+
+ object RuntimeValuesTable {
+ const val TABLE_NAME = "runtime_values"
+ val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
+
+ /**
+ * This key corresponds to an Int value, where `1` means `true` and `0` means `false`.
+ *
+ * Whether the shade layout should be wide (true) or narrow (false).
+ *
+ * In a wide layout, notifications and quick settings each take up only half the screen
+ * width (whether they are shown at the same time or not). In a narrow layout, they can each
+ * be as wide as the entire screen.
+ */
+ const val KEY_IS_SHADE_LAYOUT_WIDE = "is_shade_layout_wide"
+
+ object Columns {
+ /** String. Unique ID for the value. */
+ const val NAME = "name"
+ /** Type depends on the key name. */
+ const val VALUE = "value"
+ }
+ }
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
index f5a955d..47c5bda 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/FakeCustomizationProviderClient.kt
@@ -19,6 +19,7 @@
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
+import android.os.Bundle
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -64,11 +65,13 @@
value = true,
)
),
+ runtimeValues: Bundle = Bundle(),
) : CustomizationProviderClient {
private val slots = MutableStateFlow(slots)
private val affordances = MutableStateFlow(affordances)
private val flags = MutableStateFlow(flags)
+ private val runtimeValues = MutableStateFlow(runtimeValues)
private val selections = MutableStateFlow<Map<String, List<String>>>(emptyMap())
@@ -93,6 +96,10 @@
return flags.value
}
+ override suspend fun queryRuntimeValues(): Bundle {
+ return runtimeValues.value
+ }
+
override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
return slots.asStateFlow()
}
@@ -101,6 +108,10 @@
return flags.asStateFlow()
}
+ override fun observeRuntimeValues(): Flow<Bundle> {
+ return runtimeValues.asStateFlow()
+ }
+
override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return affordances.value
}
@@ -139,10 +150,7 @@
}
}
- fun setFlag(
- name: String,
- value: Boolean,
- ) {
+ fun setFlag(name: String, value: Boolean) {
flags.value =
flags.value.toMutableList().apply {
removeIf { it.name == name }
@@ -150,6 +158,10 @@
}
}
+ fun setRuntimeValues(runtimeValues: Bundle) {
+ this.runtimeValues.value = runtimeValues
+ }
+
fun setSlotCapacity(slotId: String, capacity: Int) {
slots.value =
slots.value.toMutableList().apply {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 80de087..fa8cdcc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -24,7 +24,6 @@
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -40,7 +39,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -56,15 +54,11 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings mockSecureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mockSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mockSecureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 24f3a29..7e4b6f9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -16,16 +16,11 @@
package com.android.systemui.accessibility.floatingmenu;
-import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
-import android.content.Context;
import android.content.res.Configuration;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -33,7 +28,6 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.util.settings.SecureSettings;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -42,8 +36,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Locale;
/** Tests for {@link MenuInfoRepository}. */
@@ -54,30 +46,16 @@
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
- private AccessibilityManager mAccessibilityManager;
-
- @Mock
private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
@Mock
private SecureSettings mSecureSettings;
private MenuInfoRepository mMenuInfoRepository;
- private final List<String> mShortcutTargets = new ArrayList<>();
@Before
public void setUp() {
- mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
- mShortcutTargets.add(MAGNIFICATION_CONTROLLER_NAME);
- doReturn(mShortcutTargets).when(mAccessibilityManager).getAccessibilityShortcutTargets(
- anyInt());
-
- mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
- mMockSettingsContentsChanged, mSecureSettings);
- }
-
- @After
- public void tearDown() {
- mShortcutTargets.clear();
+ mMenuInfoRepository = new MenuInfoRepository(mContext, mMockSettingsContentsChanged,
+ mSecureSettings);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 157cccc..1f48bec 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -83,8 +83,7 @@
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- mSecureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mSecureSettings);
final int halfScreenHeight =
stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 46f076a..f7b81cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -33,7 +33,6 @@
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.recyclerview.widget.RecyclerView;
@@ -53,7 +52,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -80,15 +78,11 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
windowManager);
mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index ee8ce17..c1708d1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -32,7 +32,6 @@
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -49,7 +48,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -68,9 +66,6 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
private SysuiTestableContext mSpyContext;
@Before
@@ -89,8 +84,7 @@
doNothing().when(mSpyContext).startActivity(any());
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index f1401f1..73efea7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -28,7 +28,6 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
-import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
import com.android.systemui.model.SysUiState
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
@@ -236,13 +235,13 @@
dialog.show()
val slider = dialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider)!!
- val changeListener = slider.onSeekBarWithIconButtonsChangeListener
+ val seekBarListener = slider.getSeekBarChangeListener()
val seekBar: SeekBar = slider.findViewById(R.id.seekbar)!!
// Default seekbar progress for font size is 1, simulate dragging to 0 without
// releasing the finger.
- changeListener.onStartTrackingTouch(seekBar)
+ seekBarListener.onStartTrackingTouch(seekBar)
// Update seekbar progress. This will trigger onProgressChanged in the
// OnSeekBarChangeListener and the seekbar could get updated progress value
// in onStopTrackingTouch.
@@ -261,16 +260,7 @@
assertThat(systemScale).isEqualTo(1.0f)
// Simulate releasing the finger from the seekbar.
- changeListener.onStopTrackingTouch(seekBar)
- backgroundDelayableExecutor.runAllReady()
- backgroundDelayableExecutor.advanceClockToNext()
- backgroundDelayableExecutor.runAllReady()
-
- // SeekBar interaction is finalized.
- changeListener.onUserInteractionFinalized(
- seekBar,
- OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER,
- )
+ seekBarListener.onStopTrackingTouch(seekBar)
backgroundDelayableExecutor.runAllReady()
backgroundDelayableExecutor.advanceClockToNext()
backgroundDelayableExecutor.runAllReady()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
index af8c357..b33a83c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -68,6 +68,7 @@
import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.Scene
import com.android.systemui.scene.ui.composable.SceneContainer
+import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.testKosmos
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.awaitCancellation
@@ -115,7 +116,13 @@
private val Kosmos.initialSceneKey by Fixture { Scenes.Bouncer }
private val Kosmos.sceneContainerConfig by Fixture {
val navigationDistances = mapOf(Scenes.Lockscreen to 1, Scenes.Bouncer to 0)
- SceneContainerConfig(sceneKeys, initialSceneKey, emptyList(), navigationDistances)
+ SceneContainerConfig(
+ sceneKeys,
+ initialSceneKey,
+ SceneContainerTransitions,
+ emptyList(),
+ navigationDistances,
+ )
}
private val view = mock<View>()
@@ -182,6 +189,7 @@
Scenes.Bouncer to bouncerScene,
),
initialSceneKey = Scenes.Bouncer,
+ sceneTransitions = SceneContainerTransitions,
overlayByKey = emptyMap(),
dataSourceDelegator = kosmos.sceneDataSourceDelegator,
qsSceneAdapter = { kosmos.fakeQsSceneAdapter },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
index cecb525..01baadd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsViewTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -129,16 +128,43 @@
}
@Test
- public void setProgress_onlyOnProgressChangedTriggeredWithFromUserFalse() {
+ public void setProgress_onProgressChangedAndOnUserInteractionFinalized() {
reset(mOnSeekBarChangeListener);
mIconDiscreteSliderLinearLayout.setProgress(1);
+ // If users are changing seekbar progress without touching the seekbar or clicking the
+ // buttons, trigger onUserInteractionFinalized.
verify(mOnSeekBarChangeListener).onProgressChanged(
eq(mSeekbar), /* progress= */ eq(1), /* fromUser= */ eq(false));
verify(mOnSeekBarChangeListener, never()).onStartTrackingTouch(/* seekBar= */ any());
verify(mOnSeekBarChangeListener, never()).onStopTrackingTouch(/* seekBar= */ any());
+ verify(mOnSeekBarChangeListener).onUserInteractionFinalized(
+ /* seekBar= */ any(),
+ eq(OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER));
+ }
+
+ @Test
+ public void setProgressToSeekBarByTouch_onUserInteractionFinalizedAfterTouchEnds() {
+ reset(mOnSeekBarChangeListener);
+ final SeekBarWithIconButtonsView.SeekBarChangeListener seekBarChangeListener =
+ mIconDiscreteSliderLinearLayout.getSeekBarChangeListener();
+ final SeekBar seekBar = mIconDiscreteSliderLinearLayout.findViewById(R.id.seekbar);
+
+ // Simulate changing seekbar progress by touch
+ seekBarChangeListener.onStartTrackingTouch(seekBar);
+ mIconDiscreteSliderLinearLayout.setProgress(1);
+
+ verify(mOnSeekBarChangeListener).onProgressChanged(
+ eq(mSeekbar), /* progress= */ eq(1), /* fromUser= */ eq(false));
verify(mOnSeekBarChangeListener, never()).onUserInteractionFinalized(
- /* seekBar= */any(), /* control= */ anyInt());
+ /* seekBar= */ any(),
+ eq(OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER));
+
+ // Notify onUserInteractionFinalized after touch ends
+ seekBarChangeListener.onStopTrackingTouch(seekBar);
+ verify(mOnSeekBarChangeListener).onUserInteractionFinalized(
+ /* seekBar= */ any(),
+ eq(OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER));
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index a804879..f924ccb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -62,6 +62,7 @@
import com.android.systemui.complication.ComplicationLayoutEngine
import com.android.systemui.complication.dagger.ComplicationComponent
import com.android.systemui.dreams.complication.HideComplicationTouchHandler
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent
import com.android.systemui.dreams.dagger.DreamOverlayComponent
import com.android.systemui.dreams.touch.CommunalTouchHandler
import com.android.systemui.flags.andSceneContainer
@@ -119,8 +120,7 @@
private val mComplicationComponentFactory = mock<ComplicationComponent.Factory>()
private val mComplicationHostViewController = mock<ComplicationHostViewController>()
private val mComplicationVisibilityController = mock<ComplicationLayoutEngine>()
- private val mDreamComplicationComponentFactory =
- mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory>()
+ private val mDreamComplicationComponentFactory = mock<DreamComplicationComponent.Factory>()
private val mHideComplicationTouchHandler = mock<HideComplicationTouchHandler>()
private val mDreamOverlayComponentFactory = mock<DreamOverlayComponent.Factory>()
private val mCommunalTouchHandler = mock<CommunalTouchHandler>()
@@ -160,8 +160,7 @@
private lateinit var mService: DreamOverlayService
private class EnvironmentComponents(
- val dreamsComplicationComponent:
- com.android.systemui.dreams.complication.dagger.ComplicationComponent,
+ val dreamsComplicationComponent: DreamComplicationComponent,
val dreamOverlayComponent: DreamOverlayComponent,
val complicationComponent: ComplicationComponent,
val ambientTouchComponent: AmbientTouchComponent,
@@ -186,8 +185,7 @@
}
private fun setupComponentFactories(
- dreamComplicationComponentFactory:
- com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory,
+ dreamComplicationComponentFactory: DreamComplicationComponent.Factory,
dreamOverlayComponentFactory: DreamOverlayComponent.Factory,
complicationComponentFactory: ComplicationComponent.Factory,
ambientTouchComponentFactory: AmbientTouchComponent.Factory,
@@ -208,8 +206,7 @@
whenever(complicationComponent.getVisibilityController())
.thenReturn(mComplicationVisibilityController)
- val dreamComplicationComponent =
- mock<com.android.systemui.dreams.complication.dagger.ComplicationComponent>()
+ val dreamComplicationComponent = mock<DreamComplicationComponent>()
whenever(dreamComplicationComponent.getHideComplicationTouchHandler())
.thenReturn(mHideComplicationTouchHandler)
whenever(dreamOverlayComponent.communalTouchHandler).thenReturn(mCommunalTouchHandler)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
new file mode 100644
index 0000000..e659ef2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.fakeInputManager
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES
+import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.customInputGesturesRepository
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.settings.userTracker
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+class CustomInputGesturesRepositoryTest : SysuiTestCase() {
+
+ private val mockUserContext: Context = mock()
+ private val kosmos = testKosmos().also {
+ it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
+ }
+
+ private val inputManager = kosmos.fakeInputManager.inputManager
+ private val testScope = kosmos.testScope
+ private val customInputGesturesRepository = kosmos.customInputGesturesRepository
+
+ @Before
+ fun setup(){
+ whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+ }
+
+ @Test
+ fun customInputGestures_initialValueReturnsDataFromAPI() {
+ testScope.runTest {
+ val customInputGestures = listOf(allAppsInputGestureData)
+ whenever(
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).then { return@then customInputGestures }
+
+ val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
+
+ assertThat(inputGestures).containsExactly(allAppsInputGestureData)
+ }
+ }
+
+ @Test
+ fun customInputGestures_isUpdatedToMostRecentDataAfterNewGestureIsAdded() {
+ testScope.runTest {
+ var customInputGestures = listOf<InputGestureData>()
+ whenever(
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).then { return@then customInputGestures }
+ whenever(inputManager.addCustomInputGesture(any())).then { invocation ->
+ val inputGesture = invocation.getArgument<InputGestureData>(0)
+ customInputGestures = customInputGestures + inputGesture
+ return@then CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ }
+
+ val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
+ assertThat(inputGestures).isEmpty()
+
+ customInputGesturesRepository.addCustomInputGesture(allAppsInputGestureData)
+ assertThat(inputGestures).containsExactly(allAppsInputGestureData)
+ }
+ }
+
+ @Test
+ fun retrieveCustomInputGestures_retrievesMostRecentData() {
+ testScope.runTest {
+ var customInputGestures = listOf<InputGestureData>()
+ whenever(
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ ).then { return@then customInputGestures }
+
+
+ assertThat(customInputGesturesRepository.retrieveCustomInputGestures()).isEmpty()
+
+ customInputGestures = listOf(allAppsInputGestureData)
+
+ assertThat(customInputGesturesRepository.retrieveCustomInputGestures())
+ .containsExactly(allAppsInputGestureData)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
index 0d32b7f..72916a3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepositoryTest.kt
@@ -18,6 +18,8 @@
import android.content.Context
import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
import android.hardware.input.fakeInputManager
import android.platform.test.annotations.DisableFlags
@@ -34,15 +36,19 @@
import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
import com.android.systemui.keyboard.shortcut.customShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.ALL_SUPPORTED_MODIFIERS
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allCustomizableInputGesturesWithSimpleShortcutCombinations
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutCategory
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.customizableInputGestureWithUnknownKeyGestureType
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.expectedShortcutCategoriesWithSimpleShortcutCombination
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddCustomShortcutRequestInfo
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardDeleteCustomShortcutRequestInfo
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardKeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Add
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo.Delete
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
import com.android.systemui.kosmos.testScope
@@ -55,6 +61,7 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
@@ -187,11 +194,11 @@
@Test
fun shortcutBeingCustomized_updatedOnCustomizationRequested() {
testScope.runTest {
- repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo)
+ repo.onCustomizationRequested(allAppsShortcutAddRequest)
val shortcutBeingCustomized = repo.getShortcutBeingCustomized()
- assertThat(shortcutBeingCustomized).isEqualTo(standardAddCustomShortcutRequestInfo)
+ assertThat(shortcutBeingCustomized).isEqualTo(allAppsShortcutAddRequest)
}
}
@@ -211,7 +218,7 @@
fun buildInputGestureDataForShortcutBeingCustomized_noKeyCombinationSelected_returnsNull() {
testScope.runTest {
helper.toggle(deviceId = 123)
- repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo)
+ repo.onCustomizationRequested(allAppsShortcutAddRequest)
val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
@@ -223,7 +230,7 @@
fun buildInputGestureDataForShortcutBeingCustomized_successfullyBuildInputGestureData() {
testScope.runTest {
helper.toggle(deviceId = 123)
- repo.onCustomizationRequested(standardAddCustomShortcutRequestInfo)
+ repo.onCustomizationRequested(allAppsShortcutAddRequest)
repo.updateUserKeyCombination(standardKeyCombination)
val inputGestureData = repo.buildInputGestureDataForShortcutBeingCustomized()
@@ -242,13 +249,78 @@
.thenReturn(listOf(allAppsInputGestureData, goHomeInputGestureData))
whenever(inputManager.removeCustomInputGesture(allAppsInputGestureData))
.thenReturn(CUSTOM_INPUT_GESTURE_RESULT_SUCCESS)
-
helper.toggle(deviceId = 123)
- repo.onCustomizationRequested(standardDeleteCustomShortcutRequestInfo)
- val result = repo.deleteShortcutCurrentlyBeingCustomized()
-
+ val result = customizeShortcut(allAppsShortcutDeleteRequest)
assertThat(result).isEqualTo(ShortcutCustomizationRequestResult.SUCCESS)
}
}
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_isUpdatedAfterCustomShortcutIsDeleted() {
+ testScope.runTest {
+ // TODO(b/380445594) refactor tests and move these stubbing to ShortcutHelperTestHelper
+ var customInputGestures = listOf(allAppsInputGestureData)
+ whenever(inputManager.getCustomInputGestures(anyOrNull())).then {
+ return@then customInputGestures
+ }
+ whenever(inputManager.removeCustomInputGesture(any())).then {
+ val inputGestureToRemove = it.getArgument<InputGestureData>(0)
+ val containsGesture = customInputGestures.contains(inputGestureToRemove)
+ customInputGestures = customInputGestures - inputGestureToRemove
+ return@then if (containsGesture) CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ else CUSTOM_INPUT_GESTURE_RESULT_ERROR_DOES_NOT_EXIST
+ }
+ val categories by collectLastValue(repo.categories)
+ helper.toggle(deviceId = 123)
+
+ customizeShortcut(customizationRequest = allAppsShortcutDeleteRequest)
+ assertThat(categories).isEmpty()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun categories_isUpdatedAfterCustomShortcutIsAdded() {
+ testScope.runTest {
+ // TODO(b/380445594) refactor tests and move these stubbings to ShortcutHelperTestHelper
+ var customInputGestures = listOf<InputGestureData>()
+ whenever(inputManager.getCustomInputGestures(anyOrNull())).then {
+ return@then customInputGestures
+ }
+ whenever(inputManager.addCustomInputGesture(any())).then {
+ val inputGestureToAdd = it.getArgument<InputGestureData>(0)
+ customInputGestures = customInputGestures + inputGestureToAdd
+ return@then CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+ }
+ val categories by collectLastValue(repo.categories)
+ helper.toggle(deviceId = 123)
+
+ customizeShortcut(allAppsShortcutAddRequest, standardKeyCombination)
+ assertThat(categories).containsExactly(allAppsShortcutCategory)
+ }
+ }
+
+ private suspend fun customizeShortcut(
+ customizationRequest: ShortcutCustomizationRequestInfo,
+ keyCombination: KeyCombination? = null
+ ): ShortcutCustomizationRequestResult{
+ repo.onCustomizationRequested(customizationRequest)
+ repo.updateUserKeyCombination(keyCombination)
+
+ return when (customizationRequest) {
+ is Add -> {
+ repo.confirmAndSetShortcutCurrentlyBeingCustomized()
+ }
+
+ is Delete -> {
+ repo.deleteShortcutCurrentlyBeingCustomized()
+ }
+
+ else -> {
+ ShortcutCustomizationRequestResult.ERROR_OTHER
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 6d22b49..7855d42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -35,6 +35,7 @@
import android.view.KeyEvent.META_SYM_ON
import android.view.KeyboardShortcutGroup
import android.view.KeyboardShortcutInfo
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperKeys
import com.android.systemui.keyboard.shortcut.shared.model.KeyCombination
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
@@ -47,6 +48,7 @@
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutSubCategory
import com.android.systemui.keyboard.shortcut.shared.model.shortcut
+import com.android.systemui.keyboard.shortcut.shared.model.shortcutCategory
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCustomizationUiState
import com.android.systemui.res.R
@@ -558,14 +560,14 @@
),
)
- val standardAddCustomShortcutRequestInfo =
+ val allAppsShortcutAddRequest =
ShortcutCustomizationRequestInfo.Add(
label = "Open apps list",
categoryType = System,
subCategoryLabel = "System controls",
)
- val standardDeleteCustomShortcutRequestInfo =
+ val allAppsShortcutDeleteRequest =
ShortcutCustomizationRequestInfo.Delete(
label = "Open apps list",
categoryType = System,
@@ -610,6 +612,20 @@
)
.build()
+ val allAppsShortcutCategory =
+ shortcutCategory(System) {
+ subCategory("System controls") {
+ shortcut("Open apps list") {
+ command {
+ isCustom(true)
+ key(ShortcutHelperKeys.metaModifierIconResId)
+ key("Shift")
+ key("A")
+ }
+ }
+ }
+ }
+
val expectedStandardDeleteShortcutUiState =
ShortcutCustomizationUiState.DeleteShortcutDialog(isDialogShowing = false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index d0ce34c..d3d1a35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -34,9 +34,9 @@
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyDownEventWithoutActionKeyPressed
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.keyUpEventWithActionKeyPressed
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddCustomShortcutRequestInfo
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutAddRequest
import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardAddShortcutRequest
-import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.standardDeleteCustomShortcutRequestInfo
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsShortcutDeleteRequest
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
import com.android.systemui.keyboard.shortcut.shortcutCustomizationViewModelFactory
import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
@@ -99,7 +99,7 @@
@Test
fun uiState_correctlyUpdatedWhenDeleteShortcutCustomizationIsRequested() {
testScope.runTest {
- viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo)
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
assertThat(uiState).isEqualTo(expectedStandardDeleteShortcutUiState)
@@ -122,7 +122,7 @@
fun uiState_consumedOnDeleteDialogShown() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo)
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
viewModel.onDialogShown()
assertThat(
@@ -170,7 +170,7 @@
fun uiState_errorMessage_isEmptyByDefault() {
testScope.runTest {
val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
- viewModel.onShortcutCustomizationRequested(standardAddCustomShortcutRequestInfo)
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
viewModel.onDialogShown()
assertThat((uiState as ShortcutCustomizationUiState.AddShortcutDialog).errorMessage)
@@ -298,7 +298,7 @@
}
private suspend fun openAddShortcutDialogAndSetShortcut() {
- viewModel.onShortcutCustomizationRequested(standardAddCustomShortcutRequestInfo)
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
viewModel.onDialogShown()
viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
@@ -308,7 +308,7 @@
}
private suspend fun openDeleteShortcutDialogAndDeleteShortcut() {
- viewModel.onShortcutCustomizationRequested(standardDeleteCustomShortcutRequestInfo)
+ viewModel.onShortcutCustomizationRequested(allAppsShortcutDeleteRequest)
viewModel.onDialogShown()
viewModel.deleteShortcutCurrentlyBeingCustomized()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 614d51e..093ef46 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -41,6 +41,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.flag.junit.FlagsParameterization;
+import android.provider.Settings;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.view.WindowManager;
@@ -70,6 +71,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.settings.FakeSettings;
import com.google.common.util.concurrent.MoreExecutors;
@@ -111,6 +113,7 @@
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+ private FakeSettings mSecureSettings;
private final Executor mMainExecutor = MoreExecutors.directExecutor();
private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -131,6 +134,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+
+ mSecureSettings = new FakeSettings();
+ mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 0);
+
// Preferred refresh rate is equal to the first displayMode's refresh rate
mPreferredRefreshRate = mContext.getDisplay().getSystemSupportedModes()[0].getRefreshRate();
overrideResource(
@@ -164,12 +171,8 @@
() -> mSelectedUserInteractor,
mUserTracker,
mKosmos.getNotificationShadeWindowModel(),
- mKosmos::getCommunalInteractor) {
- @Override
- protected boolean isDebuggable() {
- return false;
- }
- };
+ mSecureSettings,
+ mKosmos::getCommunalInteractor);
mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {});
mNotificationShadeWindowController.fetchWindowRootView();
@@ -351,6 +354,19 @@
}
@Test
+ public void setKeyguardShowingWithSecureWindowsDisabled_disablesSecureFlag() {
+ mSecureSettings.putInt(Settings.Secure.DISABLE_SECURE_WINDOWS, 1);
+ mNotificationShadeWindowController.setBouncerShowing(true);
+
+ verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture());
+ assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue();
+ assertThat(
+ (mLayoutParameters.getValue().inputFeatures & INPUT_FEATURE_SENSITIVE_FOR_PRIVACY)
+ != 0)
+ .isTrue();
+ }
+
+ @Test
public void setKeyguardNotShowing_disablesSecureFlag() {
mNotificationShadeWindowController.setBouncerShowing(false);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
new file mode 100644
index 0000000..ef1ae09
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.shade.display
+
+import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.display.data.repository.display
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+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
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val testScope = kosmos.testScope
+ private val displayRepository = kosmos.displayRepository
+ val underTest = StatusBarTouchShadeDisplayPolicy(displayRepository, testScope.backgroundScope)
+
+ @Test
+ fun displayId_defaultToDefaultDisplay() {
+ assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun onStatusBarTouched_called_updatesDisplayId() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+ }
+
+ @Test
+ fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
+ testScope.runTest {
+ val displayIds by collectValues(underTest.displayId)
+ assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
+
+ underTest.onStatusBarTouched(2)
+
+ // Never set, as 2 was not a display according to the repository.
+ assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
+ }
+
+ @Test
+ fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
+ testScope.runTest {
+ val displayId by collectLastValue(underTest.displayId)
+
+ displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
+ underTest.onStatusBarTouched(2)
+
+ assertThat(displayId).isEqualTo(2)
+
+ displayRepository.removeDisplay(2)
+
+ assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 2a3878c..5247433 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.os.UserHandle
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.Lifecycle
@@ -251,9 +252,25 @@
val primaryUserMockContext = mock<Context>()
mContext.prepareCreateContextAsUser(UserHandle.of(PRIMARY_USER_ID), primaryUserMockContext)
controller.onUserSwitched(PRIMARY_USER_ID)
+
// Create is expected to be called once when the test starts and a second time when the user
- // is switched.
+ // is switched. The first WifiPickerTracker should have its onStop() method called prior to
+ // the new WifiPickerTracker being created.
verify(wifiPickerTrackerFactory, times(2)).create(any(), any(), any(), any())
+ verify(wifiPickerTracker).onStop()
+ }
+
+ @Test
+ @DisableFlags(FLAG_MULTIUSER_WIFI_PICKER_TRACKER_SUPPORT)
+ fun switchUsers_flagDisabled() {
+ val primaryUserMockContext = mock<Context>()
+ mContext.prepareCreateContextAsUser(UserHandle.of(PRIMARY_USER_ID), primaryUserMockContext)
+ controller.onUserSwitched(PRIMARY_USER_ID)
+
+ // Create is expected to only be called once when the test starts, switching users should
+ // have no effects.
+ verify(wifiPickerTrackerFactory, times(1)).create(any(), any(), any(), any())
+ verify(wifiPickerTracker, never()).onStop()
}
private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index b8745b3..2eb4590 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -33,7 +33,6 @@
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -57,7 +56,6 @@
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Mock private KeyguardMediaController mKeyguardMediaController;
- @Mock private NotificationSectionsFeatureManager mSectionsFeatureManager;
@Mock private MediaContainerController mMediaContainerController;
@Mock private NotificationRoundnessManager mNotificationRoundnessManager;
@Mock private SectionHeaderController mIncomingHeaderController;
@@ -73,26 +71,10 @@
@Before
public void setUp() {
- when(mSectionsFeatureManager.getNumberOfBuckets()).thenAnswer(
- invocation -> {
- int count = 2;
- if (mSectionsFeatureManager.isFilteringEnabled()) {
- count = 5;
- }
- if (mSectionsFeatureManager.isMediaControlsEnabled()) {
- if (!mSectionsFeatureManager.isFilteringEnabled()) {
- count = 5;
- } else {
- count += 1;
- }
- }
- return count;
- });
mSectionsManager =
new NotificationSectionsManager(
mConfigurationController,
mKeyguardMediaController,
- mSectionsFeatureManager,
mMediaContainerController,
mNotificationRoundnessManager,
mIncomingHeaderController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 2b7e950..dcac294 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -3,7 +3,6 @@
import android.annotation.DimenRes
import android.content.pm.PackageManager
import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
@@ -30,7 +29,6 @@
import com.android.systemui.statusbar.notification.headsup.AvalancheController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
@@ -154,22 +152,6 @@
@Test
@DisableSceneContainer
- fun resetViewStates_defaultHun_yTranslationIsInset() {
- whenever(notificationRow.isPinned).thenReturn(true)
- whenever(notificationRow.isHeadsUp).thenReturn(true)
- resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
- }
-
- @Test
- @DisableSceneContainer
- fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() {
- whenever(notificationRow.isPinned).thenReturn(true)
- whenever(notificationRow.isHeadsUp).thenReturn(true)
- resetViewStates_stackMargin_changesHunYTranslation()
- }
-
- @Test
- @DisableSceneContainer
fun resetViewStates_defaultHunWhenShadeIsOpening_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
@@ -183,24 +165,7 @@
@Test
@DisableSceneContainer
- @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_hunAnimatingAway_yTranslationIsInset() {
- whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
- resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
- }
-
- @Test
- @DisableSceneContainer
- @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_hunAnimatingAway_StackMarginChangesHunYTranslation() {
- whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
- resetViewStates_stackMargin_changesHunYTranslation()
- }
-
- @Test
- @DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_defaultHun_newHeadsUpAnim_yTranslationIsInset() {
+ fun resetViewStates_defaultHun_yTranslationIsInset() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
resetViewStates_hunYTranslationIs(stackScrollAlgorithm.mHeadsUpInset)
@@ -208,8 +173,7 @@
@Test
@DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_defaultHunWithStackMargin_newHeadsUpAnim_changesHunYTranslation() {
+ fun resetViewStates_defaultHunWithStackMargin_changesHunYTranslation() {
whenever(notificationRow.isPinned).thenReturn(true)
whenever(notificationRow.isHeadsUp).thenReturn(true)
resetViewStates_stackMargin_changesHunYTranslation()
@@ -399,8 +363,7 @@
@Test
@DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_defaultHun_showingQS_newHeadsUpAnim_hunTranslatedToMax() {
+ fun resetViewStates_defaultHun_showingQS_hunTranslatedToMax() {
// Given: the shade is open and scrolled to the bottom to show the QuickSettings
val maxHunTranslation = 2000f
ambientState.maxHeadsUpTranslation = maxHunTranslation
@@ -416,8 +379,7 @@
@Test
@DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_hunAnimatingAway_showingQS_newHeadsUpAnim_hunTranslatedToBottomOfScreen() {
+ fun resetViewStates_hunAnimatingAway_showingQS_hunTranslatedToBottomOfScreen() {
// Given: the shade is open and scrolled to the bottom to show the QuickSettings
val bottomOfScreen = 2600f
val maxHunTranslation = 2000f
@@ -437,8 +399,7 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_hunAnimatingAway_newHeadsUpAnim_hunTranslatedToTopOfScreen() {
+ fun resetViewStates_hunAnimatingAway_hunTranslatedToTopOfScreen() {
val topMargin = 100f
ambientState.maxHeadsUpTranslation = 2000f
ambientState.stackTopMargin = topMargin.toInt()
@@ -461,7 +422,6 @@
@Test
@DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunAnimatingAwayWhileDozing_yTranslationIsInset() {
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
@@ -472,7 +432,6 @@
@Test
@DisableSceneContainer
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun resetViewStates_hunAnimatingAwayWhileDozing_hasStackMargin_changesHunYTranslation() {
whenever(notificationRow.isHeadsUpAnimatingAway).thenReturn(true)
@@ -494,18 +453,6 @@
}
@Test
- @DisableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
- fun resetViewStates_hunsOverlappingAndBottomHunAnimatingAway_bottomHunClipped() {
- val topHun = mockExpandableNotificationRow()
- val bottomHun = mockExpandableNotificationRow()
- whenever(topHun.isHeadsUp).thenReturn(true)
- whenever(topHun.isPinned).thenReturn(true)
- whenever(bottomHun.isHeadsUpAnimatingAway).thenReturn(true)
-
- resetViewStates_hunsOverlapping_bottomHunClipped(topHun, bottomHun)
- }
-
- @Test
@EnableSceneContainer
fun resetViewStates_emptyShadeView_isCenteredVertically_withSceneContainer() {
stackScrollAlgorithm.initView(context)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 798465e..e4dd29a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.stack
-import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -24,7 +23,6 @@
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.row.ExpandableView
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent
import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_APPEAR
import com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR
@@ -60,11 +58,12 @@
private val viewState: ExpandableViewState =
ExpandableViewState().apply { height = VIEW_HEIGHT }
private val runnableCaptor: ArgumentCaptor<Runnable> = argumentCaptor()
+
@Before
fun setUp() {
overrideResource(
R.dimen.go_to_full_shade_appearing_translation,
- FULL_SHADE_APPEAR_TRANSLATION
+ FULL_SHADE_APPEAR_TRANSLATION,
)
overrideResource(R.dimen.heads_up_appear_y_above_screen, HEADS_UP_ABOVE_SCREEN)
@@ -74,7 +73,6 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun startAnimationForEvents_headsUpFromTop_startsHeadsUpAppearAnim() {
val topMargin = 50f
val expectedStartY = -topMargin - stackStateAnimator.mHeadsUpAppearStartAboveScreen
@@ -90,12 +88,11 @@
/* delay= */ 0L,
/* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
/* isHeadsUpAppear= */ true,
- /* onEndRunnable= */ null
+ /* onEndRunnable= */ null,
)
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun startAnimationForEvents_headsUpFromBottom_startsHeadsUpAppearAnim() {
val screenHeight = 2000f
val expectedStartY = screenHeight + stackStateAnimator.mHeadsUpAppearStartAboveScreen
@@ -114,12 +111,11 @@
/* delay= */ 0L,
/* duration= */ ANIMATION_DURATION_HEADS_UP_APPEAR.toLong(),
/* isHeadsUpAppear= */ true,
- /* onEndRunnable= */ null
+ /* onEndRunnable= */ null,
)
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun startAnimationForEvents_startsHeadsUpDisappearAnim() {
val disappearDuration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR.toLong()
val event = AnimationEvent(view, AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index f9f2cd3..3bd12e6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
@@ -35,7 +36,6 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeHeadsUpTracker;
@@ -55,7 +55,6 @@
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,7 +86,6 @@
private KeyguardStateController mKeyguardStateController;
private CommandQueue mCommandQueue;
private NotificationRoundnessManager mNotificationRoundnessManager;
- private final FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
@Before
public void setUp() throws Exception {
@@ -124,7 +122,6 @@
mNotificationRoundnessManager,
mHeadsUpStatusBarView,
new Clock(mContext, null),
- mFeatureFlags,
mock(HeadsUpNotificationIconInteractor.class),
Optional.of(mOperatorNameView));
mHeadsUpAppearanceController.setAppearFraction(0.0f, 0.0f);
@@ -141,21 +138,21 @@
mRow.setPinnedStatus(PinnedStatus.NotPinned);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- assertEquals(null, mHeadsUpStatusBarView.getShowingEntry());
+ assertNull(mHeadsUpStatusBarView.getShowingEntry());
}
@Test
- public void testShownUpdated() {
+ public void testPinnedStatusUpdated() {
mRow.setPinnedStatus(PinnedStatus.PinnedBySystem);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mHeadsUpManager.getTopEntry()).thenReturn(mEntry);
mHeadsUpAppearanceController.onHeadsUpPinned(mEntry);
- assertTrue(mHeadsUpAppearanceController.isShown());
+ assertEquals(PinnedStatus.PinnedBySystem, mHeadsUpAppearanceController.getPinnedStatus());
mRow.setPinnedStatus(PinnedStatus.NotPinned);
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
mHeadsUpAppearanceController.onHeadsUpUnPinned(mEntry);
- Assert.assertFalse(mHeadsUpAppearanceController.isShown());
+ assertEquals(PinnedStatus.NotPinned, mHeadsUpAppearanceController.getPinnedStatus());
}
@Test
@@ -210,7 +207,7 @@
mNotificationRoundnessManager,
mHeadsUpStatusBarView,
new Clock(mContext, null),
- mFeatureFlags, mock(HeadsUpNotificationIconInteractor.class),
+ mock(HeadsUpNotificationIconInteractor.class),
Optional.empty());
assertEquals(expandedHeight, newController.mExpandedHeight, 0.0f);
@@ -227,7 +224,7 @@
mHeadsUpAppearanceController.onViewDetached();
verify(mHeadsUpManager).removeListener(any());
- verify(mDarkIconDispatcher).removeDarkReceiver((DarkIconDispatcher.DarkReceiver) any());
+ verify(mDarkIconDispatcher).removeDarkReceiver(any());
verify(mShadeHeadsUpTracker).removeTrackingHeadsUpListener(any());
verify(mShadeHeadsUpTracker).setHeadsUpAppearanceController(isNull());
verify(mStackScrollerController).removeOnExpandedHeightChangedListener(any());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index 4eef308..f8d45e4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -1248,10 +1248,12 @@
otherUserMockContext,
)
userRepository.setSelectedUserInfo(ANOTHER_USER)
+ verify(wifiPickerTracker).onStop()
// THEN we use the different user's context to create WifiPickerTracker
val newCaptor = argumentCaptor<Context>()
verify(wifiPickerTrackerFactory).create(newCaptor.capture(), any(), any(), any())
+ verify(wifiPickerTracker).onStop()
assertThat(newCaptor.firstValue).isEqualTo(otherUserMockContext)
}
@@ -1288,6 +1290,7 @@
// THEN we do NOT re-create WifiPickerTracker because the multiuser flag is off
verify(wifiPickerTrackerFactory, never()).create(any(), any(), any(), any())
+ verify(wifiPickerTracker, never()).onStop()
}
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
index 32ef501..09ea767 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
@@ -90,15 +90,15 @@
assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.EASE_IN)
- animatorTestRule.advanceTimeBy(config.easeInDuration + 50L)
+ animatorTestRule.advanceAnimationDuration(config.easeInDuration + 50L)
assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.MAIN)
- animatorTestRule.advanceTimeBy(config.duration + 50L)
+ animatorTestRule.advanceAnimationDuration(config.duration + 50L)
assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.EASE_OUT)
- animatorTestRule.advanceTimeBy(config.easeOutDuration + 50L)
+ animatorTestRule.advanceAnimationDuration(config.easeOutDuration + 50L)
assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
index 67a4219..c9be7e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
@@ -117,9 +117,9 @@
loadingEffect.play()
// Execute all the animators by advancing each duration with some buffer.
- animatorTestRule.advanceTimeBy(config.easeInDuration.toLong())
- animatorTestRule.advanceTimeBy(config.maxDuration.toLong())
- animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong())
+ animatorTestRule.advanceAnimationDuration(config.easeInDuration.toLong())
+ animatorTestRule.advanceAnimationDuration(config.maxDuration.toLong())
+ animatorTestRule.advanceAnimationDuration(config.easeOutDuration.toLong())
animatorTestRule.advanceTimeBy(500)
assertThat(states)
@@ -206,12 +206,12 @@
assertThat(isFinished).isFalse()
loadingEffect.play()
- animatorTestRule.advanceTimeBy(config.easeInDuration.toLong() + 500L)
+ animatorTestRule.advanceAnimationDuration(config.easeInDuration.toLong() + 500L)
assertThat(isFinished).isFalse()
loadingEffect.finish()
- animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong() + 500L)
+ animatorTestRule.advanceAnimationDuration(config.easeOutDuration.toLong() + 500L)
assertThat(isFinished).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
index 266a60d..665f55b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
@@ -193,7 +193,11 @@
}
private fun TestScope.advanceTime(timeMs: Long) {
- animatorTestRule.advanceTimeBy(timeMs)
+ if (timeMs == ANIMATION_DURATION) {
+ animatorTestRule.advanceAnimationDuration(timeMs)
+ } else {
+ animatorTestRule.advanceTimeBy(timeMs)
+ }
advanceTimeBy(timeMs)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
index ef2d4ce..c7b685f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt
@@ -182,7 +182,7 @@
private fun runOnProgressThreadWithInterval(
vararg blocks: () -> Unit,
- intervalMillis: Long = 60,
+ intervalMillis: Long = 100,
) {
blocks.forEach {
bgHandler.post(it)
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 5270403..478050b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -236,6 +236,9 @@
<!-- The size of a bluetooth indicator icon that displays next to the RSSI status icon. -->
<dimen name="status_bar_connected_device_bt_indicator_size">17dp</dimen>
+ <!-- Height of a small notification in the status bar (2025 redesign version) -->
+ <dimen name="notification_2025_min_height">@*android:dimen/notification_2025_min_height</dimen>
+
<!-- Height of a small notification in the status bar-->
<dimen name="notification_min_height">@*android:dimen/notification_min_height</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index df9f705..071cf8a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -95,9 +95,10 @@
private val broadcastDispatcher: BroadcastDispatcher,
private val batteryController: BatteryController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ // TODO b/362719719 - We should use the configuration controller associated with the display.
private val configurationController: ConfigurationController,
@DisplaySpecific private val resources: Resources,
- private val context: Context,
+ @DisplaySpecific val context: Context,
@Main private val mainExecutor: DelayableExecutor,
@Background private val bgExecutor: Executor,
private val clockBuffers: ClockMessageBuffers,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index add459b..1083136 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -44,6 +44,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -103,7 +104,8 @@
};
@Inject
- public KeyguardDisplayManager(Context context,
+ public KeyguardDisplayManager(
+ @ShadeDisplayAware Context context,
Lazy<NavigationBarController> navigationBarControllerLazy,
DisplayTracker displayTracker,
@Main Executor mainExecutor,
@@ -331,7 +333,8 @@
private boolean mIsInConcurrentDisplayState;
@Inject
- DeviceStateHelper(Context context,
+ DeviceStateHelper(
+ @ShadeDisplayAware Context context,
DeviceStateManager deviceStateManager,
@Main Executor mainExecutor) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
index 5a02486..07bd813 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
@@ -25,6 +25,7 @@
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
import com.android.systemui.shade.NotificationShadeWindowView
+import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START
@@ -43,7 +44,7 @@
class KeyguardUnfoldTransition
@Inject
constructor(
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
private val keyguardRootView: KeyguardRootView,
private val shadeWindowView: NotificationShadeWindowView,
statusBarStateController: StatusBarStateController,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2c8fff8..a703b02 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -101,6 +101,7 @@
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -151,6 +152,7 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
@@ -643,6 +645,11 @@
} else {
data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
}
+ if (data == null) {
+ Log.w(TAG, "Null SimData for subscription: "
+ + changedSubscriptions.get(i));
+ continue;
+ }
for (int j = 0; j < mCallbacks.size(); j++) {
var cb = mCallbacks.get(j).get();
if (cb != null) {
@@ -2157,7 +2164,7 @@
@VisibleForTesting
@Inject
protected KeyguardUpdateMonitor(
- Context context,
+ @ShadeDisplayAware Context context,
UserTracker userTracker,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
@@ -3415,6 +3422,9 @@
* Removes all valid subscription info from the map for the given slotId.
*/
private void invalidateSlot(int slotId) {
+ if (simPinUseSlotId()) {
+ return;
+ }
synchronized (mSimDataLockObject) {
var iter = simPinUseSlotId() ? mSimDatasBySlotId.entrySet().iterator()
: mSimDatas.entrySet().iterator();
@@ -3446,7 +3456,6 @@
|| state == TelephonyManager.SIM_STATE_CARD_IO_ERROR) {
updateTelephonyCapable(true);
}
-
invalidateSlot(slotId);
}
@@ -3966,10 +3975,10 @@
private boolean refreshSimState(int subId, int slotId) {
int state = mTelephonyManager.getSimState(slotId);
synchronized (mSimDataLockObject) {
- SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId);
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
invalidateSlot(slotId);
}
+ SimData data = simPinUseSlotId() ? mSimDatasBySlotId.get(slotId) : mSimDatas.get(subId);
final boolean changed;
if (data == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index fc42045..0305b5e 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -30,6 +30,7 @@
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.clocks.ClockMessageBuffers;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
import com.android.systemui.util.ThreadAssert;
@@ -47,7 +48,7 @@
@Provides
@SysUISingleton
public static ClockRegistry getClockRegistry(
- @Application Context context,
+ @ShadeDisplayAware Context context,
PluginManager pluginManager,
@Application CoroutineScope scope,
@Main CoroutineDispatcher mainDispatcher,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 3cf400a..5b43346 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -18,7 +18,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static com.android.systemui.accessibility.AccessibilityLogger.MagnificationSettingsEvent;
@@ -48,7 +47,6 @@
import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
-import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.model.SysUiState;
@@ -118,15 +116,13 @@
@Override
protected WindowMagnificationController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
- Flags.createWindowlessWindowMagnifier()
- ? TYPE_ACCESSIBILITY_OVERLAY
- : TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
- /* options */ null);
+ TYPE_ACCESSIBILITY_OVERLAY,
+ /* options */ null);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
Supplier<SurfaceControlViewHost> scvhSupplier = () ->
- Flags.createWindowlessWindowMagnifier() ? new SurfaceControlViewHost(mContext,
- mContext.getDisplay(), new InputTransferToken(), TAG) : null;
+ new SurfaceControlViewHost(mContext,
+ mContext.getDisplay(), new InputTransferToken(), TAG);
return new WindowMagnificationController(
windowContext,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index ffb5f3d..559e6f7 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -20,7 +20,6 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_MIGRATION_TOOLTIP_PROMPT;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY;
import static android.provider.Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE;
-import static android.provider.Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
@@ -43,7 +42,6 @@
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
@@ -77,9 +75,6 @@
private final Context mContext;
private final Configuration mConfiguration;
- private final AccessibilityManager mAccessibilityManager;
- private final AccessibilityManager.AccessibilityServicesStateChangeListener
- mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final OnSettingsContentsChanged mSettingsContentsCallback;
private final SecureSettings mSecureSettings;
@@ -147,10 +142,9 @@
}
};
- MenuInfoRepository(Context context, AccessibilityManager accessibilityManager,
+ MenuInfoRepository(Context context,
OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) {
mContext = context;
- mAccessibilityManager = accessibilityManager;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mSettingsContentsCallback = settingsContentsChanged;
mSecureSettings = secureSettings;
@@ -244,13 +238,6 @@
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mSecureSettings.registerContentObserverForUserSync(
- mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
- /* notifyForDescendants */ false,
- mMenuTargetFeaturesContentObserver,
- UserHandle.USER_CURRENT);
- }
mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
@@ -264,11 +251,6 @@
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
mContext.registerComponentCallbacks(mComponentCallbacks);
-
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mAccessibilityManager.addAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
- }
}
void unregisterObserversAndCallbacks() {
@@ -276,11 +258,6 @@
mContext.getContentResolver().unregisterContentObserver(mMenuSizeContentObserver);
mContext.getContentResolver().unregisterContentObserver(mMenuFadeOutContentObserver);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
-
- if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
- mA11yServicesStateChangeListener);
- }
}
interface OnSettingsContentsChanged {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index cb96e78..cfcaa4f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -42,8 +42,7 @@
NavigationModeController navigationModeController) {
mWindowManager = viewCaptureAwareWindowManager;
- MenuViewModel menuViewModel = new MenuViewModel(
- context, accessibilityManager, secureSettings);
+ MenuViewModel menuViewModel = new MenuViewModel(context, secureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index f924784..46c407e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -17,7 +17,6 @@
package com.android.systemui.accessibility.floatingmenu;
import android.content.Context;
-import android.view.accessibility.AccessibilityManager;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
@@ -43,10 +42,9 @@
private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
private final MenuInfoRepository mInfoRepository;
- MenuViewModel(Context context, AccessibilityManager accessibilityManager,
- SecureSettings secureSettings) {
- mInfoRepository = new MenuInfoRepository(context,
- accessibilityManager, /* settingsContentsChanged= */ this, secureSettings);
+ MenuViewModel(Context context, SecureSettings secureSettings) {
+ mInfoRepository = new MenuInfoRepository(context, /* settingsContentsChanged= */ this,
+ secureSettings);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
index 7fe0032..82bce0b 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java
@@ -179,6 +179,14 @@
}
/**
+ * Only for testing. Get mSeekBarListener to the seekbar.
+ */
+ @VisibleForTesting
+ public SeekBarChangeListener getSeekBarChangeListener() {
+ return mSeekBarListener;
+ }
+
+ /**
* Only for testing. Get {@link #mSeekbar} in the layout.
*/
@VisibleForTesting
@@ -289,8 +297,10 @@
void onUserInteractionFinalized(SeekBar seekBar, @ControlUnitType int control);
}
- private class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
+ @VisibleForTesting
+ public class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
private OnSeekBarWithIconButtonsChangeListener mOnSeekBarChangeListener = null;
+ private boolean mSeekByTouch = false;
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
@@ -308,6 +318,14 @@
seekBar, OnSeekBarWithIconButtonsChangeListener.ControlUnitType.BUTTON);
} else {
mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
+ if (!mSeekByTouch) {
+ // Accessibility users could change the progress of the seekbar without
+ // touching the seekbar or clicking the buttons. We will consider the
+ // interaction has finished in this case.
+ mOnSeekBarChangeListener.onUserInteractionFinalized(
+ seekBar,
+ OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER);
+ }
}
}
updateIconViewIfNeeded(progress);
@@ -315,6 +333,7 @@
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
+ mSeekByTouch = true;
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(seekBar);
}
@@ -322,6 +341,7 @@
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
+ mSeekByTouch = false;
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(seekBar);
mOnSeekBarChangeListener.onUserInteractionFinalized(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
index 44dd34a..8ddd1ed 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.kt
@@ -34,6 +34,7 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelper
import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl
+import com.android.systemui.communal.ui.compose.sceneTransitions
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.communal.util.CommunalColorsImpl
import com.android.systemui.communal.widgets.CommunalWidgetModule
@@ -113,6 +114,7 @@
SceneContainerConfig(
sceneKeys = listOf(CommunalScenes.Blank, CommunalScenes.Communal),
initialSceneKey = CommunalScenes.Blank,
+ transitions = sceneTransitions,
navigationDistances =
mapOf(CommunalScenes.Blank to 0, CommunalScenes.Communal to 1),
)
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 1fa829a..e5acb82 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -66,10 +66,22 @@
/** Display removal event indicating a display has been removed. */
val displayRemovalEvent: Flow<Int>
- /** Provides the current set of displays. */
+ /**
+ * Provides the current set of displays.
+ *
+ * Consider using [displayIds] if only the [Display.getDisplayId] is needed.
+ */
val displays: StateFlow<Set<Display>>
/**
+ * Provides the current set of display ids.
+ *
+ * Note that it is preferred to use this instead of [displays] if only the
+ * [Display.getDisplayId] is needed.
+ */
+ val displayIds: StateFlow<Set<Int>>
+
+ /**
* Pending display id that can be enabled/disabled.
*
* When `null`, it means there is no pending display waiting to be enabled.
@@ -159,7 +171,7 @@
private val initialDisplayIds = initialDisplays.map { display -> display.displayId }.toSet()
/** Propagate to the listeners only enabled displays */
- private val enabledDisplayIds: Flow<Set<Int>> =
+ private val enabledDisplayIds: StateFlow<Set<Int>> =
allDisplayEvents
.scan(initial = initialDisplayIds) { previousIds: Set<Int>, event: DisplayEvent ->
val id = event.displayId
@@ -170,8 +182,8 @@
}
}
.distinctUntilChanged()
- .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
.debugLog("enabledDisplayIds")
+ .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), initialDisplayIds)
private val defaultDisplay by lazy {
getDisplayFromDisplayManager(Display.DEFAULT_DISPLAY)
@@ -209,6 +221,8 @@
*/
override val displays: StateFlow<Set<Display>> = enabledDisplays
+ override val displayIds: StateFlow<Set<Int>> = enabledDisplayIds
+
/**
* Implementation that maps from [displays], instead of [allDisplayEvents] for 2 reasons:
* 1. Guarantee that it emits __after__ [displays] emitted. This way it is guaranteed that
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
index 12ceedd..b990e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
@@ -31,15 +31,17 @@
override fun transitionTo(oldState: DozeMachine.State, newState: DozeMachine.State) {
this.oldState = oldState
this.newState = newState
- callbacks.forEach { it.onDozeTransition(oldState, newState) }
+
+ val cbs = synchronized(this) { callbacks.toSet() }
+ cbs.forEach { it.onDozeTransition(oldState, newState) }
}
override fun addCallback(callback: DozeTransitionCallback) {
- callbacks.add(callback)
+ synchronized(this) { callbacks.add(callback) }
}
override fun removeCallback(callback: DozeTransitionCallback) {
- callbacks.remove(callback)
+ synchronized(this) { callbacks.remove(callback) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 43b7ced..aee3a45 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -65,6 +65,7 @@
import com.android.systemui.communal.shared.model.CommunalTransitionKeys;
import com.android.systemui.complication.dagger.ComplicationComponent;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.navigationbar.gestural.domain.GestureInteractor;
@@ -141,8 +142,7 @@
*/
private boolean mBouncerShowing = false;
- private final com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
- mDreamComplicationComponentFactory;
+ private final DreamComplicationComponent.Factory mDreamComplicationComponentFactory;
private final ComplicationComponent.Factory mComplicationComponentFactory;
private final DreamOverlayComponent.Factory mDreamOverlayComponentFactory;
private final AmbientTouchComponent.Factory mAmbientTouchComponentFactory;
@@ -376,8 +376,7 @@
@Main DelayableExecutor executor,
ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
ComplicationComponent.Factory complicationComponentFactory,
- com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory
- dreamComplicationComponentFactory,
+ DreamComplicationComponent.Factory dreamComplicationComponentFactory,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
AmbientTouchComponent.Factory ambientTouchComponentFactory,
DreamOverlayStateController stateController,
@@ -479,9 +478,9 @@
mLifecycleOwner,
() -> mExecutor.execute(DreamOverlayService.this::requestExit),
new ViewModelStore(), mTouchInsetManager);
- final com.android.systemui.dreams.complication.dagger.ComplicationComponent
- dreamComplicationComponent = mDreamComplicationComponentFactory.create(
- complicationComponent.getVisibilityController(), mTouchInsetManager);
+ final DreamComplicationComponent dreamComplicationComponent =
+ mDreamComplicationComponentFactory.create(
+ complicationComponent.getVisibilityController(), mTouchInsetManager);
final DreamOverlayComponent dreamOverlayComponent = mDreamOverlayComponentFactory.create(
mLifecycleOwner, complicationComponent.getComplicationHostViewController(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
index f8ae5c2..ea5fbc6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/HideComplicationTouchHandler.java
@@ -17,8 +17,8 @@
package com.android.systemui.dreams.complication;
import static com.android.systemui.Flags.removeDreamOverlayHideOnTouch;
-import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_FADE_OUT_DELAY;
-import static com.android.systemui.dreams.complication.dagger.ComplicationModule.COMPLICATIONS_RESTORE_TIMEOUT;
+import static com.android.systemui.dreams.complication.dagger.DreamComplicationModule.COMPLICATIONS_FADE_OUT_DELAY;
+import static com.android.systemui.dreams.complication.dagger.DreamComplicationModule.COMPLICATIONS_RESTORE_TIMEOUT;
import android.util.Log;
import android.view.MotionEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt
similarity index 67%
rename from packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt
rename to packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt
index 492c502..17d3acd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationComponent.kt
@@ -6,15 +6,15 @@
import dagger.BindsInstance
import dagger.Subcomponent
-@Subcomponent(modules = [ComplicationModule::class])
-interface ComplicationComponent {
- /** Factory for generating [ComplicationComponent]. */
+@Subcomponent(modules = [DreamComplicationModule::class])
+interface DreamComplicationComponent {
+ /** Factory for generating [DreamComplicationComponent]. */
@Subcomponent.Factory
interface Factory {
fun create(
@BindsInstance visibilityController: Complication.VisibilityController,
- @BindsInstance touchInsetManager: TouchInsetManager
- ): ComplicationComponent
+ @BindsInstance touchInsetManager: TouchInsetManager,
+ ): DreamComplicationComponent
}
fun getHideComplicationTouchHandler(): HideComplicationTouchHandler
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
rename to packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt
index 6fd6f4e..59af22a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamComplicationModule.kt
@@ -1,14 +1,14 @@
package com.android.systemui.dreams.complication.dagger
import android.content.res.Resources
-import com.android.systemui.res.R
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
import dagger.Module
import dagger.Provides
import javax.inject.Named
@Module
-object ComplicationModule {
+object DreamComplicationModule {
const val COMPLICATIONS_RESTORE_TIMEOUT = "complication_restore_timeout"
const val COMPLICATIONS_FADE_OUT_DELAY = "complication_fade_out_delay"
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 3171bbc..216cb86 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -32,7 +32,7 @@
import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.SystemDialogsCloser;
-import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
+import com.android.systemui.dreams.complication.dagger.DreamComplicationComponent;
import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
import com.android.systemui.dreams.homecontrols.dagger.HomeControlsDataSourceModule;
import com.android.systemui.dreams.homecontrols.dagger.HomeControlsRemoteServiceComponent;
@@ -68,7 +68,7 @@
HomeControlsDataSourceModule.class,
},
subcomponents = {
- ComplicationComponent.class,
+ DreamComplicationComponent.class,
DreamOverlayComponent.class,
HomeControlsRemoteServiceComponent.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
new file mode 100644
index 0000000..9ffdafc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.data.repository
+
+import android.content.Context
+import android.content.Context.INPUT_SERVICE
+import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
+import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
+import android.hardware.input.InputSettings
+import android.util.Log
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
+import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_OTHER
+import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION
+import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.SUCCESS
+import com.android.systemui.settings.UserTracker
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+
+class CustomInputGesturesRepository
+@Inject
+constructor(private val userTracker: UserTracker,
+ @Background private val bgCoroutineContext: CoroutineContext)
+{
+
+ private val userContext: Context
+ get() = userTracker.createCurrentUserContext(userTracker.userContext)
+
+ // Input manager created with user context to provide correct user id when requesting custom
+ // shortcut
+ private val inputManager: InputManager
+ get() = userContext.getSystemService(INPUT_SERVICE) as InputManager
+
+ private val _customInputGesture = MutableStateFlow<List<InputGestureData>>(emptyList())
+
+ val customInputGestures =
+ _customInputGesture.onStart { refreshCustomInputGestures() }
+
+ private fun refreshCustomInputGestures() {
+ _customInputGesture.value = retrieveCustomInputGestures()
+ }
+
+ fun retrieveCustomInputGestures(): List<InputGestureData> {
+ return if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) {
+ inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+ } else emptyList()
+ }
+
+ suspend fun addCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
+ return withContext(bgCoroutineContext) {
+ when (val result = inputManager.addCustomInputGesture(inputGesture)) {
+ CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
+ refreshCustomInputGestures()
+ SUCCESS
+ }
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS ->
+ ERROR_RESERVED_COMBINATION
+
+ CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE ->
+ ERROR_RESERVED_COMBINATION
+
+ else -> {
+ Log.w(
+ TAG,
+ "Attempted to add inputGesture: $inputGesture " +
+ "but ran into an error with code: $result",
+ )
+ ERROR_OTHER
+ }
+ }
+ }
+ }
+
+ suspend fun deleteCustomInputGesture(inputGesture: InputGestureData): ShortcutCustomizationRequestResult {
+ return withContext(bgCoroutineContext){
+ when (
+ val result = inputManager.removeCustomInputGesture(inputGesture)
+ ) {
+ CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> {
+ refreshCustomInputGestures()
+ SUCCESS
+ }
+ else -> {
+ Log.w(
+ TAG,
+ "Attempted to delete inputGesture: $inputGesture " +
+ "but ran into an error with code: $result",
+ )
+ ERROR_OTHER
+ }
+ }
+ }
+ }
+
+ private companion object {
+ private const val TAG = "CustomInputGesturesRepository"
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
index 321fd57..d1bd51c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomShortcutCategoriesRepository.kt
@@ -17,17 +17,11 @@
package com.android.systemui.keyboard.shortcut.data.repository
import android.content.Context
-import android.content.Context.INPUT_SERVICE
import android.hardware.input.InputGestureData
import android.hardware.input.InputGestureData.Builder
import android.hardware.input.InputGestureData.KeyTrigger
import android.hardware.input.InputGestureData.createKeyTrigger
import android.hardware.input.InputManager
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE
-import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
-import android.hardware.input.InputSettings
-import android.hardware.input.KeyGestureEvent
import android.hardware.input.KeyGestureEvent.KeyGestureType
import android.util.Log
import androidx.annotation.VisibleForTesting
@@ -44,9 +38,6 @@
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCustomizationRequestInfo
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutKey
-import com.android.systemui.settings.UserTracker
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -55,27 +46,22 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
@SysUISingleton
class CustomShortcutCategoriesRepository
@Inject
constructor(
stateRepository: ShortcutHelperStateRepository,
- private val userTracker: UserTracker,
@Background private val backgroundScope: CoroutineScope,
@Background private val bgCoroutineContext: CoroutineContext,
private val shortcutCategoriesUtils: ShortcutCategoriesUtils,
private val context: Context,
private val inputGestureMaps: InputGestureMaps,
-) : ShortcutCategoriesRepository {
-
- private val userContext: Context
- get() = userTracker.createCurrentUserContext(userTracker.userContext)
-
- // Input manager created with user context to provide correct user id when requesting custom
- // shortcut
+ private val customInputGesturesRepository: CustomInputGesturesRepository,
private val inputManager: InputManager
- get() = userContext.getSystemService(INPUT_SERVICE) as InputManager
+) : ShortcutCategoriesRepository {
private val _selectedKeyCombination = MutableStateFlow<KeyCombination?>(null)
private val _shortcutBeingCustomized = mutableStateOf<ShortcutCustomizationRequestInfo?>(null)
@@ -125,14 +111,12 @@
)
override val categories: Flow<List<ShortcutCategory>> =
- activeInputDevice
- .map { inputDevice ->
+ combine(activeInputDevice, customInputGesturesRepository.customInputGestures)
+ { inputDevice, inputGestures ->
if (inputDevice == null) {
emptyList()
} else {
- val customInputGesturesForUser: List<InputGestureData> =
- getCustomInputGestures()
- val sources = toInternalGroupSources(customInputGesturesForUser)
+ val sources = toInternalGroupSources(inputGestures)
val supportedKeyCodes =
shortcutCategoriesUtils.fetchSupportedKeyCodes(
inputDevice.id,
@@ -181,56 +165,23 @@
private fun retrieveInputGestureDataForShortcutBeingDeleted(): InputGestureData? {
val keyGestureType = getKeyGestureTypeFromShortcutBeingDeletedLabel()
- return getCustomInputGestures().firstOrNull { it.action.keyGestureType() == keyGestureType }
+ return customInputGesturesRepository.retrieveCustomInputGestures()
+ .firstOrNull { it.action.keyGestureType() == keyGestureType }
}
- suspend fun confirmAndSetShortcutCurrentlyBeingCustomized():
- ShortcutCustomizationRequestResult {
- return withContext(bgCoroutineContext) {
- val inputGestureData =
- buildInputGestureDataForShortcutBeingCustomized()
- ?: return@withContext ShortcutCustomizationRequestResult.ERROR_OTHER
+ suspend fun confirmAndSetShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult {
+ val inputGestureData =
+ buildInputGestureDataForShortcutBeingCustomized()
+ ?: return ShortcutCustomizationRequestResult.ERROR_OTHER
- return@withContext when (inputManager.addCustomInputGesture(inputGestureData)) {
- CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> ShortcutCustomizationRequestResult.SUCCESS
- CUSTOM_INPUT_GESTURE_RESULT_ERROR_ALREADY_EXISTS ->
- ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION
-
- CUSTOM_INPUT_GESTURE_RESULT_ERROR_RESERVED_GESTURE ->
- ShortcutCustomizationRequestResult.ERROR_RESERVED_COMBINATION
-
- else -> ShortcutCustomizationRequestResult.ERROR_OTHER
- }
- }
+ return customInputGesturesRepository.addCustomInputGesture(inputGestureData)
}
- suspend fun deleteShortcutCurrentlyBeingCustomized():
- ShortcutCustomizationRequestResult {
- return withContext(bgCoroutineContext) {
- val inputGestureData =
- retrieveInputGestureDataForShortcutBeingDeleted()
- ?: return@withContext ShortcutCustomizationRequestResult.ERROR_OTHER
- return@withContext when (
- val result = inputManager.removeCustomInputGesture(inputGestureData)
- ) {
- CUSTOM_INPUT_GESTURE_RESULT_SUCCESS -> ShortcutCustomizationRequestResult.SUCCESS
- else -> {
- Log.w(
- TAG,
- "Attempted to delete shortcut being customized " +
- "${_shortcutBeingCustomized.value} but ran into an error. InputGestureData" +
- " = $inputGestureData, error code: $result",
- )
- ShortcutCustomizationRequestResult.ERROR_OTHER
- }
- }
- }
- }
-
- private fun getCustomInputGestures(): List<InputGestureData> {
- return if (InputSettings.isCustomizableInputGesturesFeatureFlagEnabled()) {
- inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
- } else emptyList()
+ suspend fun deleteShortcutCurrentlyBeingCustomized(): ShortcutCustomizationRequestResult {
+ val inputGestureData =
+ retrieveInputGestureDataForShortcutBeingDeleted()
+ ?: return ShortcutCustomizationRequestResult.ERROR_OTHER
+ return customInputGesturesRepository.deleteCustomInputGesture(inputGestureData)
}
private fun Builder.addKeyGestureTypeFromShortcutLabel(): Builder {
@@ -347,7 +298,7 @@
}
private fun fetchGroupLabelByGestureType(
- @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ @KeyGestureType keyGestureType: Int
): String? {
inputGestureMaps.gestureToInternalKeyboardShortcutGroupLabelResIdMap[keyGestureType]?.let {
return context.getString(it)
@@ -355,7 +306,7 @@
}
private fun fetchShortcutInfoLabelByGestureType(
- @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ @KeyGestureType keyGestureType: Int
): String? {
inputGestureMaps.gestureToInternalKeyboardShortcutInfoLabelResIdMap[keyGestureType]?.let {
return context.getString(it)
@@ -363,7 +314,7 @@
}
private fun fetchShortcutCategoryTypeByGestureType(
- @KeyGestureEvent.KeyGestureType keyGestureType: Int
+ @KeyGestureType keyGestureType: Int
): ShortcutCategoryType? {
return inputGestureMaps.gestureToShortcutCategoryTypeMap[keyGestureType]
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index a94df09..7a72732 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -36,6 +36,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -44,6 +45,7 @@
ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
@Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
+ @Inject lateinit var shadeModeInteractor: ShadeModeInteractor
@Inject lateinit var previewManager: KeyguardRemotePreviewManager
@Inject @Main lateinit var mainDispatcher: CoroutineDispatcher
@@ -73,6 +75,11 @@
MATCH_CODE_ALL_SELECTIONS,
)
addURI(Contract.AUTHORITY, Contract.FlagsTable.TABLE_NAME, MATCH_CODE_ALL_FLAGS)
+ addURI(
+ Contract.AUTHORITY,
+ Contract.RuntimeValuesTable.TABLE_NAME,
+ MATCH_CODE_ALL_RUNTIME_VALUES,
+ )
}
override fun onCreate(): Boolean {
@@ -94,7 +101,8 @@
MATCH_CODE_ALL_SLOTS,
MATCH_CODE_ALL_AFFORDANCES,
MATCH_CODE_ALL_FLAGS,
- MATCH_CODE_ALL_SELECTIONS -> "vnd.android.cursor.dir/vnd."
+ MATCH_CODE_ALL_SELECTIONS,
+ MATCH_CODE_ALL_RUNTIME_VALUES -> "vnd.android.cursor.dir/vnd."
else -> null
}
@@ -113,6 +121,7 @@
Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
)
MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
+ MATCH_CODE_ALL_RUNTIME_VALUES -> Contract.RuntimeValuesTable.TABLE_NAME
else -> null
}
@@ -146,6 +155,7 @@
MATCH_CODE_ALL_SLOTS -> querySlots()
MATCH_CODE_ALL_SELECTIONS -> querySelections()
MATCH_CODE_ALL_FLAGS -> queryFlags()
+ MATCH_CODE_ALL_RUNTIME_VALUES -> queryRuntimeValues()
else -> null
}
}
@@ -334,6 +344,23 @@
}
}
+ private fun queryRuntimeValues(): Cursor {
+ return MatrixCursor(
+ arrayOf(
+ Contract.RuntimeValuesTable.Columns.NAME,
+ Contract.RuntimeValuesTable.Columns.VALUE,
+ )
+ )
+ .apply {
+ addRow(
+ arrayOf(
+ Contract.RuntimeValuesTable.KEY_IS_SHADE_LAYOUT_WIDE,
+ if (shadeModeInteractor.isShadeLayoutWide.value) 1 else 0,
+ )
+ )
+ }
+ }
+
private suspend fun deleteSelection(uri: Uri, selectionArgs: Array<out String>?): Int {
if (selectionArgs == null) {
throw IllegalArgumentException(
@@ -370,5 +397,6 @@
private const val MATCH_CODE_ALL_AFFORDANCES = 2
private const val MATCH_CODE_ALL_SELECTIONS = 3
private const val MATCH_CODE_ALL_FLAGS = 4
+ private const val MATCH_CODE_ALL_RUNTIME_VALUES = 5
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 6985615..3d6cf2f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -187,10 +187,6 @@
launch("$TAG#fpIconView.viewModel") {
fgViewModel.viewModel.collect { viewModel ->
Log.d(TAG, "Updating device entry icon image state $viewModel")
- fgIconView.setImageState(
- view.getIconState(viewModel.type, viewModel.useAodVariant),
- /* merge */ false,
- )
if (viewModel.type.contentDescriptionResId != -1) {
fgIconView.contentDescription =
fgIconView.resources.getString(
@@ -205,6 +201,14 @@
viewModel.padding,
viewModel.padding,
)
+ // Set image state at the end after updating other view state. This
+ // method forces the ImageView to recompute the bounds of the drawable.
+ fgIconView.setImageState(
+ view.getIconState(viewModel.type, viewModel.useAodVariant),
+ /* merge */ false,
+ )
+ // Invalidate, just in case the padding changes just after icon changes
+ fgIconView.invalidate()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 7ccdf0a..946eccd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -25,6 +25,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -65,7 +66,7 @@
QSContainerImpl view,
QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
- ConfigurationController configurationController,
+ @ShadeDisplayAware ConfigurationController configurationController,
FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
index a222b3c..c606ce4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
@@ -43,6 +43,7 @@
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -107,7 +108,8 @@
protected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,
QSHost qsHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,
KeyguardStateController keyguardStateController, LightBarController lightBarController,
- ConfigurationController configurationController, UiEventLogger uiEventLogger) {
+ @ShadeDisplayAware ConfigurationController configurationController,
+ UiEventLogger uiEventLogger) {
super(view);
mTileQueryHelper = tileQueryHelper;
mQsHost = qsHost;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/flags/QsInCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/flags/QsInCompose.kt
new file mode 100644
index 0000000..3067ccb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/flags/QsInCompose.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.flags
+
+import com.android.systemui.flags.RefactorFlagUtils
+import com.android.systemui.shade.shared.flag.DualShade
+
+/**
+ * Object to help check if the new QS ui should be used. This is true if either [QSComposeFragment]
+ * or [DualShade] are enabled.
+ */
+object QsInCompose {
+
+ /**
+ * This is not a real flag name, but a representation of the allowed flag names. Should not be
+ * used with test annotations.
+ */
+ private val flagName = "${QSComposeFragment.FLAG_NAME}|${DualShade.FLAG_NAME}"
+
+ @JvmStatic
+ inline val isEnabled: Boolean
+ get() = QSComposeFragment.isEnabled || DualShade.isEnabled
+
+ @JvmStatic
+ fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, flagName)
+
+ @JvmStatic fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, flagName)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index bec6581..0cee7dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -42,6 +42,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.res.R;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.LocationController;
@@ -78,7 +79,7 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- ConfigurationController configurationController,
+ @ShadeDisplayAware ConfigurationController configurationController,
BatteryController batteryController,
LocationController locationController
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index 4806c3f..8f870d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -18,7 +18,7 @@
import android.os.UserHandle
import android.service.quicksettings.Tile
-import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
@@ -28,7 +28,6 @@
import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.di.QSTileScope
import com.android.systemui.user.data.repository.UserRepository
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,6 +44,7 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
+import com.android.app.tracing.coroutines.launchTraced as launch
@QSTileScope
@OptIn(ExperimentalCoroutinesApi::class)
@@ -64,7 +64,7 @@
private val bindingFlow =
mutableUserFlow
.flatMapLatest { user ->
- conflatedCallbackFlow {
+ ConflatedCallbackFlow.conflatedCallbackFlow {
serviceInteractor.setUser(user)
// Wait for the CustomTileInteractor to become initialized first, because
@@ -79,7 +79,7 @@
defaultsRepository.requestNewDefaults(
user,
tileSpec.componentName,
- true,
+ true
)
}
.launchIn(this)
@@ -99,7 +99,7 @@
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>,
+ triggers: Flow<DataUpdateTrigger>
): Flow<CustomTileDataModel> {
tileScope.launch { mutableUserFlow.emit(user) }
return bindingFlow.combine(triggers) { _, _ -> }.flatMapLatest { dataFlow(user) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
index f9a1ad5..ab3862b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
@@ -25,7 +25,6 @@
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
@@ -36,17 +35,14 @@
import dagger.assisted.AssistedInject
import java.io.PrintWriter
import java.util.concurrent.CopyOnWriteArraySet
-import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectIndexed
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.takeWhile
-import kotlinx.coroutines.launch
// TODO(b/http://b/299909989): Use QSTileViewModel directly after the rollout
class QSTileViewModelAdapter
@@ -55,7 +51,6 @@
@Application private val applicationScope: CoroutineScope,
private val qsHost: QSHost,
@Assisted private val qsTileViewModel: QSTileViewModel,
- @UiBackground private val uiBgDispatcher: CoroutineDispatcher,
) : QSTile, Dumpable {
private val context
@@ -167,25 +162,19 @@
override fun setListening(client: Any?, listening: Boolean) {
client ?: return
if (listening) {
- applicationScope.launch(uiBgDispatcher) {
- val shouldStartMappingJob =
- listeningClients.add(client) // new client
- && listeningClients.size == 1 // first client
-
- if (shouldStartMappingJob) {
- stateJob =
- qsTileViewModel.state
- .filterNotNull()
- .map { mapState(context, it, qsTileViewModel.config) }
- .onEach { legacyState ->
- val changed = legacyState.copyTo(cachedState)
- if (changed) {
- callbacks.forEach { it.onStateChanged(legacyState) }
- }
+ val clientWasNotAlreadyListening = listeningClients.add(client)
+ if (clientWasNotAlreadyListening && listeningClients.size == 1) {
+ stateJob =
+ qsTileViewModel.state
+ .filterNotNull()
+ .map { mapState(context, it, qsTileViewModel.config) }
+ .onEach { legacyState ->
+ val changed = legacyState.copyTo(cachedState)
+ if (changed) {
+ callbacks.forEach { it.onStateChanged(legacyState) }
}
- .flowOn(uiBgDispatcher)
- .launchIn(applicationScope)
- }
+ }
+ .launchIn(applicationScope)
}
} else {
listeningClients.remove(client)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index e441a23..e36e40d 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -29,6 +29,7 @@
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
@@ -98,6 +99,7 @@
Scenes.Shade.takeUnless { DualShade.isEnabled },
),
initialSceneKey = Scenes.Gone,
+ transitions = SceneContainerTransitions,
overlayKeys =
listOfNotNull(
Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 4beec10..fe01452 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -29,6 +29,7 @@
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
@@ -106,6 +107,7 @@
Scenes.Shade.takeUnless { DualShade.isEnabled },
),
initialSceneKey = Scenes.Lockscreen,
+ transitions = SceneContainerTransitions,
overlayKeys =
listOfNotNull(
Overlays.NotificationsShade.takeIf { DualShade.isEnabled },
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
index 16ed59f4..c1646b8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ShadelessSceneContainerFrameworkModule.kt
@@ -20,6 +20,7 @@
import com.android.systemui.scene.domain.resolver.HomeSceneFamilyResolverModule
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import dagger.Module
import dagger.Provides
@@ -35,7 +36,7 @@
// List SceneResolver modules for supported SceneFamilies
HomeSceneFamilyResolverModule::class,
- ],
+ ]
)
object ShadelessSceneContainerFrameworkModule {
@@ -46,20 +47,12 @@
return SceneContainerConfig(
// Note that this list is in z-order. The first one is the bottom-most and the
// last one is top-most.
- sceneKeys =
- listOf(
- Scenes.Gone,
- Scenes.Lockscreen,
- Scenes.Bouncer,
- ),
+ sceneKeys = listOf(Scenes.Gone, Scenes.Lockscreen, Scenes.Bouncer),
initialSceneKey = Scenes.Lockscreen,
+ transitions = SceneContainerTransitions,
overlayKeys = emptyList(),
navigationDistances =
- mapOf(
- Scenes.Gone to 0,
- Scenes.Lockscreen to 0,
- Scenes.Bouncer to 1,
- )
+ mapOf(Scenes.Gone to 0, Scenes.Lockscreen to 0, Scenes.Bouncer to 1),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
index 2311e47..ce7be83 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
@@ -18,6 +18,7 @@
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
+import com.android.compose.animation.scene.SceneTransitions
/** Models the configuration of the scene container. */
data class SceneContainerConfig(
@@ -38,6 +39,9 @@
*/
val initialSceneKey: SceneKey,
+ /** Transition definitions to be used when animating between scene transitions. */
+ val transitions: SceneTransitions,
+
/**
* The keys to all overlays in the container, sorted by z-order such that the last one renders
* on top of all previous ones. Overlay keys within the same container must not repeat but it's
@@ -61,7 +65,7 @@
* Note that this is not the z-order of rendering; that's determined by the order of declaration
* of scenes in the [sceneKeys] list.
*/
- val navigationDistances: Map<SceneKey, Int>
+ val navigationDistances: Map<SceneKey, Int>,
) {
init {
check(sceneKeys.isNotEmpty()) { "A container must have at least one scene key." }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 1e3a233..1c15c74 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -196,6 +196,7 @@
sceneByKey = sceneByKey,
overlayByKey = overlayByKey,
initialSceneKey = containerConfig.initialSceneKey,
+ sceneTransitions = containerConfig.transitions,
dataSourceDelegator = dataSourceDelegator,
qsSceneAdapter = qsSceneAdapter,
)
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 8c004c4..6844f05 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -48,7 +48,7 @@
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel;
import com.android.systemui.compose.ComposeInitializer;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.qs.flags.QSComposeFragment;
+import com.android.systemui.qs.flags.QsInCompose;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
@@ -96,7 +96,7 @@
super.onCreate(savedInstanceState);
setWindowAttributes();
View view;
- if (!QSComposeFragment.isEnabled()) {
+ if (!QsInCompose.isEnabled()) {
setContentView(R.layout.brightness_mirror_container);
view = findViewById(R.id.brightness_mirror_container);
setDialogContent((FrameLayout) view);
@@ -140,7 +140,7 @@
window.getDecorView();
window.setLayout(WRAP_CONTENT, WRAP_CONTENT);
getTheme().applyStyle(R.style.Theme_SystemUI_QuickSettings, false);
- if (QSComposeFragment.isEnabled()) {
+ if (QsInCompose.isEnabled()) {
window.getDecorView().addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@Override
@@ -217,7 +217,7 @@
@Override
protected void onStart() {
super.onStart();
- if (!QSComposeFragment.isEnabled()) {
+ if (!QsInCompose.isEnabled()) {
mBrightnessController.registerCallbacks();
}
MetricsLogger.visible(this, MetricsEvent.BRIGHTNESS_DIALOG);
@@ -241,7 +241,7 @@
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsEvent.BRIGHTNESS_DIALOG);
- if (!QSComposeFragment.isEnabled()) {
+ if (!QsInCompose.isEnabled()) {
mBrightnessController.unregisterCallbacks();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 4d77e3e..41d56c5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -27,9 +27,10 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.Region;
-import android.os.Build;
import android.os.RemoteException;
import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.util.Log;
import android.view.Display;
import android.view.IWindow;
@@ -73,6 +74,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.settings.SecureSettings;
import dagger.Lazy;
@@ -130,6 +132,7 @@
private final SysuiColorExtractor mColorExtractor;
private final NotificationShadeWindowModel mNotificationShadeWindowModel;
+ private final SecureSettings mSecureSettings;
/**
* Layout params would be aggregated and dispatched all at once if this is > 0.
*
@@ -163,6 +166,7 @@
Lazy<SelectedUserInteractor> userInteractor,
UserTracker userTracker,
NotificationShadeWindowModel notificationShadeWindowModel,
+ SecureSettings secureSettings,
Lazy<CommunalInteractor> communalInteractor) {
mContext = context;
mWindowRootViewComponentFactory = windowRootViewComponentFactory;
@@ -178,6 +182,7 @@
mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
mNotificationShadeWindowModel = notificationShadeWindowModel;
+ mSecureSettings = secureSettings;
// prefix with {slow} to make sure this dumps at the END of the critical section.
dumpManager.registerCriticalDumpable("{slow}NotificationShadeWindowControllerImpl", this);
mAuthController = authController;
@@ -400,7 +405,7 @@
(long) mLpChanged.preferredMaxDisplayRefreshRate);
}
- if (state.bouncerShowing && !isDebuggable()) {
+ if (state.bouncerShowing && !isSecureWindowsDisabled()) {
mLpChanged.flags |= LayoutParams.FLAG_SECURE;
} else {
mLpChanged.flags &= ~LayoutParams.FLAG_SECURE;
@@ -413,8 +418,11 @@
}
}
- protected boolean isDebuggable() {
- return Build.IS_DEBUGGABLE;
+ private boolean isSecureWindowsDisabled() {
+ return mSecureSettings.getIntForUser(
+ Settings.Secure.DISABLE_SECURE_WINDOWS,
+ 0,
+ UserHandle.USER_CURRENT) == 1;
}
private void adjustScreenOrientation(NotificationShadeWindowState state) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index 1b22ee4..bb96b0b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -31,9 +31,12 @@
@Module
interface ShadeDisplayPolicyModule {
+
+ @Binds fun provideDefaultPolicy(impl: StatusBarTouchShadeDisplayPolicy): ShadeDisplayPolicy
+
@IntoSet
@Binds
- fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+ fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
@IntoSet
@Binds
@@ -41,5 +44,9 @@
impl: AnyExternalShadeDisplayPolicy
): ShadeDisplayPolicy
- @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
+ @Binds
+ @IntoSet
+ fun provideStatusBarTouchShadeDisplayPolicy(
+ impl: StatusBarTouchShadeDisplayPolicy
+ ): ShadeDisplayPolicy
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
index 13e7664..d43aad7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/SpecificDisplayIdPolicy.kt
@@ -22,12 +22,11 @@
import kotlinx.coroutines.flow.StateFlow
/** Policy to specify a display id explicitly. */
-open class SpecificDisplayIdPolicy(displayId: Int) : ShadeDisplayPolicy {
- override val name: String
- get() = "display_${displayId}_policy"
+open class SpecificDisplayIdPolicy(id: Int) : ShadeDisplayPolicy {
+ override val name: String = "display_${id}_policy"
- override val displayId: StateFlow<Int> = MutableStateFlow(displayId)
+ override val displayId: StateFlow<Int> = MutableStateFlow(id)
}
-class DefaultShadeDisplayPolicy @Inject constructor() :
+class DefaultDisplayShadePolicy @Inject constructor() :
SpecificDisplayIdPolicy(Display.DEFAULT_DISPLAY)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
new file mode 100644
index 0000000..22e9487
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.shade.display
+
+import android.util.Log
+import android.view.Display
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/**
+ * Moves the shade on the last display that received a status bar touch.
+ *
+ * If the display is removed, falls back to the default one.
+ */
+@SysUISingleton
+class StatusBarTouchShadeDisplayPolicy
+@Inject
+constructor(displayRepository: DisplayRepository, @Background val backgroundScope: CoroutineScope) :
+ ShadeDisplayPolicy {
+ override val name: String
+ get() = "status_bar_latest_touch"
+
+ private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
+ private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds
+
+ override val displayId: StateFlow<Int>
+ get() = currentDisplayId
+
+ private var removalListener: Job? = null
+
+ /** Called when the status bar on the given display is touched. */
+ fun onStatusBarTouched(statusBarDisplayId: Int) {
+ ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ if (statusBarDisplayId !in availableDisplayIds.value) {
+ Log.e(TAG, "Got touch on unknown display $statusBarDisplayId")
+ return
+ }
+ currentDisplayId.value = statusBarDisplayId
+ if (removalListener == null) {
+ // Lazy start this at the first invocation. it's fine to let it run also when the policy
+ // is not selected anymore, as the job doesn't do anything until someone subscribes to
+ // displayId.
+ removalListener = monitorDisplayRemovals()
+ }
+ }
+
+ private fun monitorDisplayRemovals(): Job {
+ return backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#monitorDisplayRemovals") {
+ currentDisplayId.subscriptionCount
+ .map { it > 0 }
+ .distinctUntilChanged()
+ // When Active is false, no collect happens, and the old one is cancelled.
+ // This is needed to prevent "availableDisplayIds" collection while nobody is
+ // listening at the flow provided by this class.
+ .collectLatest { active ->
+ if (active) {
+ availableDisplayIds.collect { availableIds ->
+ if (currentDisplayId.value !in availableIds) {
+ currentDisplayId.value = Display.DEFAULT_DISPLAY
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "StatusBarTouchDisplayPolicy"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 6fd2d3f..c997ac5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -768,6 +768,8 @@
Intent.CATEGORY_APP_EMAIL,
Intent.CATEGORY_APP_CALENDAR,
Intent.CATEGORY_APP_MAPS,
+ Intent.CATEGORY_APP_MUSIC,
+ Intent.CATEGORY_APP_MESSAGING,
Intent.CATEGORY_APP_CALCULATOR,
};
String[] shortcutLabels = {
@@ -776,15 +778,19 @@
mContext.getString(R.string.keyboard_shortcut_group_applications_email),
mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
mContext.getString(R.string.keyboard_shortcut_group_applications_maps),
+ mContext.getString(R.string.keyboard_shortcut_group_applications_music),
+ mContext.getString(R.string.keyboard_shortcut_group_applications_sms),
mContext.getString(R.string.keyboard_shortcut_group_applications_calculator)
};
int[] keyCodes = {
KeyEvent.KEYCODE_B,
- KeyEvent.KEYCODE_P,
- KeyEvent.KEYCODE_E,
KeyEvent.KEYCODE_C,
+ KeyEvent.KEYCODE_E,
+ KeyEvent.KEYCODE_K,
KeyEvent.KEYCODE_M,
+ KeyEvent.KEYCODE_P,
+ KeyEvent.KEYCODE_S,
KeyEvent.KEYCODE_U,
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index 52a79d3..0de4ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -132,13 +132,10 @@
}
if (mWifiPickerTracker != null) {
- mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
+ mWifiPickerTracker.onStop();
}
Context context = mContext.createContextAsUser(UserHandle.of(newUserId), /* flags= */ 0);
mWifiPickerTracker = mWifiPickerTrackerFactory.create(context, mLifecycle, this, TAG);
- if (!mCallbacks.isEmpty()) {
- mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED));
- }
}
@Override
@@ -147,7 +144,11 @@
if (DEBUG) Log.d(TAG, "addCallback " + callback);
mCallbacks.add(callback);
if (mCallbacks.size() == 1) {
- mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED));
+ if (mWifiPickerTrackerFactory.isSupported()) {
+ mWifiPickerTracker.onStart();
+ } else {
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.STARTED));
+ }
}
}
@@ -157,7 +158,11 @@
if (DEBUG) Log.d(TAG, "removeCallback " + callback);
mCallbacks.remove(callback);
if (mCallbacks.isEmpty()) {
- mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
+ if (mWifiPickerTrackerFactory.isSupported()) {
+ mWifiPickerTracker.onStop();
+ } else {
+ mMainExecutor.execute(() -> mLifecycle.setCurrentState(Lifecycle.State.CREATED));
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index 9f5a311..f91c5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -242,10 +242,10 @@
private fun updateBubblesVisibility(statusBarVisible: Boolean) {
if (displayId != Display.DEFAULT_DISPLAY) {
+ // Bubbles are currently only supported on the default display.
return
}
bubblesOptional.ifPresent { bubbles: Bubbles ->
- // TODO(b/373311537): per display implementation of Bubbles
bubbles.onStatusBarVisibilityChanged(statusBarVisible)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 60d846e..2d6ed70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -55,6 +55,7 @@
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.NotificationChannels;
@@ -84,7 +85,7 @@
@Inject
public InstantAppNotifier(
- Context context,
+ @ShadeDisplayAware Context context,
CommandQueue commandQueue,
UserTracker userTracker,
@Main Executor mainExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index b67092c..0569074 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -17,98 +17,16 @@
package com.android.systemui.statusbar.notification
import android.content.Context
-import android.provider.DeviceConfig
-import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
-import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
-import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
-import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
-import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
-import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
-import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS
-import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
-import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
-import com.android.systemui.statusbar.notification.stack.PriorityBucket
-import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.Utils
import javax.inject.Inject
-private var sUsePeopleFiltering: Boolean? = null
-
-/** Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. */
@SysUISingleton
class NotificationSectionsFeatureManager
@Inject
-constructor(val proxy: DeviceConfigProxy, val context: Context) {
-
- fun isFilteringEnabled(): Boolean {
- return usePeopleFiltering(proxy)
- }
+constructor(val context: Context) {
fun isMediaControlsEnabled(): Boolean {
return Utils.useQsMediaPlayer(context)
}
-
- fun getNotificationBuckets(): IntArray {
- if (
- PriorityPeopleSection.isEnabled ||
- NotificationMinimalism.isEnabled ||
- NotificationClassificationFlag.isEnabled
- ) {
- // We don't need this list to be adaptive, it can be the superset of all features.
- return PriorityBucket.getAllInOrder()
- }
- return when {
- isFilteringEnabled() && isMediaControlsEnabled() ->
- intArrayOf(
- BUCKET_HEADS_UP,
- BUCKET_FOREGROUND_SERVICE,
- BUCKET_MEDIA_CONTROLS,
- BUCKET_PEOPLE,
- BUCKET_ALERTING,
- BUCKET_SILENT
- )
- !isFilteringEnabled() && isMediaControlsEnabled() ->
- intArrayOf(
- BUCKET_HEADS_UP,
- BUCKET_FOREGROUND_SERVICE,
- BUCKET_MEDIA_CONTROLS,
- BUCKET_ALERTING,
- BUCKET_SILENT
- )
- isFilteringEnabled() && !isMediaControlsEnabled() ->
- intArrayOf(
- BUCKET_HEADS_UP,
- BUCKET_FOREGROUND_SERVICE,
- BUCKET_PEOPLE,
- BUCKET_ALERTING,
- BUCKET_SILENT
- )
- else -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
- }
- }
-
- fun getNumberOfBuckets(): Int {
- return getNotificationBuckets().size
- }
-
- @VisibleForTesting
- fun clearCache() {
- sUsePeopleFiltering = null
- }
-}
-
-private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
- if (sUsePeopleFiltering == null) {
- sUsePeopleFiltering =
- proxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- NOTIFICATIONS_USE_PEOPLE_FILTERING,
- true
- )
- }
-
- return sUsePeopleFiltering!!
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 3b48b39..d0d2258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -4,7 +4,6 @@
import android.view.View
import androidx.annotation.FloatRange
import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.statusbar.notification.stack.AnimationProperties
import com.android.systemui.statusbar.notification.stack.StackStateAnimator
import kotlin.math.abs
@@ -39,15 +38,11 @@
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
val topCornerRadius: Float
- get() =
- if (NotificationsImprovedHunAnimation.isEnabled) roundableState.topCornerRadius
- else topRoundness * maxRadius
+ get() = roundableState.topCornerRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
val bottomCornerRadius: Float
- get() =
- if (NotificationsImprovedHunAnimation.isEnabled) roundableState.bottomCornerRadius
- else bottomRoundness * maxRadius
+ get() = roundableState.bottomCornerRadius
/** Get and update the current radii */
val updatedRadii: FloatArray
@@ -123,7 +118,7 @@
return requestTopRoundness(
value = value,
sourceType = sourceType,
- animate = roundableState.targetView.isShown
+ animate = roundableState.targetView.isShown,
)
}
@@ -190,7 +185,7 @@
return requestBottomRoundness(
value = value,
sourceType = sourceType,
- animate = roundableState.targetView.isShown
+ animate = roundableState.targetView.isShown,
)
}
@@ -289,11 +284,7 @@
*
* This method reuses the previous [radii] for performance reasons.
*/
- fun updateRadii(
- topCornerRadius: Float,
- bottomCornerRadius: Float,
- radii: FloatArray,
- ) {
+ fun updateRadii(topCornerRadius: Float, bottomCornerRadius: Float, radii: FloatArray) {
if (radii.size != 8) error("Unexpected radiiBuffer size ${radii.size}")
if (radii[0] != topCornerRadius || radii[4] != bottomCornerRadius) {
@@ -312,11 +303,7 @@
*/
class RoundableState
@JvmOverloads
-constructor(
- internal val targetView: View,
- private val roundable: Roundable,
- maxRadius: Float,
-) {
+constructor(internal val targetView: View, private val roundable: Roundable, maxRadius: Float) {
internal var maxRadius = maxRadius
private set
@@ -387,18 +374,12 @@
internal fun isBottomAnimating() = PropertyAnimator.isAnimating(targetView, bottomAnimatable)
/** Set the current top roundness */
- internal fun setTopRoundness(
- value: Float,
- animated: Boolean,
- ) {
+ internal fun setTopRoundness(value: Float, animated: Boolean) {
PropertyAnimator.setProperty(targetView, topAnimatable, value, DURATION, animated)
}
/** Set the current bottom roundness */
- internal fun setBottomRoundness(
- value: Float,
- animated: Boolean,
- ) {
+ internal fun setBottomRoundness(value: Float, animated: Boolean) {
PropertyAnimator.setProperty(targetView, bottomAnimatable, value, DURATION, animated)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index d538f52..5c51ada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -46,7 +46,6 @@
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.util.DumpUtilsKt;
@@ -407,12 +406,7 @@
mAppearAnimator = ValueAnimator.ofFloat(mAppearAnimationFraction,
targetValue);
- if (NotificationsImprovedHunAnimation.isEnabled()
- || NotificationHeadsUpCycling.isEnabled()) {
- mAppearAnimator.setInterpolator(mCurrentAppearInterpolator);
- } else {
- mAppearAnimator.setInterpolator(Interpolators.LINEAR);
- }
+ mAppearAnimator.setInterpolator(mCurrentAppearInterpolator);
mAppearAnimator.setDuration(
(long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
mAppearAnimator.addUpdateListener(animation -> {
@@ -531,10 +525,7 @@
* @param clipSide Which side if view we want to clip from
*/
private void updateAppearRect(ClipSide clipSide) {
- float interpolatedFraction =
- NotificationsImprovedHunAnimation.isEnabled()
- || NotificationHeadsUpCycling.isEnabled() ? mAppearAnimationFraction
- : mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
+ float interpolatedFraction = mAppearAnimationFraction;
mAppearAnimationTranslation = (1.0f - interpolatedFraction) * mAnimationTranslationY;
final int fullHeight = getActualHeight();
float height = fullHeight * interpolatedFraction;
@@ -566,14 +557,6 @@
updateAppearRect(ClipSide.BOTTOM);
}
- private float getInterpolatedAppearAnimationFraction() {
-
- if (mAppearAnimationFraction >= 0) {
- return mCurrentAppearInterpolator.getInterpolation(mAppearAnimationFraction);
- }
- return 1.0f;
- }
-
private void updateAppearAnimationAlpha() {
updateAppearAnimationContentAlpha(
mAppearAnimationFraction,
@@ -643,26 +626,6 @@
super.applyRoundnessAndInvalidate();
}
- @Override
- public float getTopCornerRadius() {
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- return super.getTopCornerRadius();
- }
-
- float fraction = getInterpolatedAppearAnimationFraction();
- return MathUtils.lerp(0, super.getTopCornerRadius(), fraction);
- }
-
- @Override
- public float getBottomCornerRadius() {
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- return super.getBottomCornerRadius();
- }
-
- float fraction = getInterpolatedAppearAnimationFraction();
- return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction);
- }
-
private void applyBackgroundRoundness(float topRadius, float bottomRadius) {
mBackgroundNormal.setRadius(topRadius, bottomRadius);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b7ab996..7ad65fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row;
+import static android.app.Flags.notificationsRedesignTemplates;
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -102,6 +103,7 @@
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -121,7 +123,6 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
@@ -2083,8 +2084,13 @@
R.dimen.notification_min_height_before_p);
mMaxSmallHeightBeforeS = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_before_s);
- mMaxSmallHeight = NotificationUtils.getFontScaledHeight(mContext,
- R.dimen.notification_min_height);
+ if (notificationsRedesignTemplates()) {
+ mMaxSmallHeight = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.notification_2025_min_height);
+ } else {
+ mMaxSmallHeight = NotificationUtils.getFontScaledHeight(mContext,
+ R.dimen.notification_min_height);
+ }
mMaxSmallHeightLarge = NotificationUtils.getFontScaledHeight(mContext,
R.dimen.notification_min_height_increased);
mMaxExpandedHeight = NotificationUtils.getFontScaledHeight(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
index 11db2fc..d5551b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java
@@ -50,6 +50,7 @@
import com.android.internal.logging.InstanceIdSequence;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
+import com.android.systemui.shade.ShadeDisplayAware;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
@@ -69,7 +70,8 @@
private NotificationPanelLogger mNotificationPanelLogger;
@Inject
- public ExpandableNotificationRowDragController(Context context,
+ public ExpandableNotificationRowDragController(
+ @ShadeDisplayAware Context context,
HeadsUpManager headsUpManager,
ShadeController shadeController,
NotificationPanelLogger notificationPanelLogger) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index a323c26..80cf818 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -30,7 +30,6 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.RoundableState;
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
import com.android.systemui.util.DumpUtilsKt;
@@ -123,15 +122,6 @@
return EMPTY_PATH;
}
float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
- if (!NotificationsImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
- float overShoot = topRadius + bottomRadius - height;
- float currentTopRoundness = getTopRoundness();
- float currentBottomRoundness = getBottomRoundness();
- topRadius -= overShoot * currentTopRoundness
- / (currentTopRoundness + currentBottomRoundness);
- bottomRadius -= overShoot * currentBottomRoundness
- / (currentTopRoundness + currentBottomRoundness);
- }
getRoundedRectPath(left, top, right, bottom, topRadius, bottomRadius, mTmpPath);
return mTmpPath;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt
deleted file mode 100644
index 16d35fe..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationsImprovedHunAnimation.kt
+++ /dev/null
@@ -1,53 +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.statusbar.notification.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the notifications improved hun animation flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object NotificationsImprovedHunAnimation {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_IMPROVED_HUN_ANIMATION
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.notificationsImprovedHunAnimation()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 31e4d2c..043d64e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -21,7 +21,6 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.media.controls.ui.controller.KeyguardMediaController
import com.android.systemui.shade.ShadeDisplayAware
-import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.collection.NotificationClassificationFlag
import com.android.systemui.statusbar.notification.collection.render.MediaContainerController
@@ -36,6 +35,7 @@
import com.android.systemui.statusbar.notification.dagger.SocialHeader
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.foldToSparseArray
@@ -51,7 +51,6 @@
internal constructor(
@ShadeDisplayAware private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
- private val sectionsFeatureManager: NotificationSectionsFeatureManager,
private val mediaContainerController: MediaContainerController,
private val notificationRoundnessManager: NotificationRoundnessManager,
@IncomingHeader private val incomingHeaderController: SectionHeaderController,
@@ -120,8 +119,8 @@
}
fun createSectionsForBuckets(): Array<NotificationSection> =
- sectionsFeatureManager
- .getNotificationBuckets()
+ PriorityBucket
+ .getAllInOrder()
.map { NotificationSection(it) }
.toTypedArray()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 223475e..c7b3fd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -118,7 +118,6 @@
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent;
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds;
@@ -3459,11 +3458,8 @@
}
AnimationEvent event = new AnimationEvent(row, type);
event.headsUpFromBottom = onBottom;
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- // TODO(b/283084712) remove this with the flag and update the HUN filters at
- // creation
- event.filter.animateHeight = false;
- }
+ // TODO(b/283084712) remove this and update the HUN filters at creation
+ event.filter.animateHeight = false;
mAnimationEvents.add(event);
if (SPEW) {
Log.v(TAG, "Generating HUN animation event: "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index b251b07..1653029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -41,7 +41,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import java.util.ArrayList;
import java.util.List;
@@ -1065,8 +1064,7 @@
headsUpTranslation);
childState.setYTranslation(inSpaceTranslation + extraTranslation);
cyclingInHunHeight = -1;
- } else
- if (NotificationsImprovedHunAnimation.isEnabled() && !ambientState.isDozing()) {
+ } else if (!ambientState.isDozing()) {
if (shouldHunAppearFromBottom(ambientState, childState)) {
// move to the bottom of the screen
childState.setYTranslation(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index 058233f..4686bef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -37,8 +37,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import com.android.systemui.statusbar.notification.shared.NotificationHeadsUpCycling;
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation;
import java.util.ArrayList;
import java.util.HashSet;
@@ -179,10 +177,7 @@
mHeadsUpDisappearChildren.clear();
mNewEvents.clear();
mNewAddChildren.clear();
- if (NotificationsImprovedHunAnimation.isEnabled()
- || NotificationHeadsUpCycling.isEnabled()) {
- mAnimationProperties.resetCustomInterpolators();
- }
+ mAnimationProperties.resetCustomInterpolators();
}
private void initAnimationProperties(ExpandableView child,
@@ -498,8 +493,7 @@
}
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_CYCLING,
/* isHeadsUpAppear= */ true, onAnimationEnd);
- } else if (NotificationsImprovedHunAnimation.isEnabled()
- && (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR)) {
+ } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) {
mHeadsUpAppearChildren.add(changingView);
mTmpState.copyFrom(changingView.getViewState());
@@ -602,30 +596,6 @@
endRunnable.run();
}
needsCustomAnimation |= needsAnimation;
- } else if (event.animationType == ANIMATION_TYPE_HEADS_UP_APPEAR) {
- NotificationsImprovedHunAnimation.assertInLegacyMode();
- // This item is added, initialize its properties.
- ExpandableViewState viewState = changingView.getViewState();
- mTmpState.copyFrom(viewState);
- if (event.headsUpFromBottom) {
- mTmpState.setYTranslation(mHeadsUpAppearHeightBottom);
- } else {
- Runnable onAnimationEnd = null;
- if (loggable) {
- String finalKey = key;
- onAnimationEnd = () -> mLogger.appearAnimationEnded(finalKey);
- }
- changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR,
- true /* isHeadsUpAppear */, onAnimationEnd);
- }
- mHeadsUpAppearChildren.add(changingView);
- // this only captures HEADS_UP_APPEAR animations, but HUNs can appear with normal
- // ADD animations, which would not be logged here.
- if (loggable) {
- mLogger.logHUNViewAppearing(key);
- }
-
- mTmpState.applyToView(changingView);
} else if (event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR
|| event.animationType == ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
@@ -636,14 +606,10 @@
// transiently
mHostLayout.addTransientView(changingView, 0);
changingView.setTransientContainer(mHostLayout);
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- // StackScrollAlgorithm cannot find this view because it has been removed
- // from the NSSL. To correctly translate the view to the top or bottom of
- // the screen (where it animated from), we need to update its translation.
- mTmpState.setYTranslation(
- getHeadsUpYTranslationStart(event.headsUpFromBottom)
- );
- }
+ // StackScrollAlgorithm cannot find this view because it has been removed
+ // from the NSSL. To correctly translate the view to the top or bottom of
+ // the screen (where it animated from), we need to update its translation.
+ mTmpState.setYTranslation(getHeadsUpYTranslationStart(event.headsUpFromBottom));
endRunnable = changingView::removeFromTransientContainer;
}
@@ -697,14 +663,12 @@
startAnimation, postAnimation,
getGlobalAnimationFinishedListener(), ExpandableView.ClipSide.BOTTOM);
mAnimationProperties.delay += removeAnimationDelay;
- if (NotificationsImprovedHunAnimation.isEnabled()) {
- mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
- mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
- Interpolators.FAST_OUT_SLOW_IN_REVERSE);
- mAnimationProperties.getAnimationFilter().animateY = true;
- mTmpState.animateTo(changingView, mAnimationProperties);
- mAnimationProperties.resetCustomInterpolators();
- }
+ mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
+ mAnimationProperties.setCustomInterpolator(View.TRANSLATION_Y,
+ Interpolators.FAST_OUT_SLOW_IN_REVERSE);
+ mAnimationProperties.getAnimationFilter().animateY = true;
+ mTmpState.animateTo(changingView, mAnimationProperties);
+ mAnimationProperties.resetCustomInterpolators();
} else if (endRunnable != null) {
endRunnable.run();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 01efd5d..f8f29ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -117,7 +117,7 @@
private val interactor: SharedNotificationContainerInteractor,
dumpManager: DumpManager,
@Application applicationScope: CoroutineScope,
- private val context: Context,
+ @ShadeDisplayAware private val context: Context,
@ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 677ed9f..6cad68f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -27,7 +27,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.dagger.qualifiers.DisplaySpecific;
-import com.android.systemui.flags.FeatureFlagsClassic;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.res.R;
@@ -44,6 +43,7 @@
import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
@@ -98,7 +98,7 @@
@VisibleForTesting
float mAppearFraction;
private ExpandableNotificationRow mTrackedChild;
- private boolean mShown;
+ private PinnedStatus mPinnedStatus = PinnedStatus.NotPinned;
private final ViewClippingUtil.ClippingParameters mParentClippingParams =
new ViewClippingUtil.ClippingParameters() {
@Override
@@ -108,7 +108,6 @@
};
private boolean mAnimationsEnabled = true;
private final KeyguardStateController mKeyguardStateController;
- private final FeatureFlagsClassic mFeatureFlags;
private final HeadsUpNotificationIconInteractor mHeadsUpNotificationIconInteractor;
@VisibleForTesting
@@ -127,7 +126,6 @@
NotificationRoundnessManager notificationRoundnessManager,
HeadsUpStatusBarView headsUpStatusBarView,
Clock clockView,
- FeatureFlagsClassic featureFlags,
HeadsUpNotificationIconInteractor headsUpNotificationIconInteractor,
@Named(OPERATOR_NAME_FRAME_VIEW) Optional<View> operatorNameViewOptional) {
super(headsUpStatusBarView);
@@ -145,7 +143,6 @@
mStackScrollerController = stackScrollerController;
mShadeViewController = shadeViewController;
- mFeatureFlags = featureFlags;
mHeadsUpNotificationIconInteractor = headsUpNotificationIconInteractor;
mStackScrollerController.setHeadsUpAppearanceController(this);
mClockView = clockView;
@@ -157,7 +154,7 @@
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (shouldBeVisible()) {
- updateTopEntry("onLayoutChange");
+ updateTopEntry();
// trigger scroller to notify the latest panel translation
mStackScrollerController.requestLayout();
@@ -207,7 +204,7 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- updateTopEntry("onHeadsUpPinned");
+ updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -218,7 +215,7 @@
mPhoneStatusBarTransitions.onHeadsUpStateChanged(isHeadsUp);
}
- private void updateTopEntry(String reason) {
+ private void updateTopEntry() {
NotificationEntry newEntry = null;
if (shouldBeVisible()) {
newEntry = mHeadsUpManager.getTopEntry();
@@ -226,27 +223,29 @@
NotificationEntry previousEntry = mView.getShowingEntry();
mView.setEntry(newEntry);
if (newEntry != previousEntry) {
- boolean animateIsolation = false;
if (newEntry == null) {
// no heads up anymore, lets start the disappear animation
-
- setShown(false);
- animateIsolation = !isExpanded();
+ setPinnedStatus(PinnedStatus.NotPinned);
} else if (previousEntry == null) {
// We now have a headsUp and didn't have one before. Let's start the disappear
// animation
- setShown(true);
- animateIsolation = !isExpanded();
+ setPinnedStatus(PinnedStatus.PinnedBySystem);
}
- mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(
- newEntry == null ? null : newEntry.getRepresentativeEntry().getKey());
+
+ String isolatedIconKey;
+ if (newEntry != null) {
+ isolatedIconKey = newEntry.getRepresentativeEntry().getKey();
+ } else {
+ isolatedIconKey = null;
+ }
+ mHeadsUpNotificationIconInteractor.setIsolatedIconNotificationKey(isolatedIconKey);
}
}
- private void setShown(boolean isShown) {
- if (mShown != isShown) {
- mShown = isShown;
- if (isShown) {
+ private void setPinnedStatus(PinnedStatus pinnedStatus) {
+ if (mPinnedStatus != pinnedStatus) {
+ mPinnedStatus = pinnedStatus;
+ if (pinnedStatus.isPinned()) {
updateParentClipping(false /* shouldClip */);
mView.setVisibility(View.VISIBLE);
show(mView);
@@ -330,8 +329,8 @@
}
@VisibleForTesting
- public boolean isShown() {
- return mShown;
+ public PinnedStatus getPinnedStatus() {
+ return mPinnedStatus;
}
/**
@@ -355,7 +354,7 @@
@Override
public void onHeadsUpUnPinned(NotificationEntry entry) {
- updateTopEntry("onHeadsUpUnPinned");
+ updateTopEntry();
updateHeader(entry);
updateHeadsUpAndPulsingRoundness(entry);
}
@@ -373,7 +372,7 @@
updateHeadsUpHeaders();
}
if (isExpanded() != oldIsExpanded) {
- updateTopEntry("setAppearFraction");
+ updateTopEntry();
}
}
@@ -447,11 +446,11 @@
}
public void onStateChanged() {
- updateTopEntry("onStateChanged");
+ updateTopEntry();
}
@Override
public void onFullyHiddenChanged(boolean isFullyHidden) {
- updateTopEntry("onFullyHiddenChanged");
+ updateTopEntry();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 16e023c..f19d707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -39,7 +39,9 @@
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
import com.android.systemui.statusbar.policy.Clock
@@ -52,6 +54,7 @@
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
import com.android.systemui.util.view.ViewUtil
+import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
import javax.inject.Named
@@ -79,6 +82,7 @@
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
+ private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) : ViewController<PhoneStatusBarView>(view) {
private lateinit var battery: BatteryMeterView
@@ -226,6 +230,9 @@
!upOrCancel || shadeController.isExpandedVisible,
)
}
+ if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(context.displayId)
+ }
}
private fun addDarkReceivers() {
@@ -344,6 +351,7 @@
private val statusOverlayHoverListenerFactory: StatusOverlayHoverListenerFactory,
@DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
+ private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
) {
fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
val statusBarMoveFromCenterAnimationController =
@@ -371,6 +379,7 @@
statusOverlayHoverListenerFactory,
darkIconDispatcher,
statusBarContentInsetsProviderStore.defaultDisplay,
+ lazyStatusBarShadeDisplayPolicy,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index a72c83e..6ed2ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -87,7 +87,11 @@
override val lifecycle =
LifecycleRegistry(this).also {
- mainExecutor.execute { it.currentState = Lifecycle.State.CREATED }
+ if (multiuserWifiPickerTrackerSupport()) {
+ mainExecutor.execute { it.currentState = Lifecycle.State.STARTED }
+ } else {
+ mainExecutor.execute { it.currentState = Lifecycle.State.CREATED }
+ }
}
private var wifiPickerTracker: WifiPickerTracker? = null
@@ -178,6 +182,10 @@
trySend(new)
}
}
+
+ // If a WifiPicker already exists, call onStop to begin its shutdown
+ // process in preparation for a new one to be created.
+ wifiPickerTracker?.onStop()
wifiPickerTracker =
wifiPickerTrackerFactory
.create(currentContext, lifecycle, callback, "WifiRepository")
@@ -189,17 +197,7 @@
// costly and should be avoided whenever possible).
this?.disableScanning()
}
-
- // The lifecycle must be STARTED in order for the callback to receive
- // events.
- mainExecutor.execute {
- lifecycle.currentState = Lifecycle.State.STARTED
- }
- awaitClose {
- mainExecutor.execute {
- lifecycle.currentState = Lifecycle.State.CREATED
- }
- }
+ awaitClose { mainExecutor.execute { wifiPickerTracker?.onStop() } }
}
.stateIn(scope, SharingStarted.Eagerly, current)
}
@@ -275,7 +273,6 @@
trySend(new)
}
}
-
wifiPickerTracker =
wifiPickerTrackerFactory
.create(applicationContext, lifecycle, callback, "WifiRepository")
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 5c0cc81..39b434ad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -21,20 +21,24 @@
import android.graphics.PixelFormat
import android.os.Bundle
import android.view.MotionEvent
+import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import com.android.app.tracing.coroutines.coroutineScopeTraced
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
-import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
import javax.inject.Inject
+import kotlinx.coroutines.awaitCancellation
class VolumeDialog
@Inject
constructor(
@Application context: Context,
- private val viewBinder: VolumeDialogViewBinder,
+ private val componentFactory: VolumeDialogComponent.Factory,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
) : Dialog(context, R.style.Theme_SystemUI_Dialog_Volume) {
@@ -64,7 +68,14 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.volume_dialog)
- viewBinder.bind(this)
+ requireViewById<View>(R.id.volume_dialog_root).repeatWhenAttached {
+ coroutineScopeTraced("[Volume]dialog") {
+ val component = componentFactory.create(this)
+ with(component.volumeDialogViewBinder()) { bind(this@VolumeDialog) }
+
+ awaitCancellation()
+ }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index b912361..094ec39 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.dialog
+import com.android.app.tracing.coroutines.coroutineScopeTraced
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.VolumeDialog
@@ -23,7 +24,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
-import kotlinx.coroutines.coroutineScope
class VolumeDialogPlugin
@Inject
@@ -38,7 +38,7 @@
override fun init(windowType: Int, callback: VolumeDialog.Callback?) {
job =
applicationCoroutineScope.launch {
- coroutineScope {
+ coroutineScopeTraced("[Volume]plugin") {
pluginComponent =
volumeDialogPluginComponentFactory.create(this).also {
it.viewModel().launchVolumeDialog()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
index fb15795..434f6b5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/dagger/VolumeDialogComponent.kt
@@ -20,6 +20,7 @@
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
+import com.android.systemui.volume.dialog.ui.binder.VolumeDialogViewBinder
import dagger.BindsInstance
import dagger.Subcomponent
import kotlinx.coroutines.CoroutineScope
@@ -32,21 +33,21 @@
@Subcomponent(modules = [VolumeDialogModule::class])
interface VolumeDialogComponent {
- /**
- * Provides a coroutine scope to use inside [VolumeDialogScope].
- * [com.android.systemui.volume.dialog.VolumeDialogPlugin] manages the lifecycle of this scope.
- * It's cancelled when the dialog is disposed. This helps to free occupied resources when volume
- * dialog is not shown.
- */
- @VolumeDialog fun coroutineScope(): CoroutineScope
-
- @VolumeDialogScope fun volumeDialog(): com.android.systemui.volume.dialog.VolumeDialog
+ fun volumeDialogViewBinder(): VolumeDialogViewBinder
fun sliderComponentFactory(): VolumeDialogSliderComponent.Factory
@Subcomponent.Factory
interface Factory {
- fun create(@BindsInstance @VolumeDialog scope: CoroutineScope): VolumeDialogComponent
+ fun create(
+ /**
+ * Provides a coroutine scope to use inside [VolumeDialogScope].
+ * [com.android.systemui.volume.dialog.VolumeDialogPlugin] manages the lifecycle of this
+ * scope. It's cancelled when the dialog is disposed. This helps to free occupied
+ * resources when volume dialog is not shown.
+ */
+ @BindsInstance @VolumeDialog scope: CoroutineScope
+ ): VolumeDialogComponent
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
index 7fd177d..e033624 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
@@ -18,9 +18,6 @@
import android.annotation.SuppressLint
import android.view.View
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderTouchesViewModel
@@ -30,22 +27,14 @@
@VolumeDialogSliderScope
class VolumeDialogSliderTouchesViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSliderTouchesViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSliderTouchesViewModel) {
@SuppressLint("ClickableViewAccessibility")
fun bind(view: View) {
with(view.requireViewById<Slider>(R.id.volume_dialog_slider)) {
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogSliderTouchesViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- setOnTouchListener { _, event ->
- viewModel.onTouchEvent(event)
- false
- }
- }
+ setOnTouchListener { _, event ->
+ viewModel.onTouchEvent(event)
+ false
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 1c231b5..f9334df 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -20,9 +20,6 @@
import android.animation.ObjectAnimator
import android.view.View
import android.view.animation.DecelerateInterpolator
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
@@ -33,7 +30,7 @@
import com.google.android.material.slider.Slider
import javax.inject.Inject
import kotlin.math.roundToInt
-import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -43,32 +40,20 @@
class VolumeDialogSliderViewBinder
@Inject
constructor(
- private val viewModelFactory: VolumeDialogSliderViewModel.Factory,
+ private val viewModel: VolumeDialogSliderViewModel,
private val jankListenerFactory: JankListenerFactory,
) {
- fun bind(view: View) {
- with(view) {
- val sliderView: Slider =
- requireViewById<Slider>(R.id.volume_dialog_slider).apply {
- labelBehavior = LabelFormatter.LABEL_GONE
- }
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogSliderViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- sliderView.addOnChangeListener { _, value, fromUser ->
- viewModel.setStreamVolume(value.roundToInt(), fromUser)
- }
-
- viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
-
- awaitCancellation()
- }
+ fun CoroutineScope.bind(view: View) {
+ val sliderView: Slider =
+ view.requireViewById<Slider>(R.id.volume_dialog_slider).apply {
+ labelBehavior = LabelFormatter.LABEL_GONE
}
+ sliderView.addOnChangeListener { _, value, fromUser ->
+ viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
+
+ viewModel.model.onEach { it.bindToSlider(sliderView) }.launchIn(this)
}
private suspend fun VolumeDialogStreamModel.bindToSlider(slider: Slider) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index 1b2b4ff..242845a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -21,58 +21,47 @@
import android.view.ViewGroup
import androidx.annotation.LayoutRes
import androidx.compose.ui.util.fastForEachIndexed
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlidersViewModel
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@VolumeDialogScope
class VolumeDialogSlidersViewBinder
@Inject
-constructor(private val viewModelFactory: VolumeDialogSlidersViewModel.Factory) {
+constructor(private val viewModel: VolumeDialogSlidersViewModel) {
- fun bind(view: View) {
- with(view) {
- val floatingSlidersContainer: ViewGroup =
- requireViewById(R.id.volume_dialog_floating_sliders_container)
- val mainSliderContainer: View =
- requireViewById(R.id.volume_dialog_main_slider_container)
- repeatWhenAttached {
- viewModel(
- traceName = "VolumeDialogSlidersViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { viewModelFactory.create() },
- ) { viewModel ->
- viewModel.sliders
- .onEach { uiModel ->
- uiModel.sliderComponent.bindSlider(mainSliderContainer)
+ fun CoroutineScope.bind(view: View) {
+ val floatingSlidersContainer: ViewGroup =
+ view.requireViewById(R.id.volume_dialog_floating_sliders_container)
+ val mainSliderContainer: View =
+ view.requireViewById(R.id.volume_dialog_main_slider_container)
+ viewModel.sliders
+ .onEach { uiModel ->
+ bindSlider(uiModel.sliderComponent, mainSliderContainer)
- val floatingSliderViewBinders = uiModel.floatingSliderComponent
- floatingSlidersContainer.ensureChildCount(
- viewLayoutId = R.layout.volume_dialog_slider_floating,
- count = floatingSliderViewBinders.size,
- )
- floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent ->
- sliderComponent.bindSlider(
- floatingSlidersContainer.getChildAt(index)
- )
- }
- }
- .launchIn(this)
+ val floatingSliderViewBinders = uiModel.floatingSliderComponent
+ floatingSlidersContainer.ensureChildCount(
+ viewLayoutId = R.layout.volume_dialog_slider_floating,
+ count = floatingSliderViewBinders.size,
+ )
+ floatingSliderViewBinders.fastForEachIndexed { index, sliderComponent ->
+ bindSlider(sliderComponent, floatingSlidersContainer.getChildAt(index))
}
}
- }
+ .launchIn(this)
}
- private fun VolumeDialogSliderComponent.bindSlider(sliderContainer: View) {
- sliderViewBinder().bind(sliderContainer)
- sliderTouchesViewBinder().bind(sliderContainer)
+ private fun CoroutineScope.bindSlider(
+ component: VolumeDialogSliderComponent,
+ sliderContainer: View,
+ ) {
+ with(component.sliderViewBinder()) { bind(sliderContainer) }
+ with(component.sliderTouchesViewBinder()) { bind(sliderContainer) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt
index 144c75d..9126f45 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderTouchesViewModel.kt
@@ -17,20 +17,16 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.view.MotionEvent
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInputEventsInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
+@VolumeDialogSliderScope
class VolumeDialogSliderTouchesViewModel
-@AssistedInject
+@Inject
constructor(private val interactor: VolumeDialogSliderInputEventsInteractor) {
fun onTouchEvent(event: MotionEvent) {
interactor.onTouchEvent(event)
}
-
- @AssistedFactory
- interface Factory {
- fun create(): VolumeDialogSliderTouchesViewModel
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index cf04d45..6dd5b63 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -21,9 +21,9 @@
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
+import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -49,8 +49,9 @@
private const val VOLUME_UPDATE_GRACE_PERIOD = 1000
@OptIn(ExperimentalCoroutinesApi::class)
+@VolumeDialogSliderScope
class VolumeDialogSliderViewModel
-@AssistedInject
+@Inject
constructor(
private val interactor: VolumeDialogSliderInteractor,
private val visibilityInteractor: VolumeDialogVisibilityInteractor,
@@ -90,10 +91,4 @@
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
-
- @AssistedFactory
- interface Factory {
-
- fun create(): VolumeDialogSliderViewModel
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index d197223..d8e6aec 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -17,10 +17,10 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -28,8 +28,9 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+@VolumeDialogScope
class VolumeDialogSlidersViewModel
-@AssistedInject
+@Inject
constructor(
@VolumeDialog coroutineScope: CoroutineScope,
private val slidersInteractor: VolumeDialogSlidersInteractor,
@@ -47,12 +48,6 @@
}
.stateIn(coroutineScope, SharingStarted.Eagerly, null)
.filterNotNull()
-
- @AssistedFactory
- interface Factory {
-
- fun create(): VolumeDialogSlidersViewModel
- }
}
/** Models slider ui */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index f6c1743..a3166a9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -25,9 +25,6 @@
import android.view.ViewTreeObserver.InternalInsetsInfo
import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.internal.view.RotationPolicy
-import com.android.systemui.lifecycle.WindowLifecycleState
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.lifecycle.viewModel
import com.android.systemui.res.R
import com.android.systemui.util.children
import com.android.systemui.volume.SystemUIInterpolators
@@ -44,7 +41,6 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.launchIn
@@ -61,7 +57,7 @@
@Inject
constructor(
private val volumeResources: VolumeDialogResources,
- private val dialogViewModelFactory: VolumeDialogViewModel.Factory,
+ private val viewModel: VolumeDialogViewModel,
private val jankListenerFactory: JankListenerFactory,
private val tracer: VolumeTracer,
private val volumeDialogRingerViewBinder: VolumeDialogRingerViewBinder,
@@ -69,35 +65,27 @@
private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
) {
- fun bind(dialog: Dialog) {
+ fun CoroutineScope.bind(dialog: Dialog) {
// Root view of the Volume Dialog.
val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root)
root.alpha = 0f
- root.repeatWhenAttached {
- root.viewModel(
- traceName = "VolumeDialogViewBinder",
- minWindowLifecycleState = WindowLifecycleState.ATTACHED,
- factory = { dialogViewModelFactory.create() },
- ) { viewModel ->
- animateVisibility(root, dialog, viewModel.dialogVisibilityModel)
- viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
- viewModel.motionState
- .scan(0) { acc, motionState ->
- // don't animate the initial state
- root.transitionToState(motionState, animate = acc != 0)
- acc + 1
- }
- .launchIn(this)
+ animateVisibility(root, dialog, viewModel.dialogVisibilityModel)
- launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
-
- awaitCancellation()
+ viewModel.dialogTitle.onEach { dialog.window?.setTitle(it) }.launchIn(this)
+ viewModel.motionState
+ .scan(0) { acc, motionState ->
+ // don't animate the initial state
+ root.transitionToState(motionState, animate = acc != 0)
+ acc + 1
}
- }
- volumeDialogRingerViewBinder.bind(root)
- slidersViewBinder.bind(root)
- settingsButtonViewBinder.bind(root)
+ .launchIn(this)
+
+ launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
+
+ with(volumeDialogRingerViewBinder) { bind(root) }
+ with(slidersViewBinder) { bind(root) }
+ with(settingsButtonViewBinder) { bind(root) }
}
private fun CoroutineScope.animateVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index e858cfe..ff525f46 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -17,14 +17,14 @@
package com.android.systemui.volume.dialog.ui.viewmodel
import com.android.systemui.volume.Events
-import com.android.systemui.volume.dialog.dagger.VolumeDialogComponent
+import com.android.systemui.volume.dialog.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
import javax.inject.Inject
+import javax.inject.Provider
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.mapLatest
@@ -34,8 +34,8 @@
class VolumeDialogPluginViewModel
@Inject
constructor(
- private val componentFactory: VolumeDialogComponent.Factory,
private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+ private val volumeDialogProvider: Provider<VolumeDialog>,
private val logger: VolumeDialogLogger,
) {
@@ -45,7 +45,7 @@
.mapLatest { visibilityModel ->
with(visibilityModel) {
if (this is VolumeDialogVisibilityModel.Visible) {
- showDialog(componentFactory)
+ showDialog()
Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, keyguardLocked)
logger.onShow(reason)
}
@@ -59,16 +59,14 @@
}
}
- private suspend fun showDialog(componentFactory: VolumeDialogComponent.Factory): Unit =
- coroutineScope {
- val volumeDialogComponent: VolumeDialogComponent = componentFactory.create(this)
- val dialog =
- volumeDialogComponent.volumeDialog().apply {
- setOnDismissListener {
- volumeDialogComponent.coroutineScope().cancel()
- dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
- }
+ private fun showDialog() {
+ volumeDialogProvider
+ .get()
+ .apply {
+ setOnDismissListener {
+ dialogVisibilityInteractor.dismissDialog(Events.DISMISS_REASON_UNKNOWN)
}
- dialog.show()
- }
+ }
+ .show()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index 0352799..7a6ede4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.devicePosture
import com.android.systemui.statusbar.policy.onConfigChanged
+import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
@@ -30,9 +31,7 @@
import com.android.systemui.volume.dialog.shared.model.streamLabel
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
@@ -40,9 +39,9 @@
import kotlinx.coroutines.flow.onStart
/** Provides a state for the Volume Dialog. */
-@OptIn(ExperimentalCoroutinesApi::class)
+@VolumeDialogScope
class VolumeDialogViewModel
-@AssistedInject
+@Inject
constructor(
private val context: Context,
dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
@@ -84,9 +83,4 @@
val isHalfOpen = devicePosture == DevicePostureController.DEVICE_POSTURE_HALF_OPENED
return isLandscape && isHalfOpen
}
-
- @AssistedFactory
- interface Factory {
- fun create(): VolumeDialogViewModel
- }
}
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 fa88f62..ad5f960 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -930,6 +930,10 @@
}
private void advanceTimeBy(long timeDelta) {
+ if (timeDelta == mWaitAnimationDuration) {
+ mAnimatorTestRule.advanceAnimationDuration(timeDelta);
+ return;
+ }
mAnimatorTestRule.advanceTimeBy(timeDelta);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 7c0c5c2..4553f98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -59,6 +59,7 @@
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
+import android.widget.SeekBar;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -81,6 +82,7 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
@@ -544,9 +546,10 @@
OnSeekBarWithIconButtonsChangeListener onChangeListener =
mZoomSeekbar.getOnSeekBarWithIconButtonsChangeListener();
- mZoomSeekbar.setProgress(30);
+ SeekBar mockSeekBar = Mockito.mock(SeekBar.class);
+ when(mockSeekBar.getProgress()).thenReturn(30);
onChangeListener.onUserInteractionFinalized(
- mZoomSeekbar.getSeekbar(),
+ mockSeekBar,
OnSeekBarWithIconButtonsChangeListener.ControlUnitType.SLIDER);
// should trigger callback to update magnifier scale and persist the scale
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index dddaabb..50d0049 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,12 +26,10 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.graphics.PointF;
-
import android.testing.TestableLooper;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.FlingAnimation;
@@ -51,7 +49,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -72,17 +69,13 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
- @Mock
- private AccessibilityManager mAccessibilityManager;
-
@Before
public void setUp() throws Exception {
final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
stubWindowManager);
final SecureSettings secureSettings = TestUtils.mockSecureSettings();
- final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
- secureSettings);
+ final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, secureSettings);
mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
secureSettings));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
index 400b3b3..f4580c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerTest.java
@@ -159,8 +159,7 @@
new WindowMetrics(mDisplayBounds, fakeDisplayInsets(), /* density = */ 0.0f));
doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
- mMenuViewModel = new MenuViewModel(
- mSpyContext, mStubAccessibilityManager, mSecureSettings);
+ mMenuViewModel = new MenuViewModel(mSpyContext, mSecureSettings);
MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
mSpyContext, mStubWindowManager);
mMenuView = spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 929b0aa..67e03e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -273,6 +273,12 @@
"${Contract.AUTHORITY}." +
Contract.FlagsTable.TABLE_NAME
)
+ assertThat(underTest.getType(Contract.RuntimeValuesTable.URI))
+ .isEqualTo(
+ "vnd.android.cursor.dir/vnd." +
+ "${Contract.AUTHORITY}." +
+ Contract.RuntimeValuesTable.TABLE_NAME
+ )
}
@Test
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 b3cccea..492d5f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -277,6 +277,7 @@
() -> mSelectedUserInteractor,
mUserTracker,
mKosmos.getNotificationShadeWindowModel(),
+ mSecureSettings,
mKosmos::getCommunalInteractor);
mFeatureFlags = new FakeFeatureFlags();
mSetFlagsRule.disableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index f7059e2..a64ff321 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModelFactory
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
@@ -70,8 +71,8 @@
mSetFlagsRule.setFlagsParameterization(flags)
}
- val viewId by lazy {
- if (QSComposeFragment.isEnabled) {
+ private val viewId by lazy {
+ if (QsInCompose.isEnabled) {
R.id.brightness_dialog_slider
} else {
R.id.brightness_mirror_container
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
index 382b307..2371ccc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
@@ -1,11 +1,9 @@
package com.android.systemui.statusbar.notification
-import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import org.junit.Assert.assertEquals
@@ -148,7 +146,6 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_radius_maxed_to_height() {
whenever(targetView.height).thenReturn(10)
roundable.requestRoundness(1f, 1f, SOURCE1)
@@ -157,7 +154,6 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_topRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
roundable.requestRoundness(1f, 0f, SOURCE1)
@@ -166,7 +162,6 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_bottomRadius_maxed_to_height() {
whenever(targetView.height).thenReturn(5)
roundable.requestRoundness(0f, 1f, SOURCE1)
@@ -175,7 +170,6 @@
}
@Test
- @EnableFlags(NotificationsImprovedHunAnimation.FLAG_NAME)
fun getCornerRadii_radii_kept() {
whenever(targetView.height).thenReturn(100)
roundable.requestRoundness(1f, 1f, SOURCE1)
@@ -188,16 +182,9 @@
assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius)
}
- class FakeRoundable(
- targetView: View,
- radius: Float = MAX_RADIUS,
- ) : Roundable {
+ class FakeRoundable(targetView: View, radius: Float = MAX_RADIUS) : Roundable {
override val roundableState =
- RoundableState(
- targetView = targetView,
- roundable = this,
- maxRadius = radius,
- )
+ RoundableState(targetView = targetView, roundable = this, maxRadius = radius)
override val clipHeight: Int
get() = roundableState.targetView.height
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 69efa87..abdd797 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -33,6 +33,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.systemui.Flags as AconfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.flags.FeatureFlags
@@ -44,6 +45,7 @@
import com.android.systemui.shade.ShadeLogger
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.StatusBarLongPressGestureDetector
+import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.repository.fakeStatusBarContentInsetsProviderStore
@@ -60,12 +62,14 @@
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
+import dagger.Lazy
import java.util.Optional
import javax.inject.Provider
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -98,6 +102,7 @@
@Mock private lateinit var shadeLogger: ShadeLogger
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var mStatusBarLongPressGestureDetector: StatusBarLongPressGestureDetector
+ @Mock private lateinit var statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy
private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var view: PhoneStatusBarView
@@ -329,6 +334,30 @@
}
@Test
+ @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_actionDown_propagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(mContext.displayId))
+ }
+
+ @Test
+ @EnableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_actionUp_notPropagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ }
+
+ @Test
+ @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun onTouch_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
+ controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ }
+
+ @Test
fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
@@ -402,6 +431,7 @@
mStatusOverlayHoverListenerFactory,
fakeDarkIconDispatcher,
statusBarContentInsetsProviderStore,
+ Lazy { statusBarTouchShadeDisplayPolicy },
)
.create(view)
.also { it.init() }
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 a1c9022e..eb91b1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -162,6 +162,7 @@
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.util.FakeEventLog;
import com.android.systemui.util.settings.FakeGlobalSettings;
+import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.Flags;
@@ -442,6 +443,7 @@
() -> mSelectedUserInteractor,
mUserTracker,
mNotificationShadeWindowModel,
+ new FakeSettings(),
mKosmos::getCommunalInteractor
);
mNotificationShadeWindowController.fetchWindowRootView();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
index e2fc44f..eb0aee4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/AnimatorTestRule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.animation
+import android.animation.Animator
import java.util.function.Consumer
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -71,6 +72,22 @@
}
/**
+ * This is similar to [advanceTimeBy] but it expects to reach the end of an animation. This call
+ * may produce 2 frames for the last animation frame and end animation callback.
+ *
+ * @param durationMs the duration that is greater than or equal to the animation duration.
+ */
+ fun advanceAnimationDuration(durationMs: Long) {
+ advanceTimeBy(durationMs)
+ if (Animator.isPostNotifyEndListenerEnabled()) {
+ // If the post-end-callback is enabled, the AnimatorListener#onAnimationEnd will be
+ // called on the next frame of last animation frame. So trigger additional doFrame to
+ // ensure the end callback method is called (by android.animation.AnimatorTestRule).
+ advanceTimeBy(0)
+ }
+ }
+
+ /**
* Returns the current time in milliseconds tracked by the AnimationHandlers. Note that this is
* a different time than the time tracked by {@link SystemClock}.
*/
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index ddcc926..3fc60e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -48,6 +48,7 @@
/** Fake [DisplayRepository] implementation for testing. */
class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
+ private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
private val pendingDisplayFlow =
MutableSharedFlow<DisplayRepository.PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
@@ -63,11 +64,13 @@
suspend fun addDisplay(display: Display) {
flow.value += display
+ displayIdFlow.value += display.displayId
displayAdditionEventFlow.emit(display)
}
suspend fun removeDisplay(displayId: Int) {
flow.value = flow.value.filter { it.displayId != displayId }.toSet()
+ displayIdFlow.value = displayIdFlow.value.filter { it != displayId }.toSet()
displayRemovalEventFlow.emit(displayId)
}
@@ -83,10 +86,13 @@
override val displays: StateFlow<Set<Display>>
get() = flow
+ override val displayIds: StateFlow<Set<Int>>
+ get() = displayIdFlow
+
override val pendingDisplay: Flow<DisplayRepository.PendingDisplay?>
get() = pendingDisplayFlow
- val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ private val _defaultDisplayOff: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val defaultDisplayOff: Flow<Boolean>
get() = _defaultDisplayOff.asStateFlow()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index e7672ff..2bff0c6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -22,6 +22,7 @@
import android.hardware.input.fakeInputManager
import android.view.windowManager
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
import com.android.systemui.keyboard.shortcut.data.repository.InputGestureMaps
@@ -106,16 +107,21 @@
val Kosmos.inputGestureMaps by Kosmos.Fixture { InputGestureMaps(applicationContext) }
+val Kosmos.customInputGesturesRepository by Kosmos.Fixture {
+ CustomInputGesturesRepository(userTracker, testDispatcher)
+}
+
val Kosmos.customShortcutCategoriesRepository by
Kosmos.Fixture {
CustomShortcutCategoriesRepository(
shortcutHelperStateRepository,
- userTracker,
applicationCoroutineScope,
testDispatcher,
shortcutCategoriesUtils,
applicationContext,
inputGestureMaps,
+ customInputGesturesRepository,
+ fakeInputManager.inputManager
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
index de9f629..a908765 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
@@ -18,7 +18,6 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.util.mockito.mock
val Kosmos.qsTileViewModelAdaperFactory by
@@ -29,7 +28,6 @@
applicationCoroutineScope,
mock(),
qsTileViewModel,
- testDispatcher,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 3300c96..0eca818 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -13,6 +13,7 @@
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.FakeOverlay
+import com.android.systemui.scene.ui.composable.SceneContainerTransitions
import com.android.systemui.scene.ui.viewmodel.SceneContainerHapticsViewModel
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
@@ -60,6 +61,7 @@
SceneContainerConfig(
sceneKeys = sceneKeys,
initialSceneKey = initialSceneKey,
+ transitions = SceneContainerTransitions,
overlayKeys = overlayKeys,
navigationDistances = navigationDistances,
)
diff --git a/packages/Vcn/framework-b/Android.bp b/packages/Vcn/framework-b/Android.bp
index be64bb1..8b010c7 100644
--- a/packages/Vcn/framework-b/Android.bp
+++ b/packages/Vcn/framework-b/Android.bp
@@ -40,5 +40,28 @@
"android.net.vcn",
],
+ impl_library_visibility: [
+ // Using for test only
+ "//cts/tests/netlegacy22.api",
+ "//cts/tests/tests/vcn",
+ "//external/sl4a:__subpackages__",
+ "//frameworks/base/core/tests/bandwidthtests",
+ "//frameworks/base/core/tests/benchmarks",
+ "//frameworks/base/core/tests/utillib",
+ "//frameworks/base/services/tests/VpnTests",
+ "//frameworks/base/tests/vcn",
+ "//frameworks/opt/telephony/tests/telephonytests",
+ "//packages/modules/CaptivePortalLogin/tests",
+ "//packages/modules/Connectivity/staticlibs/testutils",
+ "//packages/modules/Connectivity/staticlibs/tests:__subpackages__",
+ "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
+ "//packages/modules/Connectivity/tests:__subpackages__",
+ "//packages/modules/Connectivity/thread/tests:__subpackages__",
+ "//packages/modules/IPsec/tests/iketests",
+ "//packages/modules/NetworkStack",
+ "//packages/modules/NetworkStack/tests:__subpackages__",
+ "//packages/modules/Wifi/service/tests/wifitests",
+ ],
+
// TODO: b/375213246 Expose this library to Tethering module
}
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
index ca4b5d4..6ccfaf3 100644
--- a/packages/VpnDialogs/AndroidManifest.xml
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -41,6 +41,7 @@
<activity android:name=".PlatformVpnConfirmDialog"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight"
android:noHistory="true"
+ android:enableOnBackInvokedCallback="false"
android:excludeFromRecents="true"
android:exported="true">
</activity>
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 0c2ce8d..66c8d0f 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -282,20 +282,12 @@
visibility: ["//visibility:private"],
}
-cc_library_host_shared {
- name: "libravenwood_initializer",
- defaults: ["ravenwood_jni_defaults"],
- srcs: [
- "runtime-jni/ravenwood_initializer.cpp",
- ],
-}
-
// We need this as a separate library because we need to overload the
// sysprop symbols before libbase is loaded into the process
cc_library_host_shared {
- name: "libravenwood_sysprop",
+ name: "libravenwood_initializer",
defaults: ["ravenwood_jni_defaults"],
- srcs: ["runtime-jni/ravenwood_sysprop.cpp"],
+ srcs: ["runtime-jni/ravenwood_initializer.cpp"],
}
cc_library_host_shared {
@@ -669,7 +661,6 @@
jni_libs: [
// Libraries has to be loaded in the following order
"libravenwood_initializer",
- "libravenwood_sysprop",
"libravenwood_runtime",
"libandroid_runtime",
],
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index de3c5f2..9644a52 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -63,8 +63,6 @@
* - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}.
*/
public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
- public static final String TAG = "Ravenwood";
-
/** Scope of a hook. */
public enum Scope {
Class,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 239c806..9eff20a 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -45,7 +45,7 @@
import java.util.function.Supplier;
public class RavenwoodContext extends RavenwoodBaseContext {
- private static final String TAG = "Ravenwood";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private final Object mLock = new Object();
private final String mPackageName;
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
index 77275c4..3cb2c67 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodEnablementChecker.java
@@ -27,8 +27,6 @@
* Calculates which tests need to be executed on Ravenwood.
*/
public class RavenwoodEnablementChecker {
- private static final String TAG = "RavenwoodDisablementChecker";
-
private RavenwoodEnablementChecker() {
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index 6dfcf4ce..a5d0bfd 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -40,7 +40,7 @@
* All members must be called from the runner's main thread.
*/
public final class RavenwoodRunnerState {
- private static final String TAG = "RavenwoodRunnerState";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private static final String RAVENWOOD_RULE_ERROR =
"RavenwoodRule(s) are not executed in the correct order";
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index e730a29..930914f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -58,6 +58,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import android.util.Log_ravenwood;
import android.view.DisplayAdjustments;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -95,16 +96,18 @@
* Responsible for initializing and the environment.
*/
public class RavenwoodRuntimeEnvironmentController {
- private static final String TAG = "RavenwoodRuntimeEnvironmentController";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private RavenwoodRuntimeEnvironmentController() {
}
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer";
- private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop";
private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+ private static final String ANDROID_LOG_TAGS = "ANDROID_LOG_TAGS";
+ private static final String RAVENWOOD_ANDROID_LOG_TAGS = "RAVENWOOD_" + ANDROID_LOG_TAGS;
+
/**
* When enabled, attempt to dump all thread stacks just before we hit the
* overall Tradefed timeout, to aid in debugging deadlocks.
@@ -214,42 +217,45 @@
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
- // Some process-wide initialization. (maybe redirect stdout/stderr)
- RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
+ // Some process-wide initialization:
+ // - maybe redirect stdout/stderr
+ // - override native system property functions
+ var lib = RavenwoodCommonUtils.getJniLibraryPath(LIBRAVENWOOD_INITIALIZER_NAME);
+ System.load(lib);
+ RavenwoodRuntimeNative.reloadNativeLibrary(lib);
+
+ // Redirect stdout/stdin to the Log API.
+ RuntimeInit.redirectLogStreams();
dumpCommandLineArgs();
// We haven't initialized liblog yet, so directly write to System.out here.
RavenwoodCommonUtils.log(TAG, "globalInitInner()");
- // Load libravenwood_sysprop before other libraries that may use SystemProperties.
- var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME);
- System.load(libProp);
- RavenwoodRuntimeNative.reloadNativeLibrary(libProp);
-
// Make sure libravenwood_runtime is loaded.
System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
+ Log_ravenwood.setLogLevels(getLogTags());
+ Log_ravenwood.onRavenwoodRuntimeNativeReady();
+
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
+ // Enable all log levels for native logging, until we'll have a way to change the native
+ // side log level at runtime.
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
// before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
- if (RAVENWOOD_VERBOSE_LOGGING) {
- RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging");
- try {
- Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
- } catch (ErrnoException e) {
- throw new RuntimeException(e);
- }
+ // This would also prevent libbase from crashing the process (b/381112373) because
+ // the string format it accepts is very limited.
+ try {
+ Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+ } catch (ErrnoException e) {
+ throw new RuntimeException(e);
}
// Make sure libandroid_runtime is loaded.
RavenwoodNativeLoader.loadFrameworkNativeCode();
- // Redirect stdout/stdin to liblog.
- RuntimeInit.redirectLogStreams();
-
// Touch some references early to ensure they're <clinit>'ed
Objects.requireNonNull(Build.TYPE);
Objects.requireNonNull(Build.VERSION.SDK);
@@ -332,6 +338,18 @@
initializeCompatIds();
}
+ /**
+ * Get log tags from environmental variable.
+ */
+ @Nullable
+ private static String getLogTags() {
+ var logTags = System.getenv(RAVENWOOD_ANDROID_LOG_TAGS);
+ if (logTags == null) {
+ logTags = System.getenv(ANDROID_LOG_TAGS);
+ }
+ return logTags;
+ }
+
private static void loadRavenwoodProperties() {
var props = RavenwoodSystemProperties.readProperties("ravenwood.properties");
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index c545baa..fac0791 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -34,7 +34,7 @@
* A class to manage the core default system properties of the Ravenwood environment.
*/
public class RavenwoodSystemProperties {
- private static final String TAG = "RavenwoodSystemProperties";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
/** We pull in properties from this file. */
private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
@@ -132,9 +132,10 @@
}
private static boolean isKeyReadable(String key) {
- final String root = getKeyRoot(key);
+ // All writable keys are also readable
+ if (isKeyWritable(key)) return true;
- if (root.startsWith("debug.")) return true;
+ final String root = getKeyRoot(key);
// This set is carefully curated to help identify situations where a test may
// accidentally depend on a default value of an obscure property whose owner hasn't
@@ -145,26 +146,24 @@
if (root.startsWith("soc.")) return true;
if (root.startsWith("system.")) return true;
- // For PropertyInvalidatedCache
- if (root.startsWith("cache_key.")) return true;
+ // All core values should be readable
+ if (sDefaultValues.containsKey(key)) return true;
- switch (key) {
- case "gsm.version.baseband":
- case "no.such.thing":
- case "qemu.sf.lcd_density":
- case "ro.bootloader":
- case "ro.debuggable":
- case "ro.hardware":
- case "ro.hw_timeout_multiplier":
- case "ro.odm.build.media_performance_class":
- case "ro.sf.lcd_density":
- case "ro.treble.enabled":
- case "ro.vndk.version":
- case "ro.icu.data.path":
- return true;
- }
-
- return false;
+ // Hardcoded allowlist
+ return switch (key) {
+ case "gsm.version.baseband",
+ "no.such.thing",
+ "qemu.sf.lcd_density",
+ "ro.bootloader",
+ "ro.hardware",
+ "ro.hw_timeout_multiplier",
+ "ro.odm.build.media_performance_class",
+ "ro.sf.lcd_density",
+ "ro.treble.enabled",
+ "ro.vndk.version",
+ "ro.icu.data.path" -> true;
+ default -> false;
+ };
}
private static boolean isKeyWritable(String key) {
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
index 7870585..c8b10d2 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java
@@ -41,7 +41,7 @@
* `/tmp/Ravenwood-stats_[TEST-MODULE=NAME]_latest.csv`.
*/
public class RavenwoodTestStats {
- private static final String TAG = "RavenwoodTestStats";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped";
private static RavenwoodTestStats sInstance;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
index 31a1416..f3688d6 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java
@@ -35,7 +35,7 @@
import org.junit.runners.model.TestClass;
abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable {
- private static final String TAG = "Ravenwood";
+ public static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
boolean mRealRunnerTakesRunnerBuilder = false;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index e49d3d9..d8cde0e 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -41,7 +41,7 @@
*/
@Deprecated
public final class RavenwoodRule implements TestRule {
- private static final String TAG = "RavenwoodRule";
+ private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
@@ -193,6 +193,12 @@
return IS_ON_RAVENWOOD;
}
+ private static void ensureOnRavenwood(String featureName) {
+ if (!IS_ON_RAVENWOOD) {
+ throw new RuntimeException(featureName + " is only supported on Ravenwood.");
+ }
+ }
+
/**
* @deprecated Use
* {@code androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().getContext()}
@@ -242,6 +248,40 @@
return System.currentTimeMillis();
}
+ /**
+ * Equivalent to setting the ANDROID_LOG_TAGS environmental variable.
+ *
+ * See https://developer.android.com/tools/logcat#filteringOutput for the string format.
+ *
+ * NOTE: this works only on Ravenwood.
+ */
+ public static void setAndroidLogTags(@Nullable String androidLogTags) {
+ ensureOnRavenwood("RavenwoodRule.setAndroidLogTags()");
+ try {
+ Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+ var setter = logRavenwoodClazz.getMethod("setLogLevels", String.class);
+ setter.invoke(null, androidLogTags);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Set a log level for a given tag. Pass NULL to {@code tag} to change the default level.
+ *
+ * NOTE: this works only on Ravenwood.
+ */
+ public static void setLogLevel(@Nullable String tag, int level) {
+ ensureOnRavenwood("RavenwoodRule.setLogLevel()");
+ try {
+ Class<?> logRavenwoodClazz = Class.forName("android.util.Log_ravenwood");
+ var setter = logRavenwoodClazz.getMethod("setLogLevel", String.class, int.class);
+ setter.invoke(null, tag, level);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
// Below are internal to ravenwood. Don't use them from normal tests...
public static class RavenwoodPrivate {
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index b4b75178..8d9bcd5 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -31,8 +31,6 @@
* This is only used when a real device-side test has Ravenizer enabled.
*/
public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase {
- private static final String TAG = "Ravenwood";
-
private static class NopRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
index 2a04d44..a967a3f 100644
--- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -33,7 +33,7 @@
import java.util.function.Supplier;
public class RavenwoodCommonUtils {
- private static final String TAG = "RavenwoodCommonUtils";
+ public static final String TAG = "Ravenwood";
private RavenwoodCommonUtils() {
}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java b/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
deleted file mode 100644
index c85bd23..0000000
--- a/ravenwood/runtime-helper-src/framework/android/util/Log_host.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.util;
-
-import android.util.Log.Level;
-
-import com.android.internal.os.RuntimeInit;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
-
-import java.io.PrintStream;
-
-/**
- * Ravenwood "native substitution" class for {@link android.util.Log}.
- *
- * {@link android.util.Log} already uses the actual native code and doesn't use this class.
- * In order to switch to this Java implementation, uncomment the @RavenwoodNativeSubstitutionClass
- * annotation on {@link android.util.Log}.
- */
-public class Log_host {
-
- public static boolean isLoggable(String tag, @Level int level) {
- return true;
- }
-
- public static int println_native(int bufID, int priority, String tag, String msg) {
- if (priority < Log.INFO && !RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING) {
- return msg.length(); // No verbose logging.
- }
- final String buffer;
- switch (bufID) {
- case Log.LOG_ID_MAIN: buffer = "main"; break;
- case Log.LOG_ID_RADIO: buffer = "radio"; break;
- case Log.LOG_ID_EVENTS: buffer = "event"; break;
- case Log.LOG_ID_SYSTEM: buffer = "system"; break;
- case Log.LOG_ID_CRASH: buffer = "crash"; break;
- default: buffer = "buf:" + bufID; break;
- }
-
- final String prio;
- switch (priority) {
- case Log.VERBOSE: prio = "V"; break;
- case Log.DEBUG: prio = "D"; break;
- case Log.INFO: prio = "I"; break;
- case Log.WARN: prio = "W"; break;
- case Log.ERROR: prio = "E"; break;
- case Log.ASSERT: prio = "A"; break;
- default: prio = "prio:" + priority; break;
- }
-
- for (String s : msg.split("\\n")) {
- getRealOut().println(String.format("logd: [%s] %s %s: %s", buffer, prio, tag, s));
- }
- return msg.length();
- }
-
- public static int logger_entry_max_payload_native() {
- return 4068; // [ravenwood] This is what people use in various places.
- }
-
- /**
- * Return the "real" {@code System.out} if it's been swapped by {@code RavenwoodRuleImpl}, so
- * that we don't end up in a recursive loop.
- */
- private static PrintStream getRealOut() {
- if (RuntimeInit.sOut$ravenwood != null) {
- return RuntimeInit.sOut$ravenwood;
- } else {
- return System.out;
- }
- }
-}
diff --git a/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
new file mode 100644
index 0000000..7ab9cda
--- /dev/null
+++ b/ravenwood/runtime-helper-src/framework/android/util/Log_ravenwood.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.annotation.Nullable;
+import android.util.Log.Level;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.RuntimeInit;
+import com.android.ravenwood.RavenwoodRuntimeNative;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.PrintStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Ravenwood "native substitution" class for {@link android.util.Log}.
+ *
+ * {@link android.util.Log} already uses the actual native code and doesn't use this class.
+ * In order to switch to this Java implementation, uncomment the @RavenwoodNativeSubstitutionClass
+ * annotation on {@link android.util.Log}.
+ */
+public class Log_ravenwood {
+
+ private static final SimpleDateFormat sTimestampFormat =
+ new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+
+ private static final Object sLock = new Object();
+
+ @GuardedBy("sLock")
+ private static int sDefaultLogLevel;
+
+ @GuardedBy("sLock")
+ private static final Map<String, Integer> sTagLogLevels = new HashMap<>();
+
+ /**
+ * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setAndroidLogTags(String)}
+ * via reflections.
+ */
+ public static void setLogLevels(String androidLogTags) {
+ var map = parseLogLevels(androidLogTags);
+
+ synchronized (sLock) {
+ sTagLogLevels.clear();
+ sTagLogLevels.putAll(map);
+
+ var def = map.get("*");
+ sDefaultLogLevel = def != null ? def : Log.VERBOSE;
+ }
+ }
+
+ private static Map<String, Integer> parseLogLevels(String androidLogTags) {
+ final Map<String, Integer> ret = new HashMap<>();
+
+ if (androidLogTags == null) {
+ return ret;
+ }
+
+ String[] tagPairs = androidLogTags.trim().split("\\s+");
+ for (String tagPair : tagPairs) {
+ String[] parts = tagPair.split(":");
+ if (parts.length == 2) {
+ String tag = parts[0];
+ try {
+ int priority = switch (parts[1].toUpperCase(Locale.ROOT)) {
+ case "V": yield Log.VERBOSE;
+ case "D": yield Log.DEBUG;
+ case "I": yield Log.INFO;
+ case "W": yield Log.WARN;
+ case "E": yield Log.ERROR;
+ case "F": yield Log.ERROR + 1; // Not used in the java side.
+ case "S": yield Integer.MAX_VALUE; // Silent
+ default: throw new IllegalArgumentException(
+ "Invalid priority level for tag: " + tag);
+ };
+
+ ret.put(tag, priority);
+ } catch (IllegalArgumentException e) {
+ System.err.println(e.getMessage());
+ }
+ } else {
+ System.err.println("Invalid tag format: " + tagPair);
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Used by {@link android.platform.test.ravenwood.RavenwoodRule#setLogLevel(String, int)}
+ * via reflections. Pass NULL to {@code tag} to set the default level.
+ */
+ public static void setLogLevel(@Nullable String tag, int level) {
+ synchronized (sLock) {
+ if (tag == null) {
+ sDefaultLogLevel = level;
+ } else {
+ sTagLogLevels.put(tag, level);
+ }
+ }
+ }
+
+ /**
+ * Replaces {@link Log#isLoggable}.
+ */
+ public static boolean isLoggable(String tag, @Level int priority) {
+ synchronized (sLock) {
+ var threshold = sTagLogLevels.get(tag);
+ if (threshold == null) {
+ threshold = sDefaultLogLevel;
+ }
+ return priority >= threshold;
+ }
+ }
+
+ public static int println_native(int bufID, int priority, String tag, String msg) {
+ if (!isLoggable(tag, priority)) {
+ return msg.length();
+ }
+
+ final String prio;
+ switch (priority) {
+ case Log.VERBOSE: prio = "V"; break;
+ case Log.DEBUG: prio = "D"; break;
+ case Log.INFO: prio = "I"; break;
+ case Log.WARN: prio = "W"; break;
+ case Log.ERROR: prio = "E"; break;
+ case Log.ASSERT: prio = "A"; break;
+ default: prio = "prio:" + priority; break;
+ }
+
+ String leading = sTimestampFormat.format(new Date())
+ + " %-6d %-6d %s %-8s: ".formatted(getPid(), getTid(), prio, tag);
+ var out = getRealOut();
+ for (String s : msg.split("\\n")) {
+ out.print(leading);
+ out.println(s);
+ }
+ return msg.length();
+ }
+
+ public static int logger_entry_max_payload_native() {
+ return 4068; // [ravenwood] This is what people use in various places.
+ }
+
+ /**
+ * Return the "real" {@code System.out} if it's been swapped by {@code RavenwoodRuleImpl}, so
+ * that we don't end up in a recursive loop.
+ */
+ private static PrintStream getRealOut() {
+ if (RuntimeInit.sOut$ravenwood != null) {
+ return RuntimeInit.sOut$ravenwood;
+ } else {
+ return System.out;
+ }
+ }
+
+ /**
+ * PID. We need to use a JNI method to get it, but JNI isn't initially ready.
+ * Call {@link #onRavenwoodRuntimeNativeReady} to signal when JNI is ready, at which point
+ * we set this field.
+ * (We don't want to call native methods that may not be fully initialized even with a
+ * try-catch, because partially initialized JNI methods could crash the process.)
+ */
+ private static volatile int sPid = 0;
+
+ private static ThreadLocal<Integer> sTid =
+ ThreadLocal.withInitial(RavenwoodRuntimeNative::gettid);
+
+ /**
+ * Call it when {@link RavenwoodRuntimeNative} is usable.
+ */
+ public static void onRavenwoodRuntimeNativeReady() {
+ sPid = RavenwoodRuntimeNative.getpid();
+ }
+
+ private static int getPid() {
+ return sPid;
+ }
+
+ private static int getTid() {
+ if (sPid == 0) {
+ return 0; // Native methods not ready yet.
+ }
+ return sTid.get();
+ }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index 9a78989..acbcdf1 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -62,6 +62,8 @@
removeSystemProperty(null);
}
+ public static native int getpid();
+
public static native int gettid();
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp
index 89fb7c3..dbbc345 100644
--- a/ravenwood/runtime-jni/ravenwood_initializer.cpp
+++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp
@@ -14,16 +14,174 @@
* limitations under the License.
*/
- /*
- * This file is compiled into a single SO file, which we load at the very first.
- * We can do process-wide initialization here.
- */
+/*
+ * This file is compiled into a single SO file, which we load at the very first.
+ * We can do process-wide initialization here.
+ * Please be aware that all symbols defined in this SO file will be reloaded
+ * as `RTLD_GLOBAL`, so make sure all functions are static except those we EXPLICITLY
+ * want to expose and override globally.
+ */
+#include <dlfcn.h>
#include <fcntl.h>
-#include <unistd.h>
+
+#include <set>
#include "jni_helper.h"
+// Implement a rudimentary system properties data store
+
+#define PROP_VALUE_MAX 92
+
+namespace {
+
+struct prop_info {
+ std::string key;
+ mutable std::string value;
+ mutable uint32_t serial;
+
+ prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
+};
+
+struct prop_info_cmp {
+ using is_transparent = void;
+ bool operator()(const prop_info& lhs, const prop_info& rhs) {
+ return lhs.key < rhs.key;
+ }
+ bool operator()(std::string_view lhs, const prop_info& rhs) {
+ return lhs < rhs.key;
+ }
+ bool operator()(const prop_info& lhs, std::string_view rhs) {
+ return lhs.key < rhs;
+ }
+};
+
+} // namespace
+
+static auto& g_properties_lock = *new std::mutex;
+static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
+
+static bool property_set(const char* key, const char* value) {
+ if (key == nullptr || *key == '\0') return false;
+ if (value == nullptr) value = "";
+ bool read_only = !strncmp(key, "ro.", 3);
+ if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
+
+ std::lock_guard lock(g_properties_lock);
+ auto [it, success] = g_properties.emplace(key, value);
+ if (read_only) return success;
+ if (!success) {
+ it->value = value;
+ ++it->serial;
+ }
+ return true;
+}
+
+template <typename Func>
+static void property_get(const char* key, Func callback) {
+ std::lock_guard lock(g_properties_lock);
+ auto it = g_properties.find(key);
+ if (it != g_properties.end()) {
+ callback(*it);
+ }
+}
+
+// Redefine the __system_property_XXX functions here so we can perform
+// logging and access checks for all sysprops in native code.
+
+static void check_system_property_access(const char* key, bool write);
+
+extern "C" {
+
+int __system_property_set(const char* key, const char* value) {
+ check_system_property_access(key, true);
+ return property_set(key, value) ? 0 : -1;
+}
+
+int __system_property_get(const char* key, char* value) {
+ check_system_property_access(key, false);
+ *value = '\0';
+ property_get(key, [&](const prop_info& info) {
+ snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
+ });
+ return strlen(value);
+}
+
+const prop_info* __system_property_find(const char* key) {
+ check_system_property_access(key, false);
+ const prop_info* pi = nullptr;
+ property_get(key, [&](const prop_info& info) { pi = &info; });
+ return pi;
+}
+
+void __system_property_read_callback(const prop_info* pi,
+ void (*callback)(void*, const char*, const char*, uint32_t),
+ void* cookie) {
+ std::lock_guard lock(g_properties_lock);
+ callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
+}
+
+} // extern "C"
+
+// ---- JNI ----
+
+static JavaVM* gVM = nullptr;
+static jclass gRunnerState = nullptr;
+static jmethodID gCheckSystemPropertyAccess;
+
+static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
+ ScopedUtfChars path(env, javaPath);
+ // Force reload ourselves as global
+ dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
+}
+
+// Call back into Java code to check property access
+static void check_system_property_access(const char* key, bool write) {
+ if (gVM != nullptr && gRunnerState != nullptr) {
+ JNIEnv* env;
+ if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
+ ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
+ env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
+ env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
+ return;
+ }
+ }
+ // Not on JVM thread, abort
+ LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
+}
+
+static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
+ ScopedUtfChars key(env, javaKey);
+ jstring value = nullptr;
+ property_get(key.c_str(),
+ [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
+ return value;
+}
+
+static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
+ ScopedUtfChars key(env, javaKey);
+ ScopedUtfChars value(env, javaValue);
+ return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
+ std::lock_guard lock(g_properties_lock);
+
+ if (javaKey == nullptr) {
+ g_properties.clear();
+ return JNI_TRUE;
+ } else {
+ ScopedUtfChars key(env, javaKey);
+ auto it = g_properties.find(key);
+ if (it != g_properties.end()) {
+ g_properties.erase(it);
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+ }
+}
+
static void maybeRedirectLog() {
auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT");
if (ravenwoodLogOut == NULL) {
@@ -42,9 +200,30 @@
dup2(ttyFd, 2);
}
+static const JNINativeMethod sMethods[] = {
+ {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
+ {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
+ {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
+ {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
+};
+
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
ALOGI("%s: JNI_OnLoad", __FILE__);
maybeRedirectLog();
+
+ JNIEnv* env = GetJNIEnvOrDie(vm);
+ gVM = vm;
+
+ // Fetch several references for future use
+ gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
+ gCheckSystemPropertyAccess =
+ GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
+ "(Ljava/lang/String;Z)V");
+
+ // Expose raw property methods as JNI methods
+ jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
+ if (res < 0) return -1;
+
return JNI_VERSION_1_4;
}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index c1993f6..bab4c7e 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -174,6 +174,9 @@
throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
}
+static jint Linux_getpid(JNIEnv* env, jobject) {
+ return getpid();
+}
static jint Linux_gettid(JNIEnv* env, jobject) {
// gettid(2() was added in glibc 2.30 but Android uses an older version in prebuilt.
@@ -196,6 +199,7 @@
{ "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat },
{ "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open },
{ "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
+ { "getpid", "()I", (void*)Linux_getpid },
{ "gettid", "()I", (void*)Linux_gettid },
};
diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
deleted file mode 100644
index a78aa8d..0000000
--- a/ravenwood/runtime-jni/ravenwood_sysprop.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dlfcn.h>
-
-#include <set>
-
-#include "jni_helper.h"
-
-// Implement a rudimentary system properties data store
-
-#define PROP_VALUE_MAX 92
-
-namespace {
-
-struct prop_info {
- std::string key;
- mutable std::string value;
- mutable uint32_t serial;
-
- prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {}
-};
-
-struct prop_info_cmp {
- using is_transparent = void;
- bool operator()(const prop_info& lhs, const prop_info& rhs) {
- return lhs.key < rhs.key;
- }
- bool operator()(std::string_view lhs, const prop_info& rhs) {
- return lhs < rhs.key;
- }
- bool operator()(const prop_info& lhs, std::string_view rhs) {
- return lhs.key < rhs;
- }
-};
-
-} // namespace
-
-static auto& g_properties_lock = *new std::mutex;
-static auto& g_properties = *new std::set<prop_info, prop_info_cmp>;
-
-static bool property_set(const char* key, const char* value) {
- if (key == nullptr || *key == '\0') return false;
- if (value == nullptr) value = "";
- bool read_only = !strncmp(key, "ro.", 3);
- if (!read_only && strlen(value) >= PROP_VALUE_MAX) return false;
-
- std::lock_guard lock(g_properties_lock);
- auto [it, success] = g_properties.emplace(key, value);
- if (read_only) return success;
- if (!success) {
- it->value = value;
- ++it->serial;
- }
- return true;
-}
-
-template <typename Func>
-static void property_get(const char* key, Func callback) {
- std::lock_guard lock(g_properties_lock);
- auto it = g_properties.find(key);
- if (it != g_properties.end()) {
- callback(*it);
- }
-}
-
-// Redefine the __system_property_XXX functions here so we can perform
-// logging and access checks for all sysprops in native code.
-
-static void check_system_property_access(const char* key, bool write);
-
-extern "C" {
-
-int __system_property_set(const char* key, const char* value) {
- check_system_property_access(key, true);
- return property_set(key, value) ? 0 : -1;
-}
-
-int __system_property_get(const char* key, char* value) {
- check_system_property_access(key, false);
- *value = '\0';
- property_get(key, [&](const prop_info& info) {
- snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str());
- });
- return strlen(value);
-}
-
-const prop_info* __system_property_find(const char* key) {
- check_system_property_access(key, false);
- const prop_info* pi = nullptr;
- property_get(key, [&](const prop_info& info) { pi = &info; });
- return pi;
-}
-
-void __system_property_read_callback(const prop_info* pi,
- void (*callback)(void*, const char*, const char*, uint32_t),
- void* cookie) {
- std::lock_guard lock(g_properties_lock);
- callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial);
-}
-
-} // extern "C"
-
-// ---- JNI ----
-
-static JavaVM* gVM = nullptr;
-static jclass gRunnerState = nullptr;
-static jmethodID gCheckSystemPropertyAccess;
-
-static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
- ScopedUtfChars path(env, javaPath);
- // Force reload ourselves as global
- dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD);
-}
-
-// Call back into Java code to check property access
-static void check_system_property_access(const char* key, bool write) {
- if (gVM != nullptr && gRunnerState != nullptr) {
- JNIEnv* env;
- if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
- ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
- env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
- env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
- return;
- }
- }
- // Not on JVM thread, abort
- LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key);
-}
-
-static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
- ScopedUtfChars key(env, javaKey);
- jstring value = nullptr;
- property_get(key.c_str(),
- [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); });
- return value;
-}
-
-static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) {
- ScopedUtfChars key(env, javaKey);
- ScopedUtfChars value(env, javaValue);
- return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
-}
-
-static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
- std::lock_guard lock(g_properties_lock);
-
- if (javaKey == nullptr) {
- g_properties.clear();
- return JNI_TRUE;
- } else {
- ScopedUtfChars key(env, javaKey);
- auto it = g_properties.find(key);
- if (it != g_properties.end()) {
- g_properties.erase(it);
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
- }
-}
-
-static const JNINativeMethod sMethods[] = {
- {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
- {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
- {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
- {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
-};
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- ALOGI("%s: JNI_OnLoad", __FILE__);
-
- JNIEnv* env = GetJNIEnvOrDie(vm);
- gVM = vm;
-
- // Fetch several references for future use
- gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
- gCheckSystemPropertyAccess =
- GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
- "(Ljava/lang/String;Z)V");
-
- // Expose raw property methods as JNI methods
- jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods));
- if (res < 0) return -1;
-
- return JNI_VERSION_1_4;
-}
diff --git a/ravenwood/scripts/extract-last-soong-commands.py b/ravenwood/scripts/extract-last-soong-commands.py
new file mode 100755
index 0000000..bdc1de0
--- /dev/null
+++ b/ravenwood/scripts/extract-last-soong-commands.py
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+# 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.
+
+
+# This script extracts all the commands executed in the last soong run,
+# and write them into a script file, and print the filename.
+#
+# All the commands are commented out. Uncomment what you want to execute as
+# needed before running it.
+
+import datetime
+import gzip
+import os
+import re
+import shlex
+import sys
+
+re_command = re.compile(r''' ^\[.*?\] \s* (.*) ''', re.X)
+
+HEADER = r'''#!/bin/bash
+
+set -e # Stop on a failed command
+
+cd "${ANDROID_BUILD_TOP:?}"
+
+'''
+
+OUT_SCRIPT_DIR = "/tmp/"
+OUT_SCRIPT_FORMAT = "soong-rerun-%Y-%m-%d_%H-%M-%S.sh"
+
+def main(args):
+ log = os.environ["ANDROID_BUILD_TOP"] + "/out/verbose.log.gz"
+ outdir = "/tmp/"
+ outfile = outdir + datetime.datetime.now().strftime(OUT_SCRIPT_FORMAT)
+
+ with open(outfile, "w") as out:
+ out.write(HEADER)
+
+ with gzip.open(log) as f:
+ for line in f:
+ s = line.decode("utf-8")
+
+ if s.startswith("verbose"):
+ continue
+ if re.match('^\[.*bootstrap blueprint', s):
+ continue
+
+ s = s.rstrip()
+
+ m = re_command.search(s)
+ if m:
+ command = m.groups()[0]
+
+ out.write('#========\n')
+
+ # Show the full command line before executing it.
+ out.write('#echo ' + shlex.quote(command) + '\n')
+ out.write('\n')
+
+ # Execute the command.
+ out.write('#' + command + '\n')
+
+ out.write('\n')
+
+ continue
+
+ if s.startswith("FAILED:"):
+ break
+
+ os.chmod(outfile, 0o755)
+ print(outfile)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/ravenwood/test-authors.md b/ravenwood/test-authors.md
index c29fb7f..6d82a74 100644
--- a/ravenwood/test-authors.md
+++ b/ravenwood/test-authors.md
@@ -106,45 +106,6 @@
> **Note:** There's a known bug #308854804 where `TEST_MAPPING` is not being applied, so we're currently planning to run all Ravenwood tests unconditionally in presubmit for changes to `frameworks/base/` and `cts/` until there is a better path forward.
-## Strategies for feature flags
-
-Ravenwood supports writing tests against logic that uses feature flags through the existing `SetFlagsRule` infrastructure maintained by the feature flagging team:
-
-```
-import android.platform.test.flag.junit.SetFlagsRule;
-
-@RunWith(AndroidJUnit4.class)
-public class MyCodeTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(SetFlagsRule.DefaultInitValueType.NULL_DEFAULT);
-
- @Test
- public void testEnabled() {
- mSetFlagsRule.enableFlags(Flags.FLAG_MY_FLAG);
- // verify test logic that depends on flag being enabled
- }
-```
-
-This naturally composes together well with any `RavenwoodRule` that your test may need.
-
-While `SetFlagsRule` is generally a best-practice (as it can explicitly confirm behaviors for both "on" and "off" states), you may need to write tests that use `CheckFlagsRule` (such as when writing CTS). Ravenwood currently supports `CheckFlagsRule` by offering "all-on" and "all-off" behaviors:
-
-```
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-@RunWith(AndroidJUnit4.class)
-public class MyCodeTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isUnderRavenwood()
- ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
- : DeviceFlagsValueProvider.createCheckFlagsRule();
-```
-
-Ravenwood currently doesn't have knowledge of the "default" value of any flags, so using `createAllOnCheckFlagsRule()` is recommended to verify the widest possible set of behaviors. The example code above falls back to using default values from `DeviceFlagsValueProvider` when not running on Ravenwood.
-
## Strategies for migration/bivalent tests
Ravenwood aims to support tests that are written in a “bivalent” way, where the same test code can be dual-compiled to run on both a real Android device and under a Ravenwood environment.
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
new file mode 100644
index 0000000..74c1f3f
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/coretest/RavenwoodLogLevelTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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.ravenwoodtest.coretest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.Log;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import org.junit.Test;
+
+public class RavenwoodLogLevelTest {
+ /**
+ * Assert that the `priority` is loggable, but one level below is not.
+ */
+ private void assertBarelyLoggable(String tag, int priority) {
+ assertEquals(true, Log.isLoggable(tag, priority));
+ assertEquals(false, Log.isLoggable(tag, priority - 1));
+ }
+
+ @Test
+ public void testDefaultLogTags() {
+ RavenwoodRule.setAndroidLogTags(null);
+
+ // Info should always be loggable.
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllVerbose() {
+ RavenwoodRule.setAndroidLogTags("*:V");
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllSilent() {
+ RavenwoodRule.setAndroidLogTags("*:S");
+
+ assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+ assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+ }
+
+ @Test
+ public void testComplex() {
+ RavenwoodRule.setAndroidLogTags("TAG1:W TAG2:D *:I");
+
+ assertBarelyLoggable("TAG1", Log.WARN);
+ assertBarelyLoggable("TAG2", Log.DEBUG);
+ assertBarelyLoggable("TAG3", Log.INFO);
+ }
+
+ @Test
+ public void testAllVerbose_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.VERBOSE);
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.INFO));
+ assertEquals(true, Log.isLoggable("TAG2", Log.INFO));
+
+ assertEquals(true, Log.isLoggable("TAG1", Log.DEBUG));
+ assertEquals(true, Log.isLoggable("TAG2", Log.VERBOSE));
+ }
+
+ @Test
+ public void testAllSilent_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.ASSERT + 1);
+
+ assertEquals(false, Log.isLoggable("TAG1", Log.ASSERT));
+ assertEquals(false, Log.isLoggable("TAG2", Log.ASSERT));
+ }
+
+ @Test
+ public void testComplex_setLogLevel() {
+ RavenwoodRule.setAndroidLogTags(null);
+ RavenwoodRule.setLogLevel(null, Log.INFO);
+ RavenwoodRule.setLogLevel("TAG1", Log.WARN);
+ RavenwoodRule.setLogLevel("TAG2", Log.DEBUG);
+
+ assertBarelyLoggable("TAG1", Log.WARN);
+ assertBarelyLoggable("TAG2", Log.DEBUG);
+ assertBarelyLoggable("TAG3", Log.INFO);
+ }
+}
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index 08f53977..83c3151 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -14,7 +14,7 @@
# Support APIs not available in standard JRE
class java.io.FileDescriptor keep
- method getInt$ ()I @com.android.ravenwood.RavenwoodJdkPatch.getInt$
- method setInt$ (I)V @com.android.ravenwood.RavenwoodJdkPatch.setInt$
+ method getInt$ @com.android.ravenwood.RavenwoodJdkPatch.getInt$
+ method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
class java.util.LinkedHashMap keep
- method eldest ()Ljava/util/Map$Entry; @com.android.ravenwood.RavenwoodJdkPatch.eldest
+ method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
index 59fa464..fc885d6 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/InMemoryOutputFilter.kt
@@ -66,6 +66,9 @@
methodName: String,
descriptor: String
) {
+ if (descriptor == "*") {
+ return
+ }
if (classes.findMethod(className, methodName, descriptor) == null) {
log.w("Unknown method $className.$methodName$descriptor")
}
@@ -92,7 +95,8 @@
descriptor: String,
): FilterPolicyWithReason {
return mPolicies[getMethodKey(className, methodName, descriptor)]
- ?: super.getPolicyForMethod(className, methodName, descriptor)
+ ?: mPolicies[getMethodKey(className, methodName, "*")]
+ ?: super.getPolicyForMethod(className, methodName, descriptor)
}
fun setPolicyForMethod(
@@ -107,7 +111,8 @@
override fun getRenameTo(className: String, methodName: String, descriptor: String): String? {
return mRenames[getMethodKey(className, methodName, descriptor)]
- ?: super.getRenameTo(className, methodName, descriptor)
+ ?: mRenames[getMethodKey(className, methodName, "*")]
+ ?: super.getRenameTo(className, methodName, descriptor)
}
fun setRenameTo(className: String, methodName: String, descriptor: String, toName: String) {
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index caf80eb..7462a8c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -303,12 +303,21 @@
}
private fun parseMethod(fields: Array<String>) {
- if (fields.size < 4) {
- throw ParseException("Method ('m') expects 3 fields.")
+ if (fields.size < 3 || fields.size > 4) {
+ throw ParseException("Method ('m') expects 3 or 4 fields.")
}
val name = fields[1]
- val signature = fields[2]
- val policy = parsePolicy(fields[3])
+ val signature: String
+ val policyStr: String
+ if (fields.size <= 3) {
+ signature = "*"
+ policyStr = fields[2]
+ } else {
+ signature = fields[2]
+ policyStr = fields[3]
+ }
+
+ val policy = parsePolicy(policyStr)
if (!policy.isUsableWithMethods) {
throw ParseException("Method can't have policy '$policy'")
@@ -321,7 +330,7 @@
policy.withReason(FILTER_REASON)
)
if (policy == FilterPolicy.Substitute) {
- val fromName = fields[3].substring(1)
+ val fromName = policyStr.substring(1)
if (fromName == name) {
throw ParseException(
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
index d45f414..a3f934c 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFilePolicyMethodReplaceFilter.kt
@@ -48,10 +48,11 @@
// Maybe use 'Tri' if we end up having too many replacements.
spec.forEach {
if (className == it.fromClass &&
- methodName == it.fromMethod &&
- descriptor == it.fromDescriptor
+ methodName == it.fromMethod
) {
- return MethodReplaceTarget(it.toClass, it.toMethod)
+ if (it.fromDescriptor == "*" || descriptor == it.fromDescriptor) {
+ return MethodReplaceTarget(it.toClass, it.toMethod)
+ }
}
}
return null
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 3c138d2..2f35d35 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -3,11 +3,11 @@
# field remove remove # Implicitly remove
method <init> ()V keep
method addOne (I)I keep
- method addOneInner (I)I keep
+ method addOneInner keep
method toBeRemoved (Ljava/lang/String;)V remove
method addTwo (I)I @addTwo_host
# method addTwo_host (I)I # used as a substitute
- method nativeAddThree (I)I @addThree_host
+ method nativeAddThree @addThree_host
# method addThree_host (I)I # used as a substitute
method unsupportedMethod ()Ljava/lang/String; throw
method visibleButUsesUnsupportedMethod ()Ljava/lang/String; keep
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 71a0fc4..e50535f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1028,8 +1028,7 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);
- Handler receiverHandler =
- Flags.managerAvoidReceiverTimeout() ? BackgroundThread.getHandler() : null;
+ Handler receiverHandler = BackgroundThread.getHandler();
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2269,8 +2268,7 @@
mContext, shortcutType, userState.mUserId))
: userState.getShortcutTargetsLocked(shortcutType);
- if (Flags.clearDefaultFromA11yShortcutTargetServiceRestore()
- && shortcutType == HARDWARE) {
+ if (shortcutType == HARDWARE) {
final String defaultService =
mContext.getString(R.string.config_defaultAccessibilityService);
final ComponentName defaultServiceComponent = TextUtils.isEmpty(defaultService)
@@ -6649,28 +6647,17 @@
}
final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId);
- if (Flags.managerPackageMonitorLogicFix()) {
- if (!doit) {
- // if we're not handling the stop here, then we only need to know
- // if any of the force-stopped packages are currently enabled.
- return userState.mEnabledServices.stream().anyMatch(
- (comp) -> Arrays.stream(packages).anyMatch(
- (pkg) -> pkg.equals(comp.getPackageName()))
- );
- } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
- mManagerService.onUserStateChangedLocked(userState);
- }
- return false;
- } else {
- // this old logic did not properly indicate when base packageMonitor's routine
- // should handle stopping the package.
- if (doit && mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
- mManagerService.onUserStateChangedLocked(userState);
- return false;
- } else {
- return true;
- }
+ if (!doit) {
+ // if we're not handling the stop here, then we only need to know
+ // if any of the force-stopped packages are currently enabled.
+ return userState.mEnabledServices.stream().anyMatch(
+ (comp) -> Arrays.stream(packages).anyMatch(
+ (pkg) -> pkg.equals(comp.getPackageName()))
+ );
+ } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
+ mManagerService.onUserStateChangedLocked(userState);
}
+ return false;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index f45fa92..5ae0773 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -405,10 +405,9 @@
* @throws SecurityException if the input method is not in the same package as the service.
*/
@AccessibilityService.SoftKeyboardController.EnableImeResult
- int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service)
- throws SecurityException {
+ int canEnableDisableInputMethod(String imeId, AbstractAccessibilityServiceConnection service,
+ int callingUserId) throws SecurityException {
final String servicePackageName = service.getComponentName().getPackageName();
- final int callingUserId = UserHandle.getCallingUserId();
InputMethodInfo inputMethodInfo = null;
List<InputMethodInfo> inputMethodInfoList =
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 15999d1..a3fe9ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -410,9 +410,7 @@
final @AccessibilityService.SoftKeyboardController.EnableImeResult int checkResult;
final long identity = Binder.clearCallingIdentity();
try {
- synchronized (mLock) {
- checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this);
- }
+ checkResult = mSecurityPolicy.canEnableDisableInputMethod(imeId, this, callingUserId);
if (checkResult != ENABLE_IME_SUCCESS) {
return checkResult;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index da11a76..f855145 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -183,14 +183,12 @@
synchronized (mLock) {
mProxyA11yServiceConnections.put(displayId, connection);
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (mAppsOnVirtualDeviceListener == null) {
- mAppsOnVirtualDeviceListener = allRunningUids ->
- notifyProxyOfRunningAppsChange(allRunningUids);
- final VirtualDeviceManagerInternal localVdm = getLocalVdm();
- if (localVdm != null) {
- localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
- }
+ if (mAppsOnVirtualDeviceListener == null) {
+ mAppsOnVirtualDeviceListener = allRunningUids ->
+ notifyProxyOfRunningAppsChange(allRunningUids);
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm != null) {
+ localVdm.registerAppsOnVirtualDeviceListener(mAppsOnVirtualDeviceListener);
}
}
if (mProxyA11yServiceConnections.size() == 1) {
@@ -331,14 +329,12 @@
// device.
if (!isProxyedDeviceId(deviceId)) {
synchronized (mLock) {
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (mProxyA11yServiceConnections.size() == 0) {
- final VirtualDeviceManagerInternal localVdm = getLocalVdm();
- if (localVdm != null && mAppsOnVirtualDeviceListener != null) {
- localVdm.unregisterAppsOnVirtualDeviceListener(
- mAppsOnVirtualDeviceListener);
- mAppsOnVirtualDeviceListener = null;
- }
+ if (mProxyA11yServiceConnections.size() == 0) {
+ final VirtualDeviceManagerInternal localVdm = getLocalVdm();
+ if (localVdm != null && mAppsOnVirtualDeviceListener != null) {
+ localVdm.unregisterAppsOnVirtualDeviceListener(
+ mAppsOnVirtualDeviceListener);
+ mAppsOnVirtualDeviceListener = null;
}
}
mSystemSupport.removeDeviceIdLocked(deviceId);
@@ -671,8 +667,7 @@
+ getLastSentStateLocked(deviceId));
Slog.v(LOG_TAG, "force update: " + forceUpdate);
}
- if ((getLastSentStateLocked(deviceId)) != proxyState
- || (Flags.proxyUseAppsOnVirtualDeviceListener() && forceUpdate)) {
+ if ((getLastSentStateLocked(deviceId)) != proxyState || forceUpdate) {
setLastStateLocked(deviceId, proxyState);
mMainHandler.post(() -> {
synchronized (mLock) {
@@ -873,33 +868,22 @@
for (int i = 0; i < clients.getRegisteredCallbackCount(); i++) {
final AccessibilityManagerService.Client client =
((AccessibilityManagerService.Client) clients.getRegisteredCallbackCookie(i));
- if (Flags.proxyUseAppsOnVirtualDeviceListener()) {
- if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) {
- continue;
+ if (deviceId == DEVICE_ID_DEFAULT || deviceId == DEVICE_ID_INVALID) {
+ continue;
+ }
+ boolean uidBelongsToDevice =
+ localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId);
+ if (client.mDeviceId != deviceId && uidBelongsToDevice) {
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
+ + Arrays.toString(client.mPackageNames));
}
- boolean uidBelongsToDevice =
- localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId);
- if (client.mDeviceId != deviceId && uidBelongsToDevice) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
- + Arrays.toString(client.mPackageNames));
- }
- client.mDeviceId = deviceId;
- } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) {
- client.mDeviceId = DEVICE_ID_DEFAULT;
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to the default device from device id "
- + deviceId + " are " + Arrays.toString(client.mPackageNames));
- }
- }
- } else {
- if (deviceId != DEVICE_ID_DEFAULT && deviceId != DEVICE_ID_INVALID
- && localVdm.getDeviceIdsForUid(client.mUid).contains(deviceId)) {
- if (DEBUG) {
- Slog.v(LOG_TAG, "Packages moved to device id " + deviceId + " are "
- + Arrays.toString(client.mPackageNames));
- }
- client.mDeviceId = deviceId;
+ client.mDeviceId = deviceId;
+ } else if (client.mDeviceId == deviceId && !uidBelongsToDevice) {
+ client.mDeviceId = DEVICE_ID_DEFAULT;
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "Packages moved to the default device from device id "
+ + deviceId + " are " + Arrays.toString(client.mPackageNames));
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
index 6b48d2b..a4568aa 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/PanningScalingHandler.java
@@ -31,7 +31,6 @@
import android.view.ViewConfiguration;
import com.android.internal.R;
-import com.android.server.accessibility.Flags;
/**
* Handles the behavior while receiving scaling and panning gestures if it's enabled.
@@ -73,13 +72,9 @@
mMaxScale = maxScale;
mMinScale = minScale;
mBlockScroll = blockScroll;
- if (Flags.pinchZoomZeroMinSpan()) {
- mScaleGestureDetector = new ScaleGestureDetector(context,
- ViewConfiguration.get(context).getScaledTouchSlop() * 2,
- /* minSpan= */ 0, Handler.getMain(), this);
- } else {
- mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
- }
+ mScaleGestureDetector = new ScaleGestureDetector(context,
+ ViewConfiguration.get(context).getScaledTouchSlop() * 2,
+ /* minSpan= */ 0, Handler.getMain(), this);
mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
mScaleGestureDetector.setQuickScaleEnabled(false);
mMagnificationDelegate = magnificationDelegate;
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5d2ef77..5e1b147 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -23,6 +23,16 @@
}
flag {
+ name: "relayout_fix"
+ namespace: "autofill"
+ description: "Fixing relayout issue. Guarding enabling device config flags"
+ bug: "381226145"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "include_invisible_view_group_in_assist_structure"
namespace: "autofill"
description: "Mitigation for autofill providers miscalculating view visibility"
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index 444844121..5703633 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -31,6 +31,14 @@
}
flag {
+ name: "fill_dialog_improvements_impl"
+ is_exported: true
+ namespace: "autofill"
+ description: "Improvements for Fill Dialog for non-api changes"
+ bug: "336223371"
+}
+
+flag {
name: "fill_dialog_improvements"
is_exported: true
namespace: "autofill"
diff --git a/services/backup/flags.aconfig b/services/backup/flags.aconfig
index fcb7934..9326ea8 100644
--- a/services/backup/flags.aconfig
+++ b/services/backup/flags.aconfig
@@ -69,3 +69,18 @@
bug: "376661510"
is_fixed_read_only: true
}
+
+flag {
+ name: "enable_read_all_external_storage_files"
+ is_exported: true
+ namespace: "onboarding"
+ description: "Enables read all external storage files"
+ bug: "376598575"
+}
+flag {
+ name: "enable_metrics_settings_backup_agents"
+ namespace: "onboarding"
+ description: "Enable SettingsBackupAgent to collect B&R agent metrics."
+ bug: "379861078"
+ is_fixed_read_only: true
+}
diff --git a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
index 02f1865..6ced096 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentConnectionManager.java
@@ -101,11 +101,16 @@
private static final class BackupAgentConnection {
public final ApplicationInfo appInfo;
+ public final int backupMode;
+ public final boolean inRestrictedMode;
public IBackupAgent backupAgent;
public boolean connecting = true; // Assume we are trying to connect on creation.
- private BackupAgentConnection(ApplicationInfo appInfo) {
+ private BackupAgentConnection(ApplicationInfo appInfo, int backupMode,
+ boolean inRestrictedMode) {
this.appInfo = appInfo;
+ this.backupMode = backupMode;
+ this.inRestrictedMode = inRestrictedMode;
}
}
@@ -113,26 +118,31 @@
* Fires off a backup agent, blocking until it attaches (i.e. ActivityManager calls
* {@link #agentConnected(String, IBinder)}) or until this operation times out.
*
- * @param mode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}.
+ * @param backupMode a {@code BACKUP_MODE} from {@link android.app.ApplicationThreadConstants}.
*/
@Nullable
- public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode,
+ public IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int backupMode,
@BackupAnnotations.BackupDestination int backupDestination) {
+ if (app == null) {
+ Slog.w(TAG, mUserIdMsg + "bindToAgentSynchronous for null app");
+ return null;
+ }
+
synchronized (mAgentConnectLock) {
- boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(mode,
+ boolean useRestrictedMode = shouldUseRestrictedBackupModeForPackage(backupMode,
app.packageName);
if (mCurrentConnection != null) {
Slog.e(TAG, mUserIdMsg + "binding to new agent before unbinding from old one: "
+ mCurrentConnection.appInfo.packageName);
}
- mCurrentConnection = new BackupAgentConnection(app);
+ mCurrentConnection = new BackupAgentConnection(app, backupMode, useRestrictedMode);
// bindBackupAgent() is an async API. It will kick off the app's process and call
// agentConnected() when it receives the agent from the app.
boolean startedBindSuccessfully = false;
try {
- startedBindSuccessfully = mActivityManager.bindBackupAgent(app.packageName, mode,
- mUserId, backupDestination, useRestrictedMode);
+ startedBindSuccessfully = mActivityManager.bindBackupAgent(app.packageName,
+ backupMode, mUserId, backupDestination, useRestrictedMode);
} catch (RemoteException e) {
// can't happen - ActivityManager is local
}
@@ -173,28 +183,66 @@
/**
* Tell the ActivityManager that we are done with the {@link IBackupAgent} of this {@code app}.
* It will tell the app to destroy the agent.
+ *
+ * <p>If {@code allowKill} is set, this will kill the app's process if the app is in restricted
+ * mode or if it was started for restore and specified {@code android:killAfterRestore} in its
+ * manifest.
+ *
+ * @see #shouldUseRestrictedBackupModeForPackage(int, String)
*/
- public void unbindAgent(ApplicationInfo app) {
- synchronized (mAgentConnectLock) {
- if (mCurrentConnection == null) {
- Slog.w(TAG, mUserIdMsg + "unbindAgent but no current connection");
- } else if (!mCurrentConnection.appInfo.packageName.equals(app.packageName)) {
- Slog.w(TAG, mUserIdMsg + "unbindAgent for unexpected package: " + app.packageName
- + " expected: " + mCurrentConnection.appInfo.packageName);
- } else {
- mCurrentConnection = null;
- }
+ public void unbindAgent(ApplicationInfo app, boolean allowKill) {
+ if (app == null) {
+ Slog.w(TAG, mUserIdMsg + "unbindAgent for null app");
+ return;
+ }
+ synchronized (mAgentConnectLock) {
// Even if we weren't expecting to be bound to this agent, we should still call
// ActivityManager just in case. It will ignore the call if it also wasn't expecting it.
try {
mActivityManager.unbindBackupAgent(app);
+
+ // Evaluate this before potentially setting mCurrentConnection = null.
+ boolean willKill = allowKill && shouldKillAppOnUnbind(app);
+
+ if (mCurrentConnection == null) {
+ Slog.w(TAG, mUserIdMsg + "unbindAgent but no current connection");
+ } else if (!mCurrentConnection.appInfo.packageName.equals(app.packageName)) {
+ Slog.w(TAG,
+ mUserIdMsg + "unbindAgent for unexpected package: " + app.packageName
+ + " expected: " + mCurrentConnection.appInfo.packageName);
+ } else {
+ mCurrentConnection = null;
+ }
+
+ if (willKill) {
+ Slog.i(TAG, mUserIdMsg + "Killing agent host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ }
} catch (RemoteException e) {
// Can't happen - activity manager is local
}
}
}
+ @GuardedBy("mAgentConnectLock")
+ private boolean shouldKillAppOnUnbind(ApplicationInfo app) {
+ // We don't ask system UID processes to be killed.
+ if (UserHandle.isCore(app.uid)) {
+ return false;
+ }
+
+ // If the app is in restricted mode or if we're not sure if it is because our internal
+ // state is messed up, we need to avoid it being stuck in it.
+ if (mCurrentConnection == null || mCurrentConnection.inRestrictedMode) {
+ return true;
+ }
+
+ // App was doing restore and asked to be killed afterwards.
+ return isBackupModeRestore(mCurrentConnection.backupMode)
+ && (app.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0;
+ }
+
/**
* Callback: a requested backup agent has been instantiated. This should only be called from
* the {@link ActivityManager} when it's telling us that an agent is ready after a call to
@@ -307,16 +355,16 @@
*/
private boolean shouldUseRestrictedBackupModeForPackage(
@BackupAnnotations.OperationType int mode, String packageName) {
- if (!Flags.enableRestrictedModeChanges()) {
- return true;
- }
-
// Key/Value apps are never put in restricted mode.
if (mode == ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL
|| mode == ApplicationThreadConstants.BACKUP_MODE_RESTORE) {
return false;
}
+ if (!Flags.enableRestrictedModeChanges()) {
+ return true;
+ }
+
try {
PackageManager.Property property = mPackageManager.getPropertyAsUser(
PackageManager.PROPERTY_USE_RESTRICTED_BACKUP_MODE,
@@ -352,6 +400,11 @@
return true;
}
+ private static boolean isBackupModeRestore(int backupMode) {
+ return backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE
+ || backupMode == ApplicationThreadConstants.BACKUP_MODE_RESTORE_FULL;
+ }
+
@VisibleForTesting
Thread getThreadForCancellation(Runnable operation) {
return new Thread(operation, /* operationName */ "agent-disconnected");
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 136bacd..b343ec8 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -300,7 +300,8 @@
}
private void cleanup() {
- mBackupManagerService.tearDownAgentAndKill(mCurrentPackage.applicationInfo);
+ mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mCurrentPackage.applicationInfo, /* allowKill= */ true);
mBlankStateName.delete();
mNewStateName.delete();
mBackupDataName.delete();
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e085f6e..a90b693 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1998,39 +1998,6 @@
return mOperationStorage.isBackupOperationInProgress();
}
- /** Unbind the backup agent and kill the app if it's a non-system app. */
- public void tearDownAgentAndKill(ApplicationInfo app) {
- if (app == null) {
- // Null means the system package, so just quietly move on. :)
- return;
- }
-
- try {
- // unbind and tidy up even on timeout or failure, just in case
- mActivityManager.unbindBackupAgent(app);
-
- // The agent was running with a stub Application object, so shut it down.
- // !!! We hardcode the confirmation UI's package name here rather than use a
- // manifest flag! TODO something less direct.
- if (!UserHandle.isCore(app.uid)
- && !app.packageName.equals("com.android.backupconfirm")) {
- if (MORE_DEBUG) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Killing agent host process"));
- }
- mActivityManager.killApplicationProcess(app.processName, app.uid);
- } else {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Not killing after operation: " + app.processName));
- }
- }
- } catch (RemoteException e) {
- Slog.d(TAG, addUserIdToLogMessage(mUserId, "Lost app trying to shut down"));
- }
- }
-
// ----- Full-data backup scheduling -----
/**
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index b98cb10..cf617a5 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -323,7 +323,8 @@
private void tearDown() {
if (mPkg != null) {
- backupManagerService.tearDownAgentAndKill(mPkg.applicationInfo);
+ backupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mPkg.applicationInfo, /* allowKill= */ true);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index dc67091..0ba0d71 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -503,7 +503,8 @@
Slog.w(TAG, "adb backup cancel of " + target);
}
if (target != null) {
- mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ target.applicationInfo, /* allowKill= */ true);
}
mOperationStorage.removeOperation(mCurrentOpToken);
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 65730c9..7994948 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -596,8 +596,8 @@
// from the preflight pass. If we got as far as preflight, we now need
// to tear down the target process.
if (mBackupRunner != null) {
- mUserBackupManagerService.tearDownAgentAndKill(
- currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
}
// ... and continue looping.
} else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
@@ -609,7 +609,8 @@
EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
packageName);
}
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
BackupObserverUtils
@@ -617,7 +618,8 @@
BackupManager.ERROR_AGENT_FAILURE);
Slog.w(TAG, "Application failure for package: " + packageName);
EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName);
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) {
BackupObserverUtils
@@ -626,7 +628,8 @@
Slog.w(TAG, "Backup cancelled. package=" + packageName +
", cancelAll=" + mCancelAll);
EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName);
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
// Do nothing, clean up, and continue looping.
} else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) {
BackupObserverUtils
@@ -636,7 +639,8 @@
EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
// Abort entire backup pass.
backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
- mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ currentPackage.applicationInfo, /* allowKill= */ true);
return;
} else {
// Success!
@@ -1005,7 +1009,8 @@
mIsCancelled = true;
// Cancel tasks spun off by this task.
mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll);
- mUserBackupManagerService.tearDownAgentAndKill(mTarget.applicationInfo);
+ mUserBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mTarget.applicationInfo, /* allowKill= */ true);
// Free up everyone waiting on this task and its children.
mPreflightLatch.countDown();
mBackupLatch.countDown();
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 82232a6..689c43a 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -1303,7 +1303,7 @@
// For PM metadata (for which applicationInfo is null) there is no agent-bound state.
if (mCurrentPackage.applicationInfo != null) {
mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
- mCurrentPackage.applicationInfo);
+ mCurrentPackage.applicationInfo, /* allowKill= */ false);
}
mAgent = null;
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index b59e860..237ffa3 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -727,7 +727,8 @@
latch.await();
}
- mBackupManagerService.tearDownAgentAndKill(app);
+ mBackupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ app, /* allowKill= */ true);
} catch (RemoteException e) {
Slog.d(TAG, "Lost app trying to shut down");
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index e5c7e5c..dad84c8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -51,7 +51,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArraySet;
import android.util.EventLog;
@@ -1487,49 +1486,10 @@
// If this wasn't the PM pseudopackage, tear down the agent side
if (mCurrentPackage.applicationInfo != null) {
- // unbind and tidy up even on timeout or failure
- try {
- backupManagerService
- .getActivityManager()
- .unbindBackupAgent(mCurrentPackage.applicationInfo);
-
- // The agent was probably running with a stub Application object,
- // which isn't a valid run mode for the main app logic. Shut
- // down the app so that next time it's launched, it gets the
- // usual full initialization. Note that this is only done for
- // full-system restores: when a single app has requested a restore,
- // it is explicitly not killed following that operation.
- //
- // We execute this kill when these conditions hold:
- // 1. it's not a system-uid process,
- // 2. the app did not request its own restore (mTargetPackage == null), and
- // either
- // 3a. the app is a full-data target (TYPE_FULL_STREAM) or
- // b. the app does not state android:killAfterRestore="false" in its manifest
- final int appFlags = mCurrentPackage.applicationInfo.flags;
- final boolean killAfterRestore =
- !UserHandle.isCore(mCurrentPackage.applicationInfo.uid)
- && ((mRestoreDescription.getDataType()
- == RestoreDescription.TYPE_FULL_STREAM)
- || ((appFlags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE)
- != 0));
-
- if (mTargetPackage == null && killAfterRestore) {
- if (DEBUG) {
- Slog.d(
- TAG,
- "Restore complete, killing host process of "
- + mCurrentPackage.applicationInfo.processName);
- }
- backupManagerService
- .getActivityManager()
- .killApplicationProcess(
- mCurrentPackage.applicationInfo.processName,
- mCurrentPackage.applicationInfo.uid);
- }
- } catch (RemoteException e) {
- // can't happen; we run in the same process as the activity manager
- }
+ // If mTargetPackage is not null it means the app requested its own restore, in which
+ // case we won't allow the app to be killed.
+ backupManagerService.getBackupAgentConnectionManager().unbindAgent(
+ mCurrentPackage.applicationInfo, /* allowKill= */ mTargetPackage == null);
}
// The caller is responsible for reestablishing the state machine; our
diff --git a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
index 7a2106b..4bcfb33 100644
--- a/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
+++ b/services/companion/java/com/android/server/companion/BackupRestoreProcessor.java
@@ -240,9 +240,10 @@
boolean matchesMacAddress = Objects.equals(
associationInfo.getDeviceMacAddress(),
restored.getDeviceMacAddress());
- boolean matchesTag = !Flags.associationTag()
- || Objects.equals(associationInfo.getTag(), restored.getTag());
- return matchesMacAddress && matchesTag;
+ boolean matchesDeviceId = !Flags.associationTag()
+ || (associationInfo.getDeviceId() != null
+ && associationInfo.getDeviceId().isSameDevice(restored.getDeviceId()));
+ return matchesMacAddress || matchesDeviceId;
};
AssociationInfo local = CollectionUtils.find(localAssociations, isSameDevice);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 7cba9e0..418f3a1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -55,6 +55,7 @@
import android.bluetooth.BluetoothManager;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
+import android.companion.DeviceId;
import android.companion.IAssociationRequestCallback;
import android.companion.ICompanionDeviceManager;
import android.companion.IOnAssociationsChangedListener;
@@ -318,7 +319,6 @@
public List<AssociationInfo> getAssociations(String packageName, int userId) {
enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
"get associations");
-
return mAssociationStore.getActiveAssociationsByPackage(userId, packageName);
}
@@ -694,14 +694,10 @@
}
@Override
- public void setAssociationTag(int associationId, String tag) {
- mAssociationRequestsProcessor.setAssociationTag(associationId, tag);
+ public void setDeviceId(int associationId, DeviceId deviceId) {
+ mAssociationRequestsProcessor.setDeviceId(associationId, deviceId);
}
- @Override
- public void clearAssociationTag(int associationId) {
- setAssociationTag(associationId, null);
- }
@Override
public byte[] getBackupPayload(int userId) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index 77b1780..f2d019b 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -38,6 +38,7 @@
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
+import android.companion.DeviceId;
import android.graphics.drawable.Icon;
import android.net.MacAddress;
import android.os.Environment;
@@ -131,8 +132,11 @@
* revoked="false"
* last_time_connected="1634641160229"
* time_approved="1634389553216"
- * system_data_sync_flags="0"/>
- *
+ * system_data_sync_flags="0"
+ * device_icon="device_icon">
+ * <device_id
+ * custom_device_id="1234"/>
+ * </association>
* <association
* id="3"
* profile="android.app.role.COMPANION_DEVICE_WATCH"
@@ -143,7 +147,11 @@
* revoked="false"
* last_time_connected="1634641160229"
* time_approved="1634641160229"
- * system_data_sync_flags="1"/>
+ * system_data_sync_flags="1"
+ * device_icon="device_icon">
+ * <device_id
+ * custom_device_id="1234"/>
+ * </association>
* </associations>
* </state>
* }</pre>
@@ -160,7 +168,7 @@
private static final String XML_TAG_STATE = "state";
private static final String XML_TAG_ASSOCIATIONS = "associations";
private static final String XML_TAG_ASSOCIATION = "association";
- private static final String XML_TAG_TAG = "tag";
+ private static final String XML_TAG_DEVICE_ID = "device_id";
private static final String XML_ATTR_PERSISTENCE_VERSION = "persistence-version";
private static final String XML_ATTR_MAX_ID = "max-id";
@@ -177,6 +185,8 @@
private static final String XML_ATTR_LAST_TIME_CONNECTED = "last_time_connected";
private static final String XML_ATTR_SYSTEM_DATA_SYNC_FLAGS = "system_data_sync_flags";
private static final String XML_ATTR_DEVICE_ICON = "device_icon";
+ private static final String XML_ATTR_CUSTOM_DEVICE_ID = "custom_device_id";
+ private static final String XML_ATTR_MAC_ADDRESS_DEVICE_ID = "mac_address_device_id";
private static final String LEGACY_XML_ATTR_DEVICE = "device";
@@ -389,16 +399,16 @@
requireStartOfTag(parser, XML_TAG_ASSOCIATION);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- final String tag = readStringAttribute(parser, XML_TAG_TAG);
final String deviceAddress = readStringAttribute(parser, LEGACY_XML_ATTR_DEVICE);
final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
final boolean notify = readBooleanAttribute(parser, XML_ATTR_NOTIFY_DEVICE_NEARBY);
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
- return new AssociationInfo(associationId, userId, appPackage, tag,
+ return new AssociationInfo(associationId, userId, appPackage,
MacAddress.fromString(deviceAddress), null, profile, null,
/* managedByCompanionApp */ false, notify, /* revoked */ false, /* pending */ false,
- timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, null);
+ timeApproved, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, /* deviceIcon */ null,
+ /* deviceId */ null);
}
private static Associations readAssociationsV1(@NonNull TypedXmlPullParser parser,
@@ -436,7 +446,6 @@
final int associationId = readIntAttribute(parser, XML_ATTR_ID);
final String profile = readStringAttribute(parser, XML_ATTR_PROFILE);
final String appPackage = readStringAttribute(parser, XML_ATTR_PACKAGE);
- final String tag = readStringAttribute(parser, XML_TAG_TAG);
final MacAddress macAddress = stringToMacAddress(
readStringAttribute(parser, XML_ATTR_MAC_ADDRESS));
final String displayName = readStringAttribute(parser, XML_ATTR_DISPLAY_NAME);
@@ -451,10 +460,26 @@
XML_ATTR_SYSTEM_DATA_SYNC_FLAGS, 0);
final Icon deviceIcon = byteArrayToIcon(
readByteArrayAttribute(parser, XML_ATTR_DEVICE_ICON));
+ parser.nextTag();
+ final DeviceId deviceId = readDeviceId(parser);
- return new AssociationInfo(associationId, userId, appPackage, tag, macAddress, displayName,
+ return new AssociationInfo(associationId, userId, appPackage, macAddress, displayName,
profile, null, selfManaged, notify, revoked, pending, timeApproved,
- lastTimeConnected, systemDataSyncFlags, deviceIcon);
+ lastTimeConnected, systemDataSyncFlags, deviceIcon, deviceId);
+ }
+
+ private static DeviceId readDeviceId(@NonNull TypedXmlPullParser parser)
+ throws XmlPullParserException {
+ if (isStartOfTag(parser, XML_TAG_DEVICE_ID)) {
+ final String customDeviceId = readStringAttribute(
+ parser, XML_ATTR_CUSTOM_DEVICE_ID);
+ final MacAddress macAddress = stringToMacAddress(
+ readStringAttribute(parser, XML_ATTR_MAC_ADDRESS_DEVICE_ID));
+
+ return new DeviceId(customDeviceId, macAddress);
+ }
+
+ return null;
}
private static void writeAssociations(@NonNull XmlSerializer parent,
@@ -475,7 +500,6 @@
writeIntAttribute(serializer, XML_ATTR_ID, a.getId());
writeStringAttribute(serializer, XML_ATTR_PROFILE, a.getDeviceProfile());
writeStringAttribute(serializer, XML_ATTR_PACKAGE, a.getPackageName());
- writeStringAttribute(serializer, XML_TAG_TAG, a.getTag());
writeStringAttribute(serializer, XML_ATTR_MAC_ADDRESS, a.getDeviceMacAddressAsString());
writeStringAttribute(serializer, XML_ATTR_DISPLAY_NAME, a.getDisplayName());
writeBooleanAttribute(serializer, XML_ATTR_SELF_MANAGED, a.isSelfManaged());
@@ -490,14 +514,33 @@
writeByteArrayAttribute(
serializer, XML_ATTR_DEVICE_ICON, iconToByteArray(a.getDeviceIcon()));
+ writeDeviceId(serializer, a);
serializer.endTag(null, XML_TAG_ASSOCIATION);
}
+ private static void writeDeviceId(XmlSerializer parent, @NonNull AssociationInfo a)
+ throws IOException {
+ if (a.getDeviceId() != null) {
+ final XmlSerializer serializer = parent.startTag(null, XML_TAG_DEVICE_ID);
+ writeStringAttribute(
+ serializer,
+ XML_ATTR_CUSTOM_DEVICE_ID,
+ a.getDeviceId().getCustomId()
+ );
+ writeStringAttribute(
+ serializer,
+ XML_ATTR_MAC_ADDRESS_DEVICE_ID,
+ a.getDeviceId().getMacAddressAsString()
+ );
+ serializer.endTag(null, XML_TAG_DEVICE_ID);
+ }
+ }
+
private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
throws XmlPullParserException {
if (isStartOfTag(parser, tag)) return;
throw new XmlPullParserException(
- "Should be at the start of \"" + XML_TAG_ASSOCIATIONS + "\" tag");
+ "Should be at the start of \"" + tag + "\" tag");
}
private static @Nullable MacAddress stringToMacAddress(@Nullable String address) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
index aebd11a..899b302 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -41,6 +41,7 @@
import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
+import android.companion.DeviceId;
import android.companion.Flags;
import android.companion.IAssociationRequestCallback;
import android.content.ComponentName;
@@ -298,10 +299,10 @@
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
- /* tag */ null, macAddress, displayName, deviceProfile, associatedDevice,
+ macAddress, displayName, deviceProfile, associatedDevice,
selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false,
/* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0,
- deviceIcon);
+ deviceIcon, /* deviceId */ null);
// Add role holder for association (if specified) and add new association to store.
maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver);
}
@@ -354,14 +355,14 @@
}
/**
- * Set association tag.
+ * Set Device id for the association.
*/
- public void setAssociationTag(int associationId, String tag) {
- Slog.i(TAG, "Setting association tag=[" + tag + "] to id=[" + associationId + "]...");
+ public void setDeviceId(int associationId, DeviceId deviceId) {
+ Slog.i(TAG, "Setting DeviceId=[" + deviceId + "] to id=[" + associationId + "]...");
AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
associationId);
- association = (new AssociationInfo.Builder(association)).setTag(tag).build();
+ association = (new AssociationInfo.Builder(association)).setDeviceId(deviceId).build();
mAssociationStore.updateAssociation(association);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 4b9065b..6069e34 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -88,6 +88,9 @@
/** Called when a secure window shows on the virtual display. */
void onSecureWindowShown(int displayId, @NonNull ActivityInfo activityInfo);
+ /** Called when a secure window is no longer shown on the virtual display. */
+ void onSecureWindowHidden(int displayId);
+
/** Returns true when an intent should be intercepted */
boolean shouldInterceptIntent(@NonNull Intent intent);
}
@@ -123,6 +126,9 @@
private boolean mIsMirrorDisplay = false;
private final CountDownLatch mDisplayIdSetLatch = new CountDownLatch(1);
+ // Used for detecting changes in the window flags.
+ private int mCurrentWindowFlags = 0;
+
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
private final ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -371,12 +377,19 @@
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
int displayId = waitAndGetDisplayId();
- // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
- // aware that the virtual display has a secure window on top.
- if ((windowFlags & FLAG_SECURE) != 0 && displayId != INVALID_DISPLAY) {
+ if (displayId != INVALID_DISPLAY) {
+ // The callback is fired only when windowFlags are changed. To let VirtualDevice owner
+ // aware that the virtual display has a secure window on top.
// Post callback on the main thread, so it doesn't block activity launching.
- mHandler.post(() -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
+ if ((windowFlags & FLAG_SECURE) != 0 && (mCurrentWindowFlags & FLAG_SECURE) == 0) {
+ mHandler.post(
+ () -> mActivityListener.onSecureWindowShown(displayId, activityInfo));
+ }
+ if ((windowFlags & FLAG_SECURE) == 0 && (mCurrentWindowFlags & FLAG_SECURE) != 0) {
+ mHandler.post(() -> mActivityListener.onSecureWindowHidden(displayId));
+ }
}
+ mCurrentWindowFlags = windowFlags;
if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
activityInfo.packageName,
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index d4beb019..a1d621d 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -44,6 +44,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.compat.CompatChanges;
import android.companion.AssociationInfo;
+import android.companion.AssociationRequest;
import android.companion.virtual.ActivityPolicyExemption;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
@@ -155,6 +156,9 @@
private static final String PERSISTENT_ID_PREFIX_CDM_ASSOCIATION = "companion:";
+ private static final List<String> DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS = List.of(
+ AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
+
/**
* Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched.
*/
@@ -308,6 +312,17 @@
}
}
+ @Override
+ public void onSecureWindowHidden(int displayId) {
+ if (android.companion.virtualdevice.flags.Flags.activityControlApi()) {
+ try {
+ mActivityListener.onSecureWindowHidden(displayId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Unable to call mActivityListener for display: " + displayId, e);
+ }
+ }
+ }
+
/**
* Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true
* if the intent matches any filter notifying the DisplayPolicyController to abort the
@@ -1348,6 +1363,11 @@
return hasCustomAudioInputSupportInternal();
}
+ @Override
+ public boolean canCreateMirrorDisplays() {
+ return DEVICE_PROFILES_ALLOWING_MIRROR_DISPLAYS.contains(getDeviceProfile());
+ }
+
private boolean hasCustomAudioInputSupportInternal() {
if (!Flags.vdmPublicApis()) {
return false;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index b9540eb..0820615 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -178,6 +178,7 @@
static_libs: [
"android.frameworks.vibrator-V1-java", // AIDL
+ "android.frameworks.devicestate-V1-java", // AIDL
"android.hardware.authsecret-V1.0-java",
"android.hardware.authsecret-V1-java",
"android.hardware.boot-V1.0-java", // HIDL
@@ -238,11 +239,11 @@
"connectivity_flags_lib",
"device_config_service_flags_java",
"dreams_flags_lib",
- "aconfig_flags_java",
"aconfig_new_storage_flags_lib",
"powerstats_flags_lib",
"locksettings_flags_lib",
"profiling_flags_lib",
+ "android.adpf.sessionmanager_aidl-java",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/SecurityStateManagerService.java b/services/core/java/com/android/server/SecurityStateManagerService.java
index 98039be..fe21fbd 100644
--- a/services/core/java/com/android/server/SecurityStateManagerService.java
+++ b/services/core/java/com/android/server/SecurityStateManagerService.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.ISecurityStateManager;
@@ -56,6 +57,15 @@
@Override
public Bundle getGlobalSecurityState() {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getGlobalSecurityStateInternal();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private Bundle getGlobalSecurityStateInternal() {
Bundle globalSecurityState = new Bundle();
globalSecurityState.putString(KEY_SYSTEM_SPL, Build.VERSION.SECURITY_PATCH);
globalSecurityState.putString(KEY_VENDOR_SPL,
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2012f56..a45b715 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -55,6 +55,8 @@
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnManager.VcnStatusCode;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.vcn.util.PersistableBundleUtils;
+import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Build;
@@ -90,8 +92,6 @@
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
-import com.android.server.vcn.util.PersistableBundleUtils;
-import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import java.io.File;
import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 01daceb..37d0c7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -132,7 +132,7 @@
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.security.Flags.preventIntentRedirect;
import static android.security.Flags.preventIntentRedirectCollectNestedKeysOnServerIfNotCollected;
-import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollected;
+import static android.security.Flags.preventIntentRedirectShowToastIfNestedKeysNotCollectedRW;
import static android.security.Flags.preventIntentRedirectThrowExceptionIfNestedKeysNotCollected;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -192,6 +192,7 @@
import android.Manifest;
import android.Manifest.permission;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionMethod;
@@ -7552,7 +7553,7 @@
}
void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo,
- ApplicationInfo sdkSandboxClientApp) {
+ ApplicationInfo sdkSandboxClientApp, int profileType) {
synchronized (mAppProfiler.mProfilerLock) {
if (!Build.IS_DEBUGGABLE) {
boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
@@ -7568,7 +7569,7 @@
+ "and not profileable by shell: " + app.packageName);
}
}
- mAppProfiler.setProfileAppLPf(processName, profilerInfo);
+ mAppProfiler.setProfileAppLPf(processName, profilerInfo, profileType);
}
}
@@ -15846,7 +15847,8 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
+ if (start && profileType == ProfilerInfo.PROFILE_TYPE_REGULAR
+ && (profilerInfo == null || profilerInfo.profileFd == null)) {
throw new IllegalArgumentException("null profile info or fd");
}
@@ -17489,7 +17491,9 @@
}
if (profilerInfo != null) {
- setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo, null);
+ // We only support normal method tracing along with app startup for now.
+ setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo,
+ null, /*profileType= */ ProfilerInfo.PROFILE_TYPE_REGULAR);
}
wmLock.notify();
}
@@ -19228,6 +19232,11 @@
return mKeyFields.mCreatorPackage;
}
+ @VisibleForTesting
+ public @NonNull Key getKeyFields() {
+ return mKeyFields;
+ }
+
public static boolean isValid(@NonNull Intent intent) {
IBinder binder = intent.getCreatorToken();
IntentCreatorToken token = null;
@@ -19271,9 +19280,13 @@
this.mFlags = intent.getFlags() & Intent.IMMUTABLE_FLAGS;
ClipData clipData = intent.getClipData();
if (clipData != null) {
- this.mClipDataUris = new ArrayList<>(clipData.getItemCount());
- for (int i = 0; i < clipData.getItemCount(); i++) {
- this.mClipDataUris.add(clipData.getItemAt(i).getUri());
+ clipData = clipData.cloneOnlyUriItems();
+ if (clipData != null) {
+ List<Uri> clipDataUris = new ArrayList<>();
+ clipData.collectUris(clipDataUris);
+ if (!clipDataUris.isEmpty()) {
+ this.mClipDataUris = clipDataUris;
+ }
}
}
}
@@ -19326,7 +19339,7 @@
"[IntentRedirect] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
+ intent + "; creatorPackage: " + creatorPackage);
- if (preventIntentRedirectShowToastIfNestedKeysNotCollected()) {
+ if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
UiThread.getHandler().post(
() -> Toast.makeText(mContext,
"Nested keys not collected. go/report-bug-intentRedir to report a"
@@ -19375,11 +19388,34 @@
String creatorPackage) {
if (IntentCreatorToken.isValid(intent)) return null;
IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent);
+ return createOrGetIntentCreatorToken(intent, key);
+ }
+
+ /**
+ * @hide
+ */
+ @EnforcePermission("android.permission.INTERACT_ACROSS_USERS_FULL")
+ public IBinder refreshIntentCreatorToken(Intent intent) {
+ refreshIntentCreatorToken_enforcePermission();
+ IBinder binder = intent.getCreatorToken();
+ if (binder instanceof IntentCreatorToken) {
+ IntentCreatorToken token = (IntentCreatorToken) binder;
+ IntentCreatorToken.Key key = new IntentCreatorToken.Key(token.getCreatorUid(),
+ token.getCreatorPackage(), intent);
+ return createOrGetIntentCreatorToken(intent, key);
+
+ } else {
+ throw new IllegalArgumentException("intent does not contain a creator token.");
+ }
+ }
+
+ private static IntentCreatorToken createOrGetIntentCreatorToken(Intent intent,
+ IntentCreatorToken.Key key) {
IntentCreatorToken token;
synchronized (sIntentCreatorTokenCache) {
WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key);
if (ref == null || ref.get() == null) {
- token = new IntentCreatorToken(creatorUid, creatorPackage, intent);
+ token = new IntentCreatorToken(key.mCreatorUid, key.mCreatorPackage, intent);
sIntentCreatorTokenCache.put(key, token.mRef);
} else {
token = ref.get();
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 37d058b..9a63546 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1155,7 +1155,7 @@
String profileFile = null;
boolean start = false;
int userId = UserHandle.USER_CURRENT;
- int profileType = 0;
+ int profileType = ProfilerInfo.PROFILE_TYPE_REGULAR;
mSamplingInterval = 0;
mStreaming = false;
mClockType = ProfilerInfo.CLOCK_TYPE_DEFAULT;
@@ -1197,6 +1197,18 @@
}
}
process = getNextArgRequired();
+ } else if ("lowoverhead".equals(cmd)) {
+ // This is an experimental low overhead profiling.
+ profileType = ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD;
+ cmd = getNextArgRequired();
+ if ("start".equals(cmd)) {
+ start = true;
+ } else if ("stop".equals(cmd)) {
+ start = false;
+ } else {
+ throw new IllegalArgumentException("Profile command not valid");
+ }
+ process = getNextArgRequired();
} else {
// Compatibility with old syntax: process is specified first.
process = cmd;
@@ -1216,7 +1228,12 @@
ParcelFileDescriptor fd = null;
ProfilerInfo profilerInfo = null;
- if (start) {
+ // For regular method tracing profileFile should be provided with the start command. For
+ // low overhead method tracing the profileFile is optional and provided with the stop
+ // command.
+ if ((start && profileType == ProfilerInfo.PROFILE_TYPE_REGULAR)
+ || (profileType == ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD
+ && !start && getRemainingArgsCount() > 0)) {
profileFile = getNextArgRequired();
fd = openFileForSystem(profileFile, "w");
if (fd == null) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 4f2d69e..6b24df4 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1992,7 +1992,7 @@
}
@GuardedBy("mProfilerLock")
- private void stopProfilerLPf(ProcessRecord proc, int profileType) {
+ private void stopProfilerLPf(ProcessRecord proc, ProfilerInfo profilerInfo, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
proc = mProfileData.getProfileProc();
profileType = mProfileType;
@@ -2006,7 +2006,7 @@
return;
}
try {
- thread.profilerControl(false, null, profileType);
+ thread.profilerControl(false, profilerInfo, profileType);
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
}
@@ -2041,41 +2041,58 @@
ProfilerInfo profilerInfo, int profileType) {
try {
if (start) {
- stopProfilerLPf(null, 0);
+ boolean needsFile = (profileType == ProfilerInfo.PROFILE_TYPE_REGULAR);
+ stopProfilerLPf(null, null, 0);
mService.setProfileApp(proc.info, proc.processName, profilerInfo,
- proc.isSdkSandbox ? proc.getClientInfoForSdkSandbox() : null);
+ proc.isSdkSandbox ? proc.getClientInfoForSdkSandbox() : null, profileType);
mProfileData.setProfileProc(proc);
mProfileType = profileType;
- ParcelFileDescriptor fd = profilerInfo.profileFd;
- try {
- fd = fd.dup();
- } catch (IOException e) {
- fd = null;
- }
- profilerInfo.profileFd = fd;
- proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType);
- fd = null;
- try {
- mProfileData.getProfilerInfo().profileFd.close();
- } catch (IOException e) {
- }
- mProfileData.getProfilerInfo().profileFd = null;
- if (proc.getPid() == mService.MY_PID) {
- // When profiling the system server itself, avoid closing the file
- // descriptor, as profilerControl will not create a copy.
- // Note: it is also not correct to just set profileFd to null, as the
- // whole ProfilerInfo instance is passed down!
- profilerInfo = null;
- }
- } else {
- stopProfilerLPf(proc, profileType);
- if (profilerInfo != null && profilerInfo.profileFd != null) {
+ ParcelFileDescriptor fd = null;
+ if (needsFile) {
+ fd = profilerInfo.profileFd;
try {
- profilerInfo.profileFd.close();
+ fd = fd.dup();
+ } catch (IOException e) {
+ fd = null;
+ }
+ profilerInfo.profileFd = fd;
+ }
+
+ proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType);
+
+ if (needsFile) {
+ fd = null;
+ try {
+ mProfileData.getProfilerInfo().profileFd.close();
} catch (IOException e) {
}
+ mProfileData.getProfilerInfo().profileFd = null;
+
+ if (proc.getPid() == mService.MY_PID) {
+ // When profiling the system server itself, avoid closing the file
+ // descriptor, as profilerControl will not create a copy.
+ // Note: it is also not correct to just set profileFd to null, as the
+ // whole ProfilerInfo instance is passed down!
+ profilerInfo = null;
+ }
}
+ } else {
+ boolean mayNeedFile = (profileType == ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD);
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ ParcelFileDescriptor fd = profilerInfo.profileFd;
+ try {
+ if (mayNeedFile) {
+ fd = fd.dup();
+ } else {
+ fd.close();
+ }
+ } catch (IOException e) {
+ fd = null;
+ }
+ profilerInfo.profileFd = fd;
+ }
+ stopProfilerLPf(proc, profilerInfo, profileType);
}
return true;
@@ -2092,7 +2109,7 @@
}
@GuardedBy("mProfilerLock")
- void setProfileAppLPf(String processName, ProfilerInfo profilerInfo) {
+ void setProfileAppLPf(String processName, ProfilerInfo profilerInfo, int profileType) {
mProfileData.setProfileApp(processName);
if (mProfileData.getProfilerInfo() != null) {
@@ -2103,8 +2120,10 @@
}
}
}
- mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
- mProfileType = 0;
+ if (profilerInfo != null) {
+ mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
+ }
+ mProfileType = profileType;
}
@GuardedBy("mProfilerLock")
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index a32d3cb..05aeb42 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -57,7 +57,6 @@
final boolean visibleToInstantApp;
public final boolean exported;
final int initialPriority;
- final ApplicationInfo applicationInfo;
BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
String _packageName, String _featureId, String _receiverId, String _requiredPermission,
@@ -74,10 +73,9 @@
instantApp = _instantApp;
visibleToInstantApp = _visibleToInstantApp;
exported = _exported;
- applicationInfo = _applicationInfo;
initialPriority = getPriority();
setPriority(calculateAdjustedPriority(owningUid, initialPriority,
- applicationInfo, platformCompat));
+ _applicationInfo, platformCompat));
}
public @Nullable String getReceiverClassName() {
@@ -91,7 +89,7 @@
}
public @NonNull ApplicationInfo getApplicationInfo() {
- return applicationInfo;
+ return receiverList.app.info;
}
@NeverCompile
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index f86474f..70febcd 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_END;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_RESTRICTION_CHANGE;
import static android.app.ActivityThread.PROC_START_SEQ_IDENT;
+import static android.content.pm.Flags.appCompatOption16kb;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
@@ -2025,6 +2026,16 @@
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
+ if (appCompatOption16kb()) {
+ boolean is16KbDevice = Os.sysconf(OsConstants._SC_PAGESIZE) == 16384;
+ if (is16KbDevice
+ && mService.mContext
+ .getPackageManager()
+ .isPageSizeCompatEnabled(app.info.packageName)) {
+ runtimeFlags |= Zygote.ENABLE_PAGE_SIZE_APP_COMPAT;
+ }
+ }
+
String invokeWith = null;
if (debuggableFlag) {
// Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 7660c15..d0153d8 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -42,7 +42,6 @@
import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
import static com.android.aconfig_new_storage.Flags.supportImmediateLocalOverrides;
import static com.android.aconfig_new_storage.Flags.supportClearLocalOverridesImmediately;
-import static com.android.aconfig.flags.Flags.enableSystemAconfigdRust;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -140,6 +139,11 @@
// The list is sorted.
@VisibleForTesting
static final String[] sDeviceConfigAconfigScopes = new String[] {
+ "tv_os",
+ "aaos_carframework_triage",
+ "aaos_performance_triage",
+ "aaos_user_triage",
+ "aaos_window_triage",
"aaos_audio_triage",
"aaos_power_triage",
"aaos_sdv",
@@ -461,9 +465,8 @@
static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
// connect to aconfigd socket
LocalSocket client = new LocalSocket();
- String socketName = enableSystemAconfigdRust()
- ? "aconfigd_system" : "aconfigd";
- try{
+ String socketName = "aconfigd_system";
+ try {
client.connect(new LocalSocketAddress(
socketName, LocalSocketAddress.Namespace.RESERVED));
Slog.d(TAG, "connected to aconfigd socket");
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c6317bd..2f7a54d 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9554,9 +9554,11 @@
index = (mIndexMap.valueAt(i) + 5)/10;
}
- sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
- SENDMSG_REPLACE, device, isAbsoluteVolume ? 1 : 0, this,
- /*delay=*/0);
+ if (mStreamType == AudioSystem.STREAM_MUSIC) {
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION,
+ SENDMSG_QUEUE, device, isAbsoluteVolume ? 1 : 0, this,
+ /*delay=*/0);
+ }
setStreamVolumeIndex(index, device);
}
@@ -10103,7 +10105,7 @@
/*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {
synchronized (VolumeStreamState.class) {
- sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_REPLACE,
+ sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_QUEUE,
device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device)
|| AudioSystem.isLeAudioDeviceType(device) ? 1 : 0),
streamState, /*delay=*/0);
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 995e16b..1b5f0e5 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -657,8 +657,8 @@
return "CSD lowering volume to RS1";
case UPDATE_ABS_VOLUME_ATTENUATION:
return String.format(java.util.Locale.US,
- "Updating CSD absolute volume attenuation on device %d with %.2f dB ",
- mLongValue, mFloatValue);
+ "Updating CSD absolute volume attenuation on device 0x%s with %.2f dB ",
+ Long.toHexString(mLongValue), mFloatValue);
}
return new StringBuilder("FIXME invalid event type:").append(mEventType).toString();
}
diff --git a/services/core/java/com/android/server/audio/OWNERS b/services/core/java/com/android/server/audio/OWNERS
index b70de29..709e4c2 100644
--- a/services/core/java/com/android/server/audio/OWNERS
+++ b/services/core/java/com/android/server/audio/OWNERS
@@ -1,2 +1,3 @@
+atneya@google.com
jmtrivi@google.com
elaurent@google.com
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e1bb8a1..4c5f652 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -26,13 +26,13 @@
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
import static android.net.ipsec.ike.IkeSessionParams.ESP_ENCAP_TYPE_AUTO;
import static android.net.ipsec.ike.IkeSessionParams.ESP_IP_VERSION_AUTO;
+import static android.net.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
import static android.os.PowerWhitelistManager.REASON_VPN;
import static android.os.UserHandle.PER_USER_RANGE;
import static android.telephony.CarrierConfigManager.KEY_MIN_UDP_PORT_4500_NAT_TIMEOUT_SEC_INT;
import static android.telephony.CarrierConfigManager.KEY_PREFERRED_IKE_PROTOCOL_INT;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
-import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
import static java.util.Objects.requireNonNull;
@@ -103,6 +103,8 @@
import android.net.ipsec.ike.exceptions.IkeTimeoutException;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnTransportInfo;
+import android.net.vcn.util.MtuUtils;
+import android.net.vcn.util.PersistableBundleUtils;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -150,8 +152,6 @@
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
-import com.android.server.vcn.util.MtuUtils;
-import com.android.server.vcn.util.PersistableBundleUtils;
import libcore.io.IoUtils;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8b9c664..2513443 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,16 @@
import static android.Manifest.permission.CONTROL_DEVICE_STATE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_DUAL_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FEATURE_REAR_DISPLAY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
+import static android.frameworks.devicestate.DeviceStateConfiguration.DeviceStatePropertyValue.FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT;
import static android.hardware.devicestate.DeviceState.PROPERTY_FEATURE_REAR_DISPLAY;
+import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST;
import static android.hardware.devicestate.DeviceState.PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS;
@@ -44,6 +53,10 @@
import android.app.ActivityManagerInternal;
import android.app.IProcessObserver;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateManager;
@@ -56,9 +69,12 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.os.Trace;
+import android.util.LongSparseLongArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -82,6 +98,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
@@ -130,6 +147,8 @@
@NonNull
private final BinderService mBinderService;
@NonNull
+ private final HalService mHalService;
+ @NonNull
private final OverrideRequestController mOverrideRequestController;
@NonNull
private final DeviceStateProviderListener mDeviceStateProviderListener;
@@ -139,7 +158,7 @@
// All supported device states keyed by identifier.
@GuardedBy("mLock")
- private SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
+ private final SparseArray<DeviceState> mDeviceStates = new SparseArray<>();
// The current committed device state. Will be empty until the first device state provided by
// the DeviceStateProvider is committed.
@@ -177,7 +196,7 @@
@GuardedBy("mLock")
private final SparseArray<ProcessRecord> mProcessRecords = new SparseArray<>();
- private Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
+ private final Set<Integer> mDeviceStatesAvailableForAppRequests = new HashSet<>();
private Set<Integer> mFoldedDeviceStates = new HashSet<>();
@@ -259,6 +278,7 @@
mDeviceStateProviderListener = new DeviceStateProviderListener();
mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
+ mHalService = new HalService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mDeviceStateNotificationController = new DeviceStateNotificationController(
context, mHandler,
@@ -272,6 +292,10 @@
@Override
public void onStart() {
publishBinderService(Context.DEVICE_STATE_SERVICE, mBinderService);
+ String halServiceName = IDeviceStateService.DESCRIPTOR + "/default";
+ if (ServiceManager.isDeclared(halServiceName)) {
+ publishBinderService(halServiceName, mHalService);
+ }
publishLocalService(DeviceStateManagerInternal.class, new LocalService());
if (!Flags.deviceStatePropertyMigration()) {
@@ -440,6 +464,11 @@
return mBinderService;
}
+ @VisibleForTesting
+ IDeviceStateService getHalBinderService() {
+ return mHalService;
+ }
+
private void updateSupportedStates(DeviceState[] supportedDeviceStates,
@DeviceStateProvider.SupportedStatesUpdatedReason int reason) {
synchronized (mLock) {
@@ -1282,6 +1311,124 @@
}
}
+ private final class HalService extends IDeviceStateService.Stub {
+ private final LongSparseLongArray mPublicProperties = new LongSparseLongArray();
+ public HalService() {
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN);
+ mPublicProperties.put(
+ DeviceState.PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+ FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ FEATURE_REAR_DISPLAY);
+ mPublicProperties.put(
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT,
+ FEATURE_DUAL_DISPLAY);
+ }
+
+ private final class HalBinderCallback implements IDeviceStateManagerCallback {
+ private final IDeviceStateListener mListener;
+
+ private HalBinderCallback(@NonNull IDeviceStateListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onDeviceStateInfoChanged(DeviceStateInfo info) throws RemoteException {
+ DeviceStateConfiguration config = new DeviceStateConfiguration();
+ Set<Integer> systemProperties = new HashSet<>(
+ info.currentState.getConfiguration().getSystemProperties());
+ Set<Integer> physicalProperties = new HashSet<>(
+ info.currentState.getConfiguration().getPhysicalProperties());
+ config.deviceProperties = 0;
+ for (Integer prop : systemProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ for (Integer prop : physicalProperties) {
+ Long publicProperty = mPublicProperties.get(prop);
+ if (publicProperty != null) {
+ config.deviceProperties |= publicProperty.longValue();
+ }
+ }
+ mListener.onDeviceStateChanged(config);
+ }
+
+ @Override
+ public void onRequestActive(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public void onRequestCanceled(IBinder token) {
+ //No-op
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mListener.asBinder();
+ }
+ }
+
+ @Override
+ public void registerListener(IDeviceStateListener listener) throws RemoteException {
+ if (listener == null) {
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ final int callingPid = Binder.getCallingPid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ HalBinderCallback callback = new HalBinderCallback(listener);
+ DeviceStateInfo info = registerProcess(callingPid, callback);
+ if (info != null) {
+ callback.onDeviceStateInfoChanged(info);
+ }
+ } catch (SecurityException e) {
+ throw new ServiceSpecificException(ErrorCode.ALREADY_EXISTS);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void unregisterListener(IDeviceStateListener listener) throws RemoteException {
+ final int callingPid = Binder.getCallingPid();
+
+ synchronized (mLock) {
+ if (mProcessRecords.contains(callingPid)) {
+ mProcessRecords.remove(callingPid);
+ return;
+ }
+ }
+
+ throw new ServiceSpecificException(ErrorCode.BAD_INPUT);
+ }
+
+ @Override
+ public int getInterfaceVersion() throws RemoteException {
+ return IDeviceStateService.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return IDeviceStateService.HASH;
+ }
+ }
+
/** Implementation of {@link IDeviceStateManager} published as a binder service. */
private final class BinderService extends IDeviceStateManager.Stub {
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 0f65360..c19d2c9 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -130,6 +130,8 @@
private static final int MSG_START_SENSOR_LISTENER = 3;
private static final int MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED = 4;
private static final int MSG_SENSOR_CHANGED = 5;
+ private static final int MSG_START_DISPLAY_LISTENER = 6;
+ private static final int MSG_STOP_DISPLAY_LISTENER = 7;
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
@@ -159,7 +161,9 @@
private SensorListener mSensorListener;
private Sensor mLightSensor;
private SettingsObserver mSettingsObserver;
- private DisplayListener mDisplayListener;
+ private final DisplayListener mDisplayListener = new DisplayListener();
+ private boolean mDisplayListenerRegistered;
+ private boolean mIsDisplayActive;
private boolean mSensorRegistered;
private boolean mColorSamplingEnabled;
private int mNoFramesToSample;
@@ -231,6 +235,8 @@
mSettingsObserver = new SettingsObserver(mBgHandler);
mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver);
+ startDisplayListener();
+ mIsDisplayActive = isDisplayActive();
startSensorListener();
final IntentFilter intentFilter = new IntentFilter();
@@ -260,6 +266,7 @@
Slog.d(TAG, "Stop");
}
mBgHandler.removeMessages(MSG_BACKGROUND_START);
+ stopDisplayListener();
stopSensorListener();
mInjector.unregisterSensorListener(mContext, mSensorListener);
mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver);
@@ -443,6 +450,11 @@
private void handleSensorChanged(Sensor lightSensor) {
if (mLightSensor != lightSensor) {
mLightSensor = lightSensor;
+ if (lightSensor != null) {
+ mBgHandler.sendEmptyMessage(MSG_START_DISPLAY_LISTENER);
+ } else {
+ mBgHandler.sendEmptyMessage(MSG_STOP_DISPLAY_LISTENER);
+ }
stopSensorListener();
// Attempt to restart the sensor listener. It will check to see if it should be running
// so there is no need to also check here.
@@ -455,6 +467,7 @@
&& mLightSensor != null
&& mAmbientBrightnessStatsTracker != null
&& mInjector.isInteractive(mContext)
+ && mIsDisplayActive
&& mInjector.isBrightnessModeAutomatic(mContentResolver)) {
mAmbientBrightnessStatsTracker.start();
mSensorRegistered = true;
@@ -825,11 +838,14 @@
pw.println(" mColorSamplingEnabled=" + mColorSamplingEnabled);
pw.println(" mNoFramesToSample=" + mNoFramesToSample);
pw.println(" mFrameRate=" + mFrameRate);
+ pw.println(" mIsDisplayActive=" + mIsDisplayActive);
+ pw.println(" mDisplayListenerRegistered=" + mDisplayListenerRegistered);
}
private void enableColorSampling() {
if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
|| !mInjector.isInteractive(mContext)
+ || !mIsDisplayActive
|| mColorSamplingEnabled
|| !mShouldCollectColorSample) {
return;
@@ -860,10 +876,6 @@
+ mNoFramesToSample + " frames, success=" + mColorSamplingEnabled);
}
}
- if (mColorSamplingEnabled && mDisplayListener == null) {
- mDisplayListener = new DisplayListener();
- mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
- }
}
private void disableColorSampling() {
@@ -872,10 +884,6 @@
}
mInjector.enableColorSampling(/* enable= */ false, /* noFrames= */ 0);
mColorSamplingEnabled = false;
- if (mDisplayListener != null) {
- mInjector.unRegisterDisplayListener(mContext, mDisplayListener);
- mDisplayListener = null;
- }
if (DEBUG) {
Slog.i(TAG, "turning off color sampling");
}
@@ -913,6 +921,25 @@
}
}
+ private boolean isDisplayActive() {
+ return Display.isActiveState(mInjector.getDisplayState(mContext));
+ }
+
+ private void startDisplayListener() {
+ if (!mDisplayListenerRegistered && mLightSensor != null && mInjector.isInteractive(mContext)
+ && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
+ mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
+ mDisplayListenerRegistered = true;
+ }
+ }
+
+ private void stopDisplayListener() {
+ if (mDisplayListenerRegistered) {
+ mInjector.unregisterDisplayListener(mContext, mDisplayListener);
+ mDisplayListenerRegistered = false;
+ }
+ }
+
private final class SensorListener implements SensorEventListener {
@Override
public void onSensorChanged(SensorEvent event) {
@@ -941,6 +968,15 @@
public void onDisplayChanged(int displayId) {
if (displayId == Display.DEFAULT_DISPLAY) {
updateColorSampling();
+ boolean isDisplayActive = isDisplayActive();
+ if (mIsDisplayActive != isDisplayActive) {
+ mIsDisplayActive = isDisplayActive;
+ if (isDisplayActive) {
+ mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
+ } else {
+ mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
+ }
+ }
}
}
}
@@ -956,8 +992,10 @@
Slog.v(TAG, "settings change " + uri);
}
if (mInjector.isBrightnessModeAutomatic(mContentResolver)) {
+ mBgHandler.sendEmptyMessage(MSG_START_DISPLAY_LISTENER);
mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
} else {
+ mBgHandler.sendEmptyMessage(MSG_STOP_DISPLAY_LISTENER);
mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
}
}
@@ -980,8 +1018,10 @@
batteryLevelChanged(level, scale);
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ mBgHandler.sendEmptyMessage(MSG_STOP_DISPLAY_LISTENER);
mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
+ mBgHandler.sendEmptyMessage(MSG_START_DISPLAY_LISTENER);
mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
}
}
@@ -1023,7 +1063,12 @@
case MSG_SENSOR_CHANGED:
handleSensorChanged((Sensor) msg.obj);
break;
-
+ case MSG_START_DISPLAY_LISTENER:
+ startDisplayListener();
+ break;
+ case MSG_STOP_DISPLAY_LISTENER:
+ stopDisplayListener();
+ break;
}
}
}
@@ -1151,6 +1196,12 @@
return context.getSystemService(PowerManager.class).isInteractive();
}
+ public int getDisplayState(Context context) {
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ return display.getState();
+ }
+
public int getNightDisplayColorTemperature(Context context) {
return context.getSystemService(ColorDisplayManager.class)
.getNightDisplayColorTemperature();
@@ -1208,7 +1259,7 @@
displayManager.registerDisplayListener(listener, handler);
}
- public void unRegisterDisplayListener(Context context,
+ public void unregisterDisplayListener(Context context,
DisplayManager.DisplayListener listener) {
final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
displayManager.unregisterDisplayListener(listener);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3871f2a..0e5fc41 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1681,7 +1681,12 @@
if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
return checkCallingPermission(ADD_MIRROR_DISPLAY, "canCreateMirrorDisplays");
}
- return virtualDevice != null;
+ try {
+ return virtualDevice != null && virtualDevice.canCreateMirrorDisplays();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to query virtual device for permissions", e);
+ return false;
+ }
}
private boolean canProjectVideo(IMediaProjection projection) {
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index eb76dcb..382c883 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -73,9 +73,13 @@
* </pre>
* Supported flags:
* <ul>
- * <li><pre>secure</pre>: creates a secure display</li>
- * <li><pre>own_content_only</pre>: only shows this display's own content</li>
- * <li><pre>should_show_system_decorations</pre>: supports system decorations</li>
+ * <li><code>secure</code>: creates a secure display</li>
+ * <li><code>own_content_only</code>: only shows this display's own content</li>
+ * <li><code>should_show_system_decorations</code>: supports system decorations</li>
+ * <li><code>gravity_top_left</code>: display the overlay at the top left of the screen</li>
+ * <li><code>gravity_top_right</code>: display the overlay at the top right of the screen</li>
+ * <li><code>gravity_bottom_right</code>: display the overlay at the bottom right of the screen</li>
+ * <li><code>gravity_bottom_left</code>: display the overlay at the bottom left of the screen</li>
* </ul>
* </p><p>
* Example:
@@ -113,6 +117,12 @@
private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
"should_show_system_decorations";
+ // Gravity flags to decide where the overlay should be shown.
+ private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
+ private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
+ private static final String GRAVITY_TOP_RIGHT = "gravity_top_right";
+ private static final String GRAVITY_BOTTOM_LEFT = "gravity_bottom_left";
+
private static final int MIN_WIDTH = 100;
private static final int MIN_HEIGHT = 100;
private static final int MAX_WIDTH = 4096;
@@ -237,8 +247,11 @@
String name = getContext().getResources().getString(
com.android.internal.R.string.display_manager_overlay_display_name,
number);
- int gravity = chooseOverlayGravity(number);
OverlayFlags flags = OverlayFlags.parseFlags(flagString);
+ int gravity = flags.mGravity;
+ if (flags.mGravity == Gravity.NO_GRAVITY) {
+ gravity = chooseOverlayGravity(number);
+ }
Slog.i(TAG, "Showing overlay display device #" + number
+ ": name=" + name + ", modes=" + Arrays.toString(modes.toArray())
@@ -266,6 +279,16 @@
}
}
+ private static int parseOverlayGravity(String overlayGravity) {
+ return switch (overlayGravity) {
+ case GRAVITY_TOP_LEFT -> Gravity.TOP | Gravity.LEFT;
+ case GRAVITY_TOP_RIGHT -> Gravity.TOP | Gravity.RIGHT;
+ case GRAVITY_BOTTOM_RIGHT -> Gravity.BOTTOM | Gravity.RIGHT;
+ case GRAVITY_BOTTOM_LEFT -> Gravity.BOTTOM | Gravity.LEFT;
+ default -> Gravity.NO_GRAVITY;
+ };
+ }
+
private abstract class OverlayDisplayDevice extends DisplayDevice {
private final String mName;
private final float mRefreshRate;
@@ -605,13 +628,17 @@
/** See {@link #OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}. */
final boolean mShouldShowSystemDecorations;
+ final int mGravity;
+
OverlayFlags(
boolean secure,
boolean ownContentOnly,
- boolean shouldShowSystemDecorations) {
+ boolean shouldShowSystemDecorations,
+ int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
+ mGravity = gravity;
}
static OverlayFlags parseFlags(@Nullable String flagString) {
@@ -619,24 +646,26 @@
return new OverlayFlags(
false /* secure */,
false /* ownContentOnly */,
- false /* shouldShowSystemDecorations */);
+ false /* shouldShowSystemDecorations */,
+ Gravity.NO_GRAVITY);
}
boolean secure = false;
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
+ int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
secure = true;
- }
- if (OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY.equals(flag)) {
+ } else if (OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY.equals(flag)) {
ownContentOnly = true;
- }
- if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
+ } else if (OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS.equals(flag)) {
shouldShowSystemDecorations = true;
+ } else {
+ gravity = parseOverlayGravity(flag);
}
}
- return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations);
+ return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations, gravity);
}
@Override
@@ -645,6 +674,7 @@
.append("secure=").append(mSecure)
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
+ .append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index dabef84..6ae58c4 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -43,6 +43,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Point;
+import android.hardware.display.IBrightnessListener;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.media.projection.IMediaProjection;
@@ -183,8 +184,11 @@
if (projection != null) {
mediaProjectionCallback = new MediaProjectionCallback(appToken);
}
+
+ Callback callbackDelegate = new Callback(
+ callback, virtualDisplayConfig.getBrightnessListener(), mHandler);
VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
- ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
+ ownerUid, ownerPackageName, surface, flags, callbackDelegate,
projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);
mVirtualDisplayDevices.put(appToken, device);
@@ -337,6 +341,7 @@
private final DisplayCutout mDisplayCutout;
private final float mDefaultBrightness;
private float mCurrentBrightness;
+ private final IBrightnessListener mBrightnessListener;
public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
int ownerUid, String ownerPackageName, Surface surface, int flags,
@@ -354,7 +359,8 @@
mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
mDisplayCutout = virtualDisplayConfig.getDisplayCutout();
mDefaultBrightness = virtualDisplayConfig.getDefaultBrightness();
- mCurrentBrightness = mDefaultBrightness;
+ mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID;
+ mBrightnessListener = virtualDisplayConfig.getBrightnessListener();
mMode = createMode(mWidth, mHeight, getRefreshRate());
mSurface = surface;
mFlags = flags;
@@ -464,6 +470,7 @@
}
}
if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower()
+ && mBrightnessListener != null
&& BrightnessUtils.isValidBrightnessValue(brightnessState)
&& brightnessState != mCurrentBrightness) {
mCurrentBrightness = brightnessState;
@@ -661,10 +668,13 @@
private static final int MSG_ON_REQUESTED_BRIGHTNESS_CHANGED = 3;
private final IVirtualDisplayCallback mCallback;
+ private final IBrightnessListener mBrightnessListener;
- public Callback(IVirtualDisplayCallback callback, Handler handler) {
+ Callback(IVirtualDisplayCallback callback, IBrightnessListener brightnessListener,
+ Handler handler) {
super(handler.getLooper());
mCallback = callback;
+ mBrightnessListener = brightnessListener;
}
@Override
@@ -681,7 +691,9 @@
mCallback.onStopped();
break;
case MSG_ON_REQUESTED_BRIGHTNESS_CHANGED:
- mCallback.onRequestedBrightnessChanged((Float) msg.obj);
+ if (mBrightnessListener != null) {
+ mBrightnessListener.onBrightnessChanged((Float) msg.obj);
+ }
break;
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
index 2e66fbc..102de73 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecAtomWriter.java
@@ -265,6 +265,26 @@
enumLogReason);
}
+ /**
+ * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a
+ * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change.
+ * @param isEnabled Whether the setting is enabled.
+ * @param enumLogReason The event that triggered the log.
+ * @param manufacturerPnpId Manufacturer PNP ID reported in the EDID.
+ * @param manufacturerYear Manufacture year reported in the EDID.
+ * @param manufacturerWeek Manufacture week reporter in the EDID.
+ */
+ public void powerStateChangeOnActiveSourceLostChanged(boolean isEnabled, int enumLogReason,
+ String manufacturerPnpId, int manufacturerYear, int manufacturerWeek) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.HDMI_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLED,
+ isEnabled,
+ enumLogReason,
+ manufacturerPnpId,
+ manufacturerYear,
+ manufacturerWeek);
+ }
+
private int earcStateToEnum(int earcState) {
switch (earcState) {
case HDMI_EARC_STATUS_IDLE:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 1b527da..0b667fc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -31,6 +32,7 @@
import android.os.SystemProperties;
import android.sysprop.HdmiProperties;
import android.util.Slog;
+import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
@@ -82,6 +84,8 @@
// lost.
private Handler mDelayedPopupOnActiveSourceLostHandler;
+ private boolean mIsActiveSourceLostPopupLaunched;
+
// Determines what action should be taken upon receiving Routing Control messages.
@VisibleForTesting
protected HdmiProperties.playback_device_action_on_routing_control_values
@@ -96,6 +100,7 @@
mDelayedStandbyOnActiveSourceLostHandler = new Handler(service.getServiceLooper());
mDelayedPopupOnActiveSourceLostHandler = new Handler(service.getServiceLooper());
mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
+ mIsActiveSourceLostPopupLaunched = false;
}
@Override
@@ -275,6 +280,7 @@
public void run() {
if (!isActiveSource()) {
mService.standby();
+ mIsActiveSourceLostPopupLaunched = false;
}
}
}
@@ -283,6 +289,7 @@
void dismissUiOnActiveSourceStatusRecovered() {
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI);
+ mIsActiveSourceLostPopupLaunched = false;
mService.sendBroadcastAsUser(intent);
}
@@ -516,6 +523,7 @@
)));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivityAsUser(intent, context.getUser());
+ mIsActiveSourceLostPopupLaunched = true;
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Unable to start HdmiCecActiveSourceLostActivity");
} finally {
@@ -733,6 +741,14 @@
return Constants.ADDR_TV;
}
+ boolean isActiveSourceLostPopupLaunched() {
+ return mIsActiveSourceLostPopupLaunched;
+ }
+
+ void setIsActiveSourceLostPopupLaunched(boolean isActiveSourceLostPopupLaunched) {
+ mIsActiveSourceLostPopupLaunched = isActiveSourceLostPopupLaunched;
+ }
+
@Override
@ServiceThreadOnly
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 6e98bff..35ef18b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -50,6 +50,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.hardware.display.DeviceProductInfo;
import android.hardware.display.DisplayManager;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
@@ -106,6 +107,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
+import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -725,6 +727,13 @@
}
mPowerStatusController.setPowerStatus(getInitialPowerStatus());
setProhibitMode(false);
+ if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
+ Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy "
+ + " mode.");
+ mHdmiCecConfig.setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+ HDMI_CEC_CONTROL_ENABLED);
+ setWasCecDisabledOnStandbyByLowEnergyMode(false);
+ }
mHdmiControlEnabled = mHdmiCecConfig.getIntValue(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
@@ -771,14 +780,6 @@
Slog.i(TAG, "Device does not support eARC.");
}
mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController);
- if (isTvDevice() && getWasCecDisabledOnStandbyByLowEnergyMode()) {
- Slog.w(TAG, "Re-enable CEC on boot-up since it was disabled due to low energy "
- + " mode.");
- getHdmiCecConfig().setIntValue(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
- HDMI_CEC_CONTROL_ENABLED);
- setWasCecDisabledOnStandbyByLowEnergyMode(false);
- setCecEnabled(HDMI_CEC_CONTROL_ENABLED);
- }
if (isCecControlEnabled()) {
initializeCec(INITIATED_BY_BOOT_UP);
} else {
@@ -1008,6 +1009,21 @@
}
}, mServiceThreadExecutor);
+ if (isPlaybackDevice()) {
+ mHdmiCecConfig.registerChangeListener(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
+ new HdmiCecConfig.SettingChangeListener() {
+ @Override
+ public void onChange(String setting) {
+ boolean goToStandbyOnActiveSourceLost =
+ mHdmiCecConfig.getStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST)
+ .equals(HdmiControlManager.POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_STANDBY_NOW);
+ writePowerStateChangeOnActiveSourceLostAtom(goToStandbyOnActiveSourceLost);
+ }
+ }, mServiceThreadExecutor);
+ }
+
mDeviceConfig.addOnPropertiesChangedListener(getContext().getMainExecutor(),
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -3190,6 +3206,7 @@
// Cancel an existing timer to send the device to sleep since OTP was triggered.
playback().mDelayedStandbyOnActiveSourceLostHandler
.removeCallbacksAndMessages(null);
+ playback().setIsActiveSourceLostPopupLaunched(false);
}
if (source == null) {
@@ -5228,6 +5245,34 @@
}
/**
+ * Writes a HdmiPowerStateChangeOnActiveSourceLostToggled atom representing a
+ * HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST setting change.
+ */
+ protected void writePowerStateChangeOnActiveSourceLostAtom(boolean isSettingEnabled) {
+ String manufacturerPnpId = "undefined";
+ int manufactureYear = -1;
+ int manufactureWeek = -1;
+ Display display = getContext().getDisplay();
+ if (display != null) {
+ DeviceProductInfo deviceProductInfo = display.getDeviceProductInfo();
+ manufacturerPnpId = deviceProductInfo.getManufacturerPnpId();
+ manufactureYear = deviceProductInfo.getManufactureYear();
+ }
+ int enumLogReason =
+ HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_UNKNOWN;
+ if (playback() != null) {
+ if (playback().isActiveSourceLostPopupLaunched()) {
+ enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_POP_UP;
+ } else {
+ enumLogReason = HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_SETTING;
+ }
+ }
+
+ getAtomWriter().powerStateChangeOnActiveSourceLostChanged(isSettingEnabled, enumLogReason,
+ manufacturerPnpId, manufactureYear, manufactureWeek);
+ }
+
+ /**
* Reads the property that checks if CEC was enabled by the user while in offline mode such that
* it won't be disabled when going to sleep by low energy mode.
*/
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 3b2305c..bf08563 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -81,6 +81,8 @@
(reason) -> updateTouchpadHardwareStateNotificationsEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE),
(reason) -> updateTouchpadRightClickZoneEnabled()),
+ Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_SYSTEM_GESTURES),
+ (reason) -> updateTouchpadSystemGesturesEnabled()),
Map.entry(Settings.System.getUriFor(Settings.System.SHOW_TOUCHES),
(reason) -> updateShowTouches()),
Map.entry(Settings.System.getUriFor(Settings.System.POINTER_LOCATION),
@@ -213,6 +215,10 @@
InputSettings.useTouchpadThreeFingerTapShortcut(mContext));
}
+ private void updateTouchpadSystemGesturesEnabled() {
+ mNative.setTouchpadSystemGesturesEnabled(InputSettings.useTouchpadSystemGestures(mContext));
+ }
+
private void updateShowTouches() {
mNative.setShowTouches(getBoolean(Settings.System.SHOW_TOUCHES, false));
}
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index 728e440..935f0ff 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -147,6 +147,8 @@
void setTouchpadThreeFingerTapShortcutEnabled(boolean enabled);
+ void setTouchpadSystemGesturesEnabled(boolean enabled);
+
void setShowTouches(boolean enabled);
void setNonInteractiveDisplays(int[] displayIds);
@@ -437,6 +439,9 @@
public native void setTouchpadThreeFingerTapShortcutEnabled(boolean enabled);
@Override
+ public native void setTouchpadSystemGesturesEnabled(boolean enabled);
+
+ @Override
public native void setShowTouches(boolean enabled);
@Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
new file mode 100644
index 0000000..53389ca
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -0,0 +1,108 @@
+/*
+ * 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.location.contexthub;
+
+import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubMessage;
+import android.hardware.contexthub.HubServiceInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.location.IContextHubTransactionCallback;
+
+/**
+ * A class that represents a broker for the endpoint registered by the client app. This class
+ * manages direct IContextHubEndpoint/IContextHubEndpointCallback API/callback calls.
+ *
+ * @hide
+ */
+public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub {
+ private static final String TAG = "ContextHubEndpointBroker";
+
+ /** The context of the service. */
+ private final Context mContext;
+
+ /** The proxy to talk to the Context Hub HAL. */
+ private final IContextHubWrapper mContextHubProxy;
+
+ /** The manager that registered this endpoint. */
+ private final ContextHubEndpointManager mEndpointManager;
+
+ /** Metadata about this endpoint (app-facing container). */
+ private final HubEndpointInfo mEndpointInfo;
+
+ /** Metadata about this endpoint (HAL-facing container). */
+ private final EndpointInfo mHalEndpointInfo;
+
+ /** The remote callback interface for this endpoint. */
+ private final IContextHubEndpointCallback mContextHubEndpointCallback;
+
+ /* package */ ContextHubEndpointBroker(
+ Context context,
+ IContextHubWrapper contextHubProxy,
+ ContextHubEndpointManager endpointManager,
+ EndpointInfo halEndpointInfo,
+ IContextHubEndpointCallback callback) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ mEndpointManager = endpointManager;
+ mEndpointInfo = new HubEndpointInfo(halEndpointInfo);
+ mHalEndpointInfo = halEndpointInfo;
+ mContextHubEndpointCallback = callback;
+ }
+
+ @Override
+ public HubEndpointInfo getAssignedHubEndpointInfo() {
+ return mEndpointInfo;
+ }
+
+ @Override
+ public int openSession(HubEndpointInfo destination, HubServiceInfo serviceInfo) {
+ // TODO(b/378487799): Implement this
+ return 0;
+ }
+
+ @Override
+ public void closeSession(int sessionId, int reason) {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void openSessionRequestComplete(int sessionId) {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void unregister() {
+ // TODO(b/378487799): Implement this
+
+ }
+
+ @Override
+ public void sendMessage(
+ int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
+ // TODO(b/381102453): Implement this
+ }
+
+ @Override
+ public void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode) {
+ // TODO(b/381102453): Implement this
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
new file mode 100644
index 0000000..54ce74f
--- /dev/null
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -0,0 +1,111 @@
+/*
+ * 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.location.contexthub;
+
+import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.IContextHubEndpoint;
+import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A class that manages registration/unregistration of clients and manages messages to/from clients.
+ *
+ * @hide
+ */
+/* package */ class ContextHubEndpointManager {
+ private static final String TAG = "ContextHubEndpointManager";
+
+ /** The hub ID of the Context Hub Service. */
+ private static final long SERVICE_HUB_ID = 0x416e64726f696400L;
+
+ /** The context of the service. */
+ private final Context mContext;
+
+ /** The proxy to talk to the Context Hub. */
+ private final IContextHubWrapper mContextHubProxy;
+
+ /** A map of endpoint IDs to brokers currently registered. */
+ private final Map<Long, ContextHubEndpointBroker> mEndpointMap = new ConcurrentHashMap<>();
+
+ /** Variables for managing endpoint ID creation */
+ private final Object mEndpointLock = new Object();
+
+ @GuardedBy("mEndpointLock")
+ private long mNextEndpointId = 0;
+
+ /* package */ ContextHubEndpointManager(Context context, IContextHubWrapper contextHubProxy) {
+ mContext = context;
+ mContextHubProxy = contextHubProxy;
+ }
+
+ /**
+ * Registers a new endpoint with the service.
+ *
+ * @param pendingEndpointInfo the object describing the endpoint being registered
+ * @param callback the callback interface of the endpoint to register
+ * @return the endpoint interface
+ * @throws IllegalStateException if max number of endpoints have already registered
+ */
+ /* package */ IContextHubEndpoint registerEndpoint(
+ HubEndpointInfo pendingEndpointInfo, IContextHubEndpointCallback callback)
+ throws RemoteException {
+ ContextHubEndpointBroker broker;
+ long endpointId = getNewEndpointId();
+ EndpointInfo halEndpointInfo =
+ ContextHubServiceUtil.createHalEndpointInfo(
+ pendingEndpointInfo, endpointId, SERVICE_HUB_ID);
+ try {
+ mContextHubProxy.registerEndpoint(halEndpointInfo);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException while calling HAL registerEndpoint", e);
+ throw e;
+ }
+ broker =
+ new ContextHubEndpointBroker(
+ mContext,
+ mContextHubProxy,
+ this /* endpointManager */,
+ halEndpointInfo,
+ callback);
+ mEndpointMap.put(endpointId, broker);
+
+ // TODO(b/378487799): Add death recipient
+
+ Log.d(TAG, "Registered endpoint with ID = " + endpointId);
+ return IContextHubEndpoint.Stub.asInterface(broker);
+ }
+
+ /**
+ * @return an available endpoint ID
+ */
+ private long getNewEndpointId() {
+ synchronized (mEndpointLock) {
+ if (mNextEndpointId == Long.MAX_VALUE) {
+ throw new IllegalStateException("Too many endpoints connected");
+ }
+ return mNextEndpointId++;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index d177d0e..0d926b9 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -155,6 +155,9 @@
// The manager for sending messages to/from clients
private ContextHubClientManager mClientManager;
+ // Manages endpoint and its sessions between apps and HAL
+ private ContextHubEndpointManager mEndpointManager;
+
// The default client for old API clients
private Map<Integer, IContextHubClient> mDefaultClientMap;
@@ -330,14 +333,17 @@
HubInfoRegistry registry;
try {
registry = new HubInfoRegistry(mContextHubWrapper);
+ mEndpointManager = new ContextHubEndpointManager(mContext, mContextHubWrapper);
Log.i(TAG, "Enabling generic offload API");
} catch (UnsupportedOperationException e) {
+ mEndpointManager = null;
registry = null;
Log.w(TAG, "Generic offload API not supported, disabling");
}
mHubInfoRegistry = registry;
} else {
mHubInfoRegistry = null;
+ mEndpointManager = null;
Log.i(TAG, "Disabling generic offload API");
}
@@ -790,8 +796,11 @@
HubEndpointInfo pendingHubEndpointInfo, IContextHubEndpointCallback callback)
throws RemoteException {
super.registerEndpoint_enforcePermission();
- // TODO(b/375487784): Implement this
- return null;
+ if (mEndpointManager == null) {
+ Log.e(TAG, "ContextHubService.registerEndpoint: endpoint manager failed to initialize");
+ throw new UnsupportedOperationException("Endpoint registration is not supported");
+ }
+ return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback);
}
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 33d2ff0..05be427 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -18,6 +18,9 @@
import android.Manifest;
import android.content.Context;
+import android.hardware.contexthub.EndpointInfo;
+import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubServiceInfo;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.HostEndPoint;
@@ -415,4 +418,51 @@
static String formatDateFromTimestamp(long timeStampInMs) {
return DATE_FORMATTER.format(Instant.ofEpochMilli(timeStampInMs));
}
+
+ /**
+ * Converts a context hub HAL EndpointInfo object based on the provided HubEndpointInfo.
+ *
+ * @param info the HubEndpointInfo object
+ * @return the equivalent EndpointInfo object
+ */
+ /* package */
+ static EndpointInfo convertHalEndpointInfo(HubEndpointInfo info) {
+ return createHalEndpointInfo(
+ info, info.getIdentifier().getEndpoint(), info.getIdentifier().getHub());
+ }
+
+ /**
+ * Creates a context hub HAL EndpointInfo object based on the provided HubEndpointInfo. As
+ * opposed to convertHalEndpointInfo, this method can be used to overwrite/specify the endpoint
+ * and hub ID.
+ *
+ * @param info the HubEndpointInfo object
+ * @param endpointId the endpoint ID of this object
+ * @param hubId the hub ID of this object
+ * @return the equivalent EndpointInfo object
+ */
+ /* package */
+ static EndpointInfo createHalEndpointInfo(HubEndpointInfo info, long endpointId, long hubId) {
+ EndpointInfo outputInfo = new EndpointInfo();
+ outputInfo.id = new android.hardware.contexthub.EndpointId();
+ outputInfo.id.id = endpointId;
+ outputInfo.id.hubId = hubId;
+ outputInfo.name = info.getName();
+ outputInfo.version = info.getVersion();
+ outputInfo.tag = info.getTag();
+ Collection<String> permissions = info.getRequiredPermissions();
+ outputInfo.requiredPermissions = permissions.toArray(new String[permissions.size()]);
+ Collection<HubServiceInfo> services = info.getServiceInfoCollection();
+ outputInfo.services = new android.hardware.contexthub.Service[services.size()];
+ int i = 0;
+ for (HubServiceInfo service : services) {
+ outputInfo.services[i] = new android.hardware.contexthub.Service();
+ outputInfo.services[i].format = service.getFormat();
+ outputInfo.services[i].serviceDescriptor = service.getServiceDescriptor();
+ outputInfo.services[i].majorVersion = service.getMajorVersion();
+ outputInfo.services[i].minorVersion = service.getMinorVersion();
+ i++;
+ }
+ return outputInfo;
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 9b729eb..14d75b0 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -239,6 +239,10 @@
public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
throws RemoteException {}
+ /** Registers the endpoint with the ContextHub HAL */
+ public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {}
+
/**
* @return True if this version of the Contexthub HAL supports Location setting notifications.
*/
@@ -671,6 +675,16 @@
hub.registerEndpointCallback(cb);
}
+ @Override
+ public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
+ throws RemoteException {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return;
+ }
+ hub.registerEndpoint(info);
+ }
+
public boolean supportsLocationSettingNotifications() {
return true;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 2d2d258..ab68ed3 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -221,11 +221,6 @@
}
}
- @NonNull
- private SystemMediaRoute2Provider getSystemProviderForUser(@NonNull UserHandler userHandler) {
- return userHandler.mSystemProvider;
- }
-
// Start of methods that implement MediaRouter2 operations.
@NonNull
@@ -251,7 +246,7 @@
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
if (hasSystemRoutingPermissions) {
MediaRoute2ProviderInfo providerInfo =
- getSystemProviderForUser(userRecord.mHandler).getProviderInfo();
+ userRecord.mHandler.getSystemProvider().getProviderInfo();
if (providerInfo != null) {
systemRoutes = providerInfo.getRoutes();
} else {
@@ -264,7 +259,7 @@
} else {
systemRoutes = new ArrayList<>();
systemRoutes.add(
- getSystemProviderForUser(userRecord.mHandler).getDefaultRoute());
+ userRecord.mHandler.getSystemProvider().getDefaultRoute());
}
}
return new ArrayList<>(systemRoutes);
@@ -856,11 +851,10 @@
if (setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
- return getSystemProviderForUser(userRecord.mHandler)
+ return userRecord.mHandler.getSystemProvider()
.generateDeviceRouteSelectedSessionInfo(targetPackageName);
} else {
- sessionInfos = getSystemProviderForUser(userRecord.mHandler)
- .getSessionInfos();
+ sessionInfos = userRecord.mHandler.getSystemProvider().getSessionInfos();
if (!sessionInfos.isEmpty()) {
// Return a copy of the current system session with no modification,
// except setting the client package name.
@@ -873,8 +867,7 @@
}
} else {
return new RoutingSessionInfo.Builder(
- getSystemProviderForUser(userRecord.mHandler)
- .getDefaultSessionInfo())
+ userRecord.mHandler.getSystemProvider().getDefaultSessionInfo())
.setClientPackageName(targetPackageName)
.build();
}
@@ -1144,6 +1137,7 @@
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
RouterRecord routerRecord =
new RouterRecord(
+ mContext,
userRecord,
router,
uid,
@@ -1382,7 +1376,7 @@
}
manager.mLastSessionCreationRequest = null;
} else {
- String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
+ String defaultRouteId = userHandler.getSystemProvider().getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -1470,7 +1464,7 @@
routerRecord.mPackageName, routerRecord.mRouterId, route.getId()));
UserHandler userHandler = routerRecord.mUserRecord.mHandler;
- String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
+ String defaultRouteId = userHandler.getSystemProvider().getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -2063,6 +2057,7 @@
}
final class RouterRecord implements IBinder.DeathRecipient {
+ public final Context mContext;
public final UserRecord mUserRecord;
public final String mPackageName;
public final List<Integer> mSelectRouteSequenceNumbers;
@@ -2081,6 +2076,7 @@
@Nullable public RouteListingPreference mRouteListingPreference;
RouterRecord(
+ Context context,
UserRecord userRecord,
IMediaRouter2 router,
int uid,
@@ -2090,6 +2086,7 @@
boolean hasModifyAudioRoutingPermission,
boolean hasMediaContentControlPermission,
boolean hasMediaRoutingControl) {
+ mContext = context;
mUserRecord = userRecord;
mPackageName = packageName;
mSelectRouteSequenceNumbers = new ArrayList<>();
@@ -2133,12 +2130,11 @@
notifyRoutesUpdated(routesToReport.values().stream().toList());
List<RoutingSessionInfo> sessionInfos =
- getSystemProviderForUser(mUserRecord.mHandler).getSessionInfos();
+ mUserRecord.mHandler.getSystemProvider().getSessionInfos();
RoutingSessionInfo systemSessionToReport =
newSystemRoutingPermissionValue && !sessionInfos.isEmpty()
? sessionInfos.get(0)
- : getSystemProviderForUser(mUserRecord.mHandler)
- .getDefaultSessionInfo();
+ : mUserRecord.mHandler.getSystemProvider().getDefaultSessionInfo();
notifySessionInfoChanged(systemSessionToReport);
}
}
@@ -2288,7 +2284,7 @@
if (route.isSystemRoute() && !hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system
// route info from them.
- route = getSystemProviderForUser(mUserRecord.mHandler).getDefaultRoute();
+ route = mUserRecord.mHandler.getSystemProvider().getDefaultRoute();
}
mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
} catch (RemoteException ex) {
@@ -2331,18 +2327,34 @@
}
/**
- * Returns a filtered copy of {@code routes} that contains only the routes that are {@link
- * MediaRoute2Info#isVisibleTo visible} to the router corresponding to this record.
+ * Returns a filtered copy of {@code routes} that contains only the routes that are visible
+ * to this RouterRecord.
*/
private List<MediaRoute2Info> getVisibleRoutes(@NonNull List<MediaRoute2Info> routes) {
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : routes) {
- if (route.isVisibleTo(mPackageName)) {
+ if (route.isVisibleTo(mPackageName) && hasPermissionsToSeeRoute(route)) {
filteredRoutes.add(route);
}
}
return filteredRoutes;
}
+
+ /**
+ * @return whether this RouterRecord has the required permissions to see the given route.
+ */
+ private boolean hasPermissionsToSeeRoute(MediaRoute2Info route) {
+ if (!Flags.enableRouteVisibilityControlApi()) {
+ return true;
+ }
+ for (String permission : route.getRequiredPermissions()) {
+ if (mContext.checkPermission(permission, mPid, mUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
}
final class ManagerRecord implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index af32907..65d0ab3 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -72,7 +72,7 @@
private final class BinderService extends IMediaQualityManager.Stub {
@Override
- public PictureProfile createPictureProfile(PictureProfile pp) {
+ public PictureProfile createPictureProfile(PictureProfile pp, int userId) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -88,17 +88,17 @@
}
@Override
- public void updatePictureProfile(String id, PictureProfile pp) {
+ public void updatePictureProfile(String id, PictureProfile pp, int userId) {
// TODO: implement
}
@Override
- public void removePictureProfile(String id) {
+ public void removePictureProfile(String id, int userId) {
// TODO: implement
}
@Override
- public PictureProfile getPictureProfile(int type, String name) {
+ public PictureProfile getPictureProfile(int type, String name, int userId) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
@@ -205,7 +205,7 @@
}
@Override
- public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
+ public List<PictureProfile> getPictureProfilesByPackage(String packageName, int userId) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
@@ -213,12 +213,12 @@
}
@Override
- public List<PictureProfile> getAvailablePictureProfiles() {
+ public List<PictureProfile> getAvailablePictureProfiles(int userId) {
return new ArrayList<>();
}
@Override
- public List<String> getPictureProfilePackageNames() {
+ public List<String> getPictureProfilePackageNames(int userId) {
String [] column = {BaseParameters.PARAMETER_NAME};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
@@ -250,12 +250,12 @@
}
@Override
- public PictureProfileHandle getPictureProfileHandle(String id) {
+ public PictureProfileHandle getPictureProfileHandle(String id, int userId) {
return null;
}
@Override
- public SoundProfile createSoundProfile(SoundProfile sp) {
+ public SoundProfile createSoundProfile(SoundProfile sp, int userId) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
@@ -269,12 +269,12 @@
}
@Override
- public void updateSoundProfile(String id, SoundProfile sp) {
+ public void updateSoundProfile(String id, SoundProfile pp, int userId) {
// TODO: implement
}
@Override
- public void removeSoundProfile(String id) {
+ public void removeSoundProfile(String id, int userId) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
String[] selectionArgs = {id};
@@ -282,7 +282,7 @@
}
@Override
- public SoundProfile getSoundProfile(int type, String id) {
+ public SoundProfile getSoundProfile(int type, String id, int userId) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
String selection = BaseParameters.PARAMETER_ID + " = ?";
@@ -314,7 +314,7 @@
}
@Override
- public List<SoundProfile> getSoundProfilesByPackage(String packageName) {
+ public List<SoundProfile> getSoundProfilesByPackage(String packageName, int userId) {
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getSoundProfilesBasedOnConditions(getAllSoundProfileColumns(), selection,
@@ -322,12 +322,12 @@
}
@Override
- public List<SoundProfile> getAvailableSoundProfiles() {
+ public List<SoundProfile> getAvailableSoundProfiles(int userId) {
return new ArrayList<>();
}
@Override
- public List<String> getSoundProfilePackageNames() {
+ public List<String> getSoundProfilePackageNames(int userId) {
String [] column = {BaseParameters.PARAMETER_NAME};
List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
null, null);
@@ -397,70 +397,70 @@
}
@Override
- public void setAmbientBacklightSettings(AmbientBacklightSettings settings) {
+ public void setAmbientBacklightSettings(AmbientBacklightSettings settings, int userId) {
}
@Override
- public void setAmbientBacklightEnabled(boolean enabled) {
+ public void setAmbientBacklightEnabled(boolean enabled, int userId) {
}
@Override
- public List<ParamCapability> getParamCapabilities(List<String> names) {
+ public List<ParamCapability> getParamCapabilities(List<String> names, int userId) {
return new ArrayList<>();
}
@Override
- public List<String> getPictureProfileAllowList() {
+ public List<String> getPictureProfileAllowList(int userId) {
return new ArrayList<>();
}
@Override
- public void setPictureProfileAllowList(List<String> packages) {
+ public void setPictureProfileAllowList(List<String> packages, int userId) {
}
@Override
- public List<String> getSoundProfileAllowList() {
+ public List<String> getSoundProfileAllowList(int userId) {
return new ArrayList<>();
}
@Override
- public void setSoundProfileAllowList(List<String> packages) {
+ public void setSoundProfileAllowList(List<String> packages, int userId) {
}
@Override
- public boolean isSupported() {
+ public boolean isSupported(int userId) {
return false;
}
@Override
- public void setAutoPictureQualityEnabled(boolean enabled) {
+ public void setAutoPictureQualityEnabled(boolean enabled, int userId) {
}
@Override
- public boolean isAutoPictureQualityEnabled() {
+ public boolean isAutoPictureQualityEnabled(int userId) {
return false;
}
@Override
- public void setSuperResolutionEnabled(boolean enabled) {
+ public void setSuperResolutionEnabled(boolean enabled, int userId) {
}
@Override
- public boolean isSuperResolutionEnabled() {
+ public boolean isSuperResolutionEnabled(int userId) {
return false;
}
@Override
- public void setAutoSoundQualityEnabled(boolean enabled) {
+ public void setAutoSoundQualityEnabled(boolean enabled, int userId) {
}
@Override
- public boolean isAutoSoundQualityEnabled() {
+ public boolean isAutoSoundQualityEnabled(int userId) {
return false;
}
@Override
- public boolean isAmbientBacklightEnabled() {
+ public boolean isAmbientBacklightEnabled(int userId) {
return false;
}
}
diff --git a/services/core/java/com/android/server/pm/InstallDependencyHelper.java b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
index 42b8dd7..13aab11 100644
--- a/services/core/java/com/android/server/pm/InstallDependencyHelper.java
+++ b/services/core/java/com/android/server/pm/InstallDependencyHelper.java
@@ -97,7 +97,7 @@
if (missing.isEmpty()) {
if (DEBUG) {
- Slog.i(TAG, "No missing dependency for " + pkg);
+ Slog.d(TAG, "No missing dependency for " + pkg);
}
// No need for dependency resolution. Move to installation directly.
callback.onResult(null);
@@ -110,7 +110,7 @@
}
IDependencyInstallerCallback serviceCallback =
- new DependencyInstallerCallbackCallOnce(handler, callback);
+ new DependencyInstallerCallbackCallOnce(handler, callback, userId);
boolean scheduleSuccess;
synchronized (mRemoteServiceLock) {
scheduleSuccess = mRemoteService.run(service -> {
@@ -125,7 +125,7 @@
void notifySessionComplete(int sessionId, boolean success) {
if (DEBUG) {
- Slog.i(TAG, "Session complete for " + sessionId + " result: " + success);
+ Slog.d(TAG, "Session complete for " + sessionId + " result: " + success);
}
synchronized (mTrackers) {
List<DependencyInstallTracker> completedTrackers = new ArrayList<>();
@@ -292,79 +292,130 @@
private final Handler mHandler;
private final CallOnceProxy mCallback;
+ private final int mUserId;
@GuardedBy("this")
- private boolean mCalled = false;
+ private boolean mDependencyInstallerCallbackInvoked = false;
- DependencyInstallerCallbackCallOnce(Handler handler, CallOnceProxy callback) {
+ DependencyInstallerCallbackCallOnce(Handler handler, CallOnceProxy callback, int userId) {
mHandler = handler;
mCallback = callback;
+ mUserId = userId;
}
- // TODO(b/372862145): Consider turning the binder call to two-way so that we can
- // throw IllegalArgumentException
@Override
public void onAllDependenciesResolved(int[] sessionIds) throws RemoteException {
synchronized (this) {
- if (mCalled) {
- return;
+ if (mDependencyInstallerCallbackInvoked) {
+ throw new IllegalStateException(
+ "Callback is being or has been already processed");
}
- mCalled = true;
+ mDependencyInstallerCallbackInvoked = true;
}
- ArraySet<Integer> set = new ArraySet<>();
- for (int i = 0; i < sessionIds.length; i++) {
- if (DEBUG) {
- Slog.i(TAG, "onAllDependenciesResolved called with " + sessionIds[i]);
- }
- set.add(sessionIds[i]);
+
+ if (DEBUG) {
+ Slog.d(TAG, "onAllDependenciesResolved started");
}
- DependencyInstallTracker tracker = new DependencyInstallTracker(mCallback, set);
+ // Before creating any tracker, validate the arguments
+ ArraySet<Integer> validSessionIds = validateSessionIds(sessionIds);
+
+ if (validSessionIds.isEmpty()) {
+ mCallback.onResult(null);
+ return;
+ }
+
+ // Create a tracker now if there are any pending sessions remaining.
+ DependencyInstallTracker tracker = new DependencyInstallTracker(
+ mCallback, validSessionIds);
synchronized (mTrackers) {
mTrackers.add(tracker);
}
- // In case any of the session ids have already been installed, check if they
- // are valid.
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.i(TAG, "onAllDependenciesResolved cleaning up invalid sessions");
+ // By the time the tracker was created, some of the sessions in validSessionIds
+ // could have finished. Avoid waiting for them indefinitely.
+ for (int sessionId : validSessionIds) {
+ SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId);
+
+ // Don't wait for sessions that finished already
+ if (sessionInfo == null) {
+ notifySessionComplete(sessionId, /*success=*/ true);
}
-
- for (int i = 0; i < sessionIds.length; i++) {
- int sessionId = sessionIds[i];
- SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId);
-
- // Continue waiting if session exists and hasn't passed or failed yet.
- if (sessionInfo != null && !sessionInfo.isSessionApplied
- && !sessionInfo.isSessionFailed) {
- continue;
- }
-
- if (DEBUG) {
- Slog.i(TAG, "onAllDependenciesResolved cleaning up finished"
- + " session: " + sessionId);
- }
-
- // If session info is null, we assume it to be success.
- // TODO(b/372862145): Check historical sessions to be more precise.
- boolean success = sessionInfo == null || sessionInfo.isSessionApplied;
-
- notifySessionComplete(sessionId, /*success=*/success);
- }
- });
+ }
}
@Override
public void onFailureToResolveAllDependencies() throws RemoteException {
synchronized (this) {
- if (mCalled) {
- return;
+ if (mDependencyInstallerCallbackInvoked) {
+ throw new IllegalStateException(
+ "Callback is being or has been already processed");
}
- onError(mCallback, "Failed to resolve all dependencies automatically");
- mCalled = true;
+ mDependencyInstallerCallbackInvoked = true;
}
+ onError(mCallback, "Failed to resolve all dependencies automatically");
+ }
+
+ private ArraySet<Integer> validateSessionIds(int[] sessionIds) {
+ // Before creating any tracker, validate the arguments
+ ArraySet<Integer> validSessionIds = new ArraySet<>();
+
+ List<SessionInfo> historicalSessions = null;
+ for (int i = 0; i < sessionIds.length; i++) {
+ int sessionId = sessionIds[i];
+ SessionInfo sessionInfo = mPackageInstallerService.getSessionInfo(sessionId);
+
+ // Continue waiting if session exists and hasn't passed or failed yet.
+ if (sessionInfo != null) {
+ if (sessionInfo.isSessionFailed) {
+ throwValidationError("Session already finished: " + sessionId);
+ }
+
+ // Wait for session to finish install if it's not already successful.
+ if (!sessionInfo.isSessionApplied) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAllDependenciesResolved pending session: " + sessionId);
+ }
+ validSessionIds.add(sessionId);
+ }
+
+ // An applied session found. No need to check historical session anymore.
+ continue;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "onAllDependenciesResolved cleaning up finished"
+ + " session: " + sessionId);
+ }
+
+ if (historicalSessions == null) {
+ historicalSessions = mPackageInstallerService.getHistoricalSessions(
+ mUserId).getList();
+ }
+
+ sessionInfo = historicalSessions.stream().filter(
+ s -> s.sessionId == sessionId).findFirst().orElse(null);
+
+ if (sessionInfo == null) {
+ throwValidationError("Failed to find session: " + sessionId);
+ }
+
+ // Historical session must have been successful, otherwise throw IAE.
+ if (!sessionInfo.isSessionApplied) {
+ throwValidationError("Session already finished: " + sessionId);
+ }
+ }
+
+ return validSessionIds;
+ }
+
+ private void throwValidationError(String msg) {
+ // Allow client to invoke callback again.
+ synchronized (this) {
+ mDependencyInstallerCallbackInvoked = false;
+ }
+ throw new IllegalArgumentException(msg);
}
}
@@ -377,6 +428,7 @@
// TODO(b/372862145): Determine and add support for rebooting while dependency is being resolved
private static class DependencyInstallTracker {
private final CallOnceProxy mCallback;
+ @GuardedBy("this")
private final ArraySet<Integer> mPendingSessionIds;
DependencyInstallTracker(CallOnceProxy callback, ArraySet<Integer> pendingSessionIds) {
@@ -399,7 +451,6 @@
if (!success) {
// If one of the dependency fails, the orig session would fail too.
onError(mCallback, "Failed to install all dependencies");
- // TODO(b/372862145): Abandon the rest of the pending sessions.
return false; // No point in tracking anymore
}
@@ -411,6 +462,5 @@
return true; // Keep on tracking
}
}
-
}
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index e5e2744..8168c54 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -229,7 +229,6 @@
private final SharedLibrariesImpl mSharedLibraries;
private final PackageManagerServiceInjector mInjector;
private final UpdateOwnershipHelper mUpdateOwnershipHelper;
- private final InstallDependencyHelper mInstallDependencyHelper;
private final Object mInternalLock = new Object();
@GuardedBy("mInternalLock")
@@ -240,8 +239,7 @@
AppDataHelper appDataHelper,
RemovePackageHelper removePackageHelper,
DeletePackageHelper deletePackageHelper,
- BroadcastHelper broadcastHelper,
- InstallDependencyHelper installDependencyHelper) {
+ BroadcastHelper broadcastHelper) {
mPm = pm;
mInjector = pm.mInjector;
mAppDataHelper = appDataHelper;
@@ -255,7 +253,6 @@
mPackageAbiHelper = pm.mInjector.getAbiHelper();
mSharedLibraries = pm.mInjector.getSharedLibrariesImpl();
mUpdateOwnershipHelper = pm.mInjector.getUpdateOwnershipHelper();
- mInstallDependencyHelper = installDependencyHelper;
}
/**
@@ -1367,10 +1364,6 @@
}
}
}
-
- for (InstallRequest request : requests) {
- mInstallDependencyHelper.notifySessionComplete(request.getSessionId(), success);
- }
}
@GuardedBy("mPm.mInstallLock")
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index c66a9e9..0930299 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.util.ArraySet;
import android.util.Pair;
@@ -28,8 +29,6 @@
import java.io.File;
-
-
// TODO: Move to .parsing sub-package
@VisibleForTesting
public interface PackageAbiHelper {
@@ -79,6 +78,23 @@
AndroidPackage scannedPackage);
/**
+ * Checks alignment of APK and native libraries for 16KB device
+ *
+ * @param pkg AndroidPackage for which alignment check is being done
+ * @param libraryRoot directory for libraries
+ * @param nativeLibraryRootRequiresIsa use isa
+ * @param cpuAbiOverride ABI override mentioned in package
+ * @return {ApplicationInfo.PageSizeAppCompat} if successful or error code
+ * which suggests undefined mode
+ */
+ @ApplicationInfo.PageSizeAppCompatFlags
+ int checkPackageAlignment(
+ AndroidPackage pkg,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String cpuAbiOverride);
+
+ /**
* The native library paths and related properties that should be set on a
* {@link ParsedPackage}.
*/
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 9db4d33..7229f07 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -29,6 +29,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.os.Build;
@@ -625,4 +626,22 @@
}
return adjustedAbi;
}
+
+ @Override
+ public int checkPackageAlignment(
+ AndroidPackage pkg,
+ String libraryRoot,
+ boolean nativeLibraryRootRequiresIsa,
+ String abiOverride) {
+ NativeLibraryHelper.Handle handle = null;
+ try {
+ handle = AndroidPackageUtils.createNativeLibraryHandle(pkg);
+ return NativeLibraryHelper.checkAlignmentForCompatMode(
+ handle, libraryRoot, nativeLibraryRootRequiresIsa, abiOverride);
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Failed to check alignment of package : "
+ + pkg.getPackageName());
+ return ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_ERROR;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 47b7850..ceb9314 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -2329,6 +2329,8 @@
}
}
+ mInstallDependencyHelper.notifySessionComplete(session.sessionId, success);
+
final File appIconFile = buildAppIconFile(session.sessionId);
if (appIconFile.exists()) {
appIconFile.delete();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 8f8802e..891d66a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -61,6 +61,7 @@
import android.Manifest;
import android.annotation.AnyThread;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -188,6 +189,7 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalServices;
+import com.android.server.art.ArtManagedInstallFileHelper;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.pkg.AndroidPackage;
@@ -852,7 +854,11 @@
if (file.getName().endsWith(REMOVE_MARKER_EXTENSION)) return false;
if (file.getName().endsWith(V4Signature.EXT)) return false;
if (isAppMetadata(file)) return false;
- if (DexMetadataHelper.isDexMetadataFile(file)) return false;
+ if (com.android.art.flags.Flags.artServiceV3()) {
+ if (ArtManagedInstallFileHelper.isArtManaged(file.getPath())) return false;
+ } else {
+ if (DexMetadataHelper.isDexMetadataFile(file)) return false;
+ }
if (VerityUtils.isFsveritySignatureFile(file)) return false;
if (ApkChecksums.isDigestOrDigestSignatureFile(file)) return false;
return true;
@@ -876,6 +882,13 @@
return true;
}
};
+ private static final FileFilter sArtManagedFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ return !file.isDirectory() && com.android.art.flags.Flags.artServiceV3()
+ && ArtManagedInstallFileHelper.isArtManaged(file.getPath());
+ }
+ };
static boolean isDataLoaderInstallation(SessionParams params) {
return params.dataLoaderParams != null;
@@ -1607,6 +1620,19 @@
}
@GuardedBy("mLock")
+ private List<String> getArtManagedFilePathsLocked() {
+ String[] names = getNamesLocked();
+ ArrayList<String> result = new ArrayList<>(names.length);
+ for (String name : names) {
+ File file = new File(stageDir, name);
+ if (sArtManagedFilter.accept(file)) {
+ result.add(file.getPath());
+ }
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
private void enableFsVerityToAddedApksWithIdsig() throws PackageManagerException {
try {
List<File> files = getAddedApksLocked();
@@ -3453,7 +3479,7 @@
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile, null);
+ resolveAndStageFileLocked(addedFile, targetFile, null, List.of() /* artManagedFilePaths */);
mResolvedBaseFile = targetFile;
// Populate package name of the apex session
@@ -3546,6 +3572,7 @@
TextUtils.formatSimple("Session: %d. No packages staged in %s", sessionId,
stageDir.getAbsolutePath()));
}
+ final List<String> artManagedFilePaths = getArtManagedFilePathsLocked();
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
@@ -3602,7 +3629,8 @@
final File targetFile = new File(stageDir, targetName);
if (!isArchivedInstallation()) {
final File sourceFile = new File(apk.getPath());
- resolveAndStageFileLocked(sourceFile, targetFile, apk.getSplitName());
+ resolveAndStageFileLocked(
+ sourceFile, targetFile, apk.getSplitName(), artManagedFilePaths);
}
// Base is coming from session
@@ -3763,7 +3791,7 @@
// Inherit base if not overridden.
if (mResolvedBaseFile == null) {
mResolvedBaseFile = new File(appInfo.getBaseCodePath());
- inheritFileLocked(mResolvedBaseFile);
+ inheritFileLocked(mResolvedBaseFile, artManagedFilePaths);
// Collect the requiredSplitTypes from base
CollectionUtils.addAll(requiredSplitTypes, existing.getBaseRequiredSplitTypes());
} else if ((params.installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
@@ -3782,7 +3810,7 @@
final boolean splitRemoved = removeSplitList.contains(splitName);
final boolean splitReplaced = stagedSplits.contains(splitName);
if (!splitReplaced && !splitRemoved) {
- inheritFileLocked(splitFile);
+ inheritFileLocked(splitFile, artManagedFilePaths);
// Collect the requiredSplitTypes and staged splitTypes from splits
CollectionUtils.addAll(requiredSplitTypes,
existing.getRequiredSplitTypes()[i]);
@@ -3968,6 +3996,23 @@
DexMetadataHelper.isFsVerityRequired());
}
+ @FlaggedApi(com.android.art.flags.Flags.FLAG_ART_SERVICE_V3)
+ @GuardedBy("mLock")
+ private void maybeStageArtManagedInstallFilesLocked(File origFile, File targetFile,
+ List<String> artManagedFilePaths) throws PackageManagerException {
+ for (String path : ArtManagedInstallFileHelper.filterPathsForApk(
+ artManagedFilePaths, origFile.getPath())) {
+ File artManagedFile = new File(path);
+ if (!FileUtils.isValidExtFilename(artManagedFile.getName())) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_INVALID_APK, "Invalid filename: " + artManagedFile);
+ }
+ File targetArtManagedFile = new File(
+ ArtManagedInstallFileHelper.getTargetPathForApk(path, targetFile.getPath()));
+ stageFileLocked(artManagedFile, targetArtManagedFile);
+ }
+ }
+
private IncrementalFileStorages getIncrementalFileStorages() {
synchronized (mLock) {
return mIncrementalFileStorages;
@@ -4065,8 +4110,8 @@
}
@GuardedBy("mLock")
- private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName)
- throws PackageManagerException {
+ private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName,
+ List<String> artManagedFilePaths) throws PackageManagerException {
stageFileLocked(origFile, targetFile);
// Stage APK's fs-verity signature if present.
@@ -4077,8 +4122,13 @@
&& VerityUtils.isFsVeritySupported()) {
maybeStageV4SignatureLocked(origFile, targetFile);
}
- // Stage dex metadata (.dm) and corresponding fs-verity signature if present.
- maybeStageDexMetadataLocked(origFile, targetFile);
+ // Stage ART managed install files (e.g., dex metadata (.dm)) and corresponding fs-verity
+ // signature if present.
+ if (com.android.art.flags.Flags.artServiceV3()) {
+ maybeStageArtManagedInstallFilesLocked(origFile, targetFile, artManagedFilePaths);
+ } else {
+ maybeStageDexMetadataLocked(origFile, targetFile);
+ }
// Stage checksums (.digests) if present.
maybeStageDigestsLocked(origFile, targetFile, splitName);
}
@@ -4103,7 +4153,7 @@
}
@GuardedBy("mLock")
- private void inheritFileLocked(File origFile) {
+ private void inheritFileLocked(File origFile, List<String> artManagedFilePaths) {
mResolvedInheritedFiles.add(origFile);
maybeInheritFsveritySignatureLocked(origFile);
@@ -4111,12 +4161,20 @@
maybeInheritV4SignatureLocked(origFile);
}
- // Inherit the dex metadata if present.
- final File dexMetadataFile =
- DexMetadataHelper.findDexMetadataForFile(origFile);
- if (dexMetadataFile != null) {
- mResolvedInheritedFiles.add(dexMetadataFile);
- maybeInheritFsveritySignatureLocked(dexMetadataFile);
+ // Inherit ART managed install files (e.g., dex metadata (.dm)) if present.
+ if (com.android.art.flags.Flags.artServiceV3()) {
+ for (String path : ArtManagedInstallFileHelper.filterPathsForApk(
+ artManagedFilePaths, origFile.getPath())) {
+ File artManagedFile = new File(path);
+ mResolvedInheritedFiles.add(artManagedFile);
+ maybeInheritFsveritySignatureLocked(artManagedFile);
+ }
+ } else {
+ final File dexMetadataFile = DexMetadataHelper.findDexMetadataForFile(origFile);
+ if (dexMetadataFile != null) {
+ mResolvedInheritedFiles.add(dexMetadataFile);
+ maybeInheritFsveritySignatureLocked(dexMetadataFile);
+ }
}
// Inherit the digests if present.
final File digestsFile = ApkChecksums.findDigestsForFile(origFile);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 040b194..ab26f02 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2118,8 +2118,7 @@
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mBroadcastHelper);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper,
- mDeletePackageHelper, mBroadcastHelper,
- injector.getPackageInstallerService().getInstallDependencyHelper());
+ mDeletePackageHelper, mBroadcastHelper);
mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
mInjector.getUserManagerInternal(), mDeletePackageHelper);
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 0802e9e..a317e16 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -52,6 +52,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
@@ -63,6 +64,8 @@
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -105,6 +108,9 @@
* Helper class that handles package scanning logic
*/
final class ScanPackageUtils {
+
+ public static final int PAGE_SIZE_16KB = 16384;
+
/**
* Just scans the package without any side effects.
*
@@ -418,6 +424,37 @@
+ " abiOverride=" + pkgSetting.getCpuAbiOverride());
}
+ boolean is16KbDevice = Os.sysconf(OsConstants._SC_PAGESIZE) == PAGE_SIZE_16KB;
+ if (Flags.appCompatOption16kb() && is16KbDevice) {
+ // Alignment checks are used decide whether this app should run in compat mode when
+ // nothing was specified in manifest. Manifest should always take precedence over
+ // something decided by platform.
+ if (parsedPackage.getPageSizeAppCompatFlags()
+ > ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) {
+ pkgSetting.setPageSizeAppCompatFlags(parsedPackage.getPageSizeAppCompatFlags());
+ } else {
+ // 16 KB is only support for 64 bit ABIs and for apps which are being installed
+ // Check alignment. System, Apex and Platform packages should be page-agnostic now
+ if ((Build.SUPPORTED_64_BIT_ABIS.length > 0)
+ && !isSystemApp
+ && !isApex
+ && !isPlatformPackage) {
+ int mode =
+ packageAbiHelper.checkPackageAlignment(
+ parsedPackage,
+ pkgSetting.getLegacyNativeLibraryPath(),
+ parsedPackage.isNativeLibraryRootRequiresIsa(),
+ pkgSetting.getCpuAbiOverride());
+ if (mode >= ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_UNDEFINED) {
+ pkgSetting.setPageSizeAppCompatFlags(mode);
+ } else {
+ Slog.e(TAG, "Error occurred while checking alignment of package : "
+ + parsedPackage.getPackageName());
+ }
+ }
+ }
+ }
+
if ((scanFlags & SCAN_BOOTING) == 0 && oldSharedUserSetting != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index aba15c8..0f6688f 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -23,6 +23,7 @@
import static com.android.server.power.hint.Flags.powerhintThreadCleanup;
import static com.android.server.power.hint.Flags.resetOnForkEnabled;
+import android.adpf.ISessionManager;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -193,6 +194,7 @@
private final Object mCpuHeadroomLock = new Object();
+ private ISessionManager mSessionManager;
// this cache tracks the expiration time of the items and performs cleanup on lookup
private static class HeadroomCache<K, V> {
@@ -818,6 +820,23 @@
for (int i = tokenMap.size() - 1; i >= 0; i--) {
// Will remove the session from tokenMap
ArraySet<AppHintSession> sessionSet = tokenMap.valueAt(i);
+ IntArray closedSessionsForSf = new IntArray();
+ // Batch the closure call to SF for all the sessions that die
+ for (int j = sessionSet.size() - 1; j >= 0; j--) {
+ AppHintSession session = sessionSet.valueAt(j);
+ if (session.isTrackedBySf()) {
+ // Mark it as untracked so we don't untrack again on close
+ session.setTrackedBySf(false);
+ closedSessionsForSf.add(session.getSessionId());
+ }
+ }
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(closedSessionsForSf.toArray());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to communicate with SessionManager");
+ }
+ }
for (int j = sessionSet.size() - 1; j >= 0; j--) {
sessionSet.valueAt(j).close();
}
@@ -1350,9 +1369,9 @@
}
}
- final long sessionId = config.id != -1 ? config.id : halSessionPtr;
+ final long sessionIdForTracing = config.id != -1 ? config.id : halSessionPtr;
logPerformanceHintSessionAtom(
- callingUid, sessionId, durationNanos, tids, tag);
+ callingUid, sessionIdForTracing, durationNanos, tids, tag);
synchronized (mSessionSnapshotMapLock) {
// Update session snapshot upon session creation
@@ -1362,8 +1381,12 @@
}
AppHintSession hs = null;
synchronized (mLock) {
+ Integer configId = null;
+ if (config.id != -1) {
+ configId = new Integer((int) config.id);
+ }
hs = new AppHintSession(callingUid, callingTgid, tag, tids,
- token, halSessionPtr, durationNanos);
+ token, halSessionPtr, durationNanos, configId);
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap =
mActiveSessions.get(callingUid);
if (tokenMap == null) {
@@ -1390,6 +1413,11 @@
}
}
+ if (creationConfig.layerTokens != null
+ && creationConfig.layerTokens.length > 0) {
+ hs.associateToLayers(creationConfig.layerTokens);
+ }
+
synchronized (mThreadsUsageObject) {
mThreadsUsageMap.computeIfAbsent(callingUid, k -> new ArraySet<>());
ArraySet<ThreadUsageTracker> threadsSet = mThreadsUsageMap.get(callingUid);
@@ -1566,6 +1594,15 @@
}
@Override
+ public void passSessionManagerBinder(IBinder sessionManager) {
+ // Ensure caller is internal
+ if (Process.myUid() != Binder.getCallingUid()) {
+ return;
+ }
+ mSessionManager = ISessionManager.Stub.asInterface(sessionManager);
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
@@ -1688,6 +1725,8 @@
protected boolean mHasBeenPowerEfficient;
protected boolean mHasBeenGraphicsPipeline;
protected boolean mShouldForcePause;
+ protected Integer mSessionId;
+ protected boolean mTrackedBySF;
enum SessionModes {
POWER_EFFICIENCY,
@@ -1696,7 +1735,7 @@
protected AppHintSession(
int uid, int pid, int sessionTag, int[] threadIds, IBinder token,
- long halSessionPtr, long durationNanos) {
+ long halSessionPtr, long durationNanos, Integer sessionId) {
mUid = uid;
mPid = pid;
mTag = sessionTag;
@@ -1710,6 +1749,8 @@
mHasBeenPowerEfficient = false;
mHasBeenGraphicsPipeline = false;
mShouldForcePause = false;
+ mSessionId = sessionId;
+ mTrackedBySF = false;
final boolean allowed = mUidObserver.isUidForeground(mUid);
updateHintAllowedByProcState(allowed);
try {
@@ -1799,6 +1840,19 @@
} catch (NoSuchElementException ignored) {
Slogf.d(TAG, "Death link does not exist for session with UID " + mUid);
}
+ if (mTrackedBySF) {
+ if (mSessionManager != null) {
+ try {
+ mSessionManager.trackedSessionsDied(new int[]{mSessionId});
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ mTrackedBySF = false;
+ } else {
+ Slog.e(TAG, "SessionManager is null but there are tracked sessions");
+ }
+ }
}
synchronized (mLock) {
ArrayMap<IBinder, ArraySet<AppHintSession>> tokenMap = mActiveSessions.get(mUid);
@@ -1875,6 +1929,24 @@
}
}
+ @Override
+ public void associateToLayers(IBinder[] layerTokens) {
+ synchronized (this) {
+ if (mSessionManager != null && mSessionId != null && layerTokens != null) {
+ // Sf only untracks a session when it dies
+ if (layerTokens.length > 0) {
+ mTrackedBySF = true;
+ }
+ try {
+ mSessionManager.associateSessionToLayers(mSessionId, mUid, layerTokens);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Could not communicate with SessionManager", e);
+ }
+ }
+ }
+ }
+
public void setThreads(@NonNull int[] tids) {
setThreadsInternal(tids, true);
}
@@ -2124,10 +2196,27 @@
return mUid;
}
+ public boolean isTrackedBySf() {
+ synchronized (this) {
+ return mTrackedBySF;
+ }
+ }
+
+ public void setTrackedBySf(boolean tracked) {
+ synchronized (this) {
+ mTrackedBySF = tracked;
+ }
+ }
+
+
public int getTag() {
return mTag;
}
+ public Integer getSessionId() {
+ return mSessionId;
+ }
+
public long getTargetDurationNs() {
synchronized (this) {
return mTargetDurationNanos;
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 028ac57..6f18107 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -409,11 +409,10 @@
@Override
public long getWakelockDurationMillis() {
synchronized (BatteryStatsImpl.this) {
- long rawRealtimeUs = mClock.uptimeMillis() * 1000;
- long batteryUptimeUs = getBatteryUptime(rawRealtimeUs);
- long screenOnTimeUs = getScreenOnTime(rawRealtimeUs,
+ long batteryUptimeUs = getBatteryUptime(mClock.uptimeMillis() * 1000);
+ long screenOnTimeUs = getScreenOnTime(mClock.elapsedRealtime() * 1000,
BatteryStats.STATS_SINCE_CHARGED);
- return (batteryUptimeUs - screenOnTimeUs) / 1000;
+ return Math.max(0, (batteryUptimeUs - screenOnTimeUs) / 1000);
}
}
@@ -437,8 +436,9 @@
}
}
- if (wakeLockTimeUs != 0) {
- callback.onUidWakelockDuration(u.getUid(), wakeLockTimeUs / 1000);
+ long wakelockTimeMs = wakeLockTimeUs / 1000;
+ if (wakelockTimeMs != 0) {
+ callback.onUidWakelockDuration(u.getUid(), wakelockTimeMs);
}
}
}
diff --git a/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
index e36c994..e3e4e1b 100644
--- a/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WakelockPowerStatsCollector.java
@@ -108,14 +108,16 @@
mWakelockDurationRetriever.retrieveUidWakelockDuration((uid, durationMs) -> {
if (!mFirstCollection) {
- long[] uidStats = mPowerStats.uidStats.get(uid);
- if (uidStats == null) {
- uidStats = new long[mDescriptor.uidStatsArrayLength];
- mPowerStats.uidStats.put(uid, uidStats);
- }
+ long diffMs = Math.max(0, durationMs - mLastUidWakelockDurations.get(uid));
+ if (diffMs != 0) {
+ long[] uidStats = mPowerStats.uidStats.get(uid);
+ if (uidStats == null) {
+ uidStats = new long[mDescriptor.uidStatsArrayLength];
+ mPowerStats.uidStats.put(uid, uidStats);
+ }
- mStatsLayout.setUidUsageDuration(uidStats,
- Math.max(0, durationMs - mLastUidWakelockDurations.get(uid)));
+ mStatsLayout.setUidUsageDuration(uidStats, diffMs);
+ }
}
mLastUidWakelockDurations.put(uid, durationMs);
});
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 2cc08c3..7fb5708 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -80,13 +80,25 @@
private void initFeatures(boolean enabled) {
if (android.security.Flags.aapmFeatureDisableInstallUnknownSources()) {
+ try {
mHooks.add(new DisallowInstallUnknownSourcesAdvancedProtectionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize DisallowInstallUnknownSources", e);
+ }
}
if (android.security.Flags.aapmFeatureMemoryTaggingExtension()) {
+ try {
mHooks.add(new MemoryTaggingExtensionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize MemoryTaggingExtension", e);
+ }
}
if (android.security.Flags.aapmFeatureDisableCellular2g()) {
+ try {
mHooks.add(new DisallowCellular2GAdvancedProtectionHook(mContext, enabled));
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to initialize DisallowCellular2g", e);
+ }
}
}
@@ -199,7 +211,7 @@
}
void sendCallbackAdded(boolean enabled, IAdvancedProtectionCallback callback) {
- Message.obtain(mHandler, MODE_CHANGED, /*enabled*/ enabled ? 1 : 0, /*unused*/ -1,
+ Message.obtain(mHandler, CALLBACK_ADDED, /*enabled*/ enabled ? 1 : 0, /*unused*/ -1,
/*callback*/ callback)
.sendToTarget();
}
@@ -278,8 +290,13 @@
for (int i = 0; i < mHooks.size(); i++) {
AdvancedProtectionHook feature = mHooks.get(i);
- if (feature.isAvailable()) {
- feature.onAdvancedProtectionChanged(enabled);
+ try {
+ if (feature.isAvailable()) {
+ feature.onAdvancedProtectionChanged(enabled);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to call hook for feature "
+ + feature.getFeature().getId(), e);
}
}
synchronized (mCallbacks) {
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
index a2933d9..bb523d6 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowInstallUnknownSourcesAdvancedProtectionHook.java
@@ -118,6 +118,5 @@
+ " getAppOpPermissionPackages() threw the following exception: " + e);
}
}
- // TODO(b/369361373): Update dialog strings.
}
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 38bc026..e191ff2 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -119,14 +119,14 @@
* If resource holder retains ownership of the resource in a challenge scenario then value is
* true.
*/
- private boolean mResourceHolderRetain;
+ private boolean mResourceOwnershipRetention;
private ClientProfile(Builder builder) {
this.mId = builder.mId;
this.mTvInputSessionId = builder.mTvInputSessionId;
this.mUseCase = builder.mUseCase;
this.mProcessId = builder.mProcessId;
- this.mResourceHolderRetain = builder.mResourceHolderRetain;
+ this.mResourceOwnershipRetention = builder.mResourceOwnershipRetention;
}
public int getId() {
@@ -149,8 +149,8 @@
* Returns true when the resource holder retains ownership of the resource in a challenge
* scenario.
*/
- public boolean shouldResourceHolderRetain() {
- return mResourceHolderRetain;
+ public boolean resourceOwnershipRetentionEnabled() {
+ return mResourceOwnershipRetention;
}
/**
@@ -199,12 +199,12 @@
* scenario, when both resource holder and resource challenger have same processId and same
* priority.
*
- * @param resourceHolderRetain Set to true to allow the resource holder to retain ownership, or
- * false (or resourceHolderRetain not set at all) to allow the resource challenger to
- * acquire the resource. If not explicitly set, resourceHolderRetain is set to false.
+ * @param enabled Set to {@code true} to allow the resource holder to retain ownership,
+ * or false to allow the resource challenger to acquire the resource.
+ * If not explicitly set, enabled is set to {@code false}.
*/
- public void setResourceHolderRetain(boolean resourceHolderRetain) {
- mResourceHolderRetain = resourceHolderRetain;
+ public void setResourceOwnershipRetention(boolean enabled) {
+ mResourceOwnershipRetention = enabled;
}
/**
@@ -389,7 +389,7 @@
private String mTvInputSessionId;
private int mUseCase;
private int mProcessId;
- private boolean mResourceHolderRetain = false;
+ private boolean mResourceOwnershipRetention = false;
Builder(int id) {
this.mId = id;
@@ -428,12 +428,12 @@
/**
* Builder for {@link ClientProfile}.
*
- * @param resourceHolderRetain the determining factor for resource ownership during
- * challenger scenario. The default behavior favors the resource challenger and grants
- * them ownership of the resource if resourceHolderRetain is not explicitly set to true.
+ * @param enabled the determining factor for resource ownership during challenger scenario.
+ * The default behavior favors the resource challenger and grants them ownership of
+ * the resource if resourceOwnershipRetention is not explicitly set to true.
*/
- public Builder resourceHolderRetain(boolean resourceHolderRetain) {
- this.mResourceHolderRetain = resourceHolderRetain;
+ public Builder resourceOwnershipRetention(boolean enabled) {
+ this.mResourceOwnershipRetention = enabled;
return this;
}
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 5ae8c11..bb192c0 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -231,10 +231,10 @@
}
@Override
- public void setResourceHolderRetain(int clientId, boolean resourceHolderRetain) {
- enforceTrmAccessPermission("setResourceHolderRetain");
+ public void setResourceOwnershipRetention(int clientId, boolean enabled) {
+ enforceTrmAccessPermission("setResourceOwnershipRetention");
synchronized (mLock) {
- getClientProfile(clientId).setResourceHolderRetain(resourceHolderRetain);
+ getClientProfile(clientId).setResourceOwnershipRetention(enabled);
}
}
@@ -1079,7 +1079,8 @@
|| ((requestClient.getPriority() == currentLowestPriority)
&& isRequestFromSameProcess
&& !(setResourceHolderRetain()
- && requestClient.shouldResourceHolderRetain())))) {
+ && requestClient
+ .resourceOwnershipRetentionEnabled())))) {
frontendHandle[0] = inUseLowestPriorityFrontend.getHandle();
reclaimOwnerId[0] = inUseLowestPriorityFrontend.getOwnerClientId();
return true;
@@ -1265,7 +1266,8 @@
|| ((requestClient.getPriority() == currentLowestPriority)
&& isRequestFromSameProcess
&& !(setResourceHolderRetain()
- && requestClient.shouldResourceHolderRetain())))) {
+ && requestClient
+ .resourceOwnershipRetentionEnabled())))) {
lnbHandle[0] = inUseLowestPriorityLnb.getHandle();
reclaimOwnerId[0] = inUseLowestPriorityLnb.getOwnerClientId();
return true;
@@ -1352,7 +1354,8 @@
|| ((requestClient.getPriority() == currentLowestPriority)
&& isRequestFromSameProcess
&& !(setResourceHolderRetain()
- && requestClient.shouldResourceHolderRetain())))) {
+ && requestClient
+ .resourceOwnershipRetentionEnabled())))) {
casSessionHandle[0] = cas.getHandle();
reclaimOwnerId[0] = lowestPriorityOwnerId;
return true;
@@ -1439,7 +1442,8 @@
|| ((requestClient.getPriority() == currentLowestPriority)
&& isRequestFromSameProcess
&& !(setResourceHolderRetain()
- && requestClient.shouldResourceHolderRetain())))) {
+ && requestClient
+ .resourceOwnershipRetentionEnabled())))) {
ciCamHandle[0] = ciCam.getHandle();
reclaimOwnerId[0] = lowestPriorityOwnerId;
return true;
@@ -1677,7 +1681,8 @@
|| ((requestClient.getPriority() == currentLowestPriority)
&& isRequestFromSameProcess
&& !(setResourceHolderRetain()
- && requestClient.shouldResourceHolderRetain())))) {
+ && requestClient
+ .resourceOwnershipRetentionEnabled())))) {
demuxHandle[0] = inUseLowestPriorityDemux.getHandle();
reclaimOwnerId[0] = inUseLowestPriorityDemux.getOwnerClientId();
return true;
diff --git a/services/core/java/com/android/server/vcn/Android.bp b/services/core/java/com/android/server/vcn/Android.bp
deleted file mode 100644
index ab5da3e..0000000
--- a/services/core/java/com/android/server/vcn/Android.bp
+++ /dev/null
@@ -1,13 +0,0 @@
-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: "framework-vcn-util-sources",
- srcs: ["util/**/*.java"],
-}
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 3392d03..154897e 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -27,6 +27,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.net.vcn.VcnManager;
+import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -45,7 +46,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 1fba297..95acb10 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -38,6 +38,7 @@
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
+import android.net.vcn.util.LogUtils;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Message;
@@ -54,7 +55,6 @@
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
-import com.android.server.vcn.util.LogUtils;
import java.util.Arrays;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 2325f35..9ccf040 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -30,10 +30,10 @@
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_CONFIG_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_INTERNAL_ERROR;
import static android.net.vcn.VcnManager.VCN_ERROR_CODE_NETWORK_ERROR;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.VcnManagementService.VDBG;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -78,6 +78,9 @@
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnTransportInfo;
+import android.net.vcn.util.LogUtils;
+import android.net.vcn.util.MtuUtils;
+import android.net.vcn.util.OneWayBoolean;
import android.net.wifi.WifiInfo;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -103,9 +106,6 @@
import com.android.server.vcn.routeselection.UnderlyingNetworkController;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
-import com.android.server.vcn.util.LogUtils;
-import com.android.server.vcn.util.MtuUtils;
-import com.android.server.vcn.util.OneWayBoolean;
import java.io.IOException;
import java.net.Inet4Address;
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 16ab51e..e6a1ff9 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -16,8 +16,9 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index 0d4c373..86cee55 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -16,8 +16,9 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
import static com.android.server.VcnManagementService.LOCAL_LOG;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index d32e5cc..79c4116 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -23,9 +23,9 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.VcnManagementService.LOCAL_LOG;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index 3eeeece..f7a564a 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -19,12 +19,12 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN;
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -39,6 +39,7 @@
import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
+import android.net.vcn.util.LogUtils;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -54,7 +55,6 @@
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
-import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
index 08be11e..30f4ed1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java
@@ -16,8 +16,9 @@
package com.android.server.vcn.routeselection;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
import static com.android.server.VcnManagementService.LOCAL_LOG;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 92ce251..1798661 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -34,6 +34,7 @@
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewZygote;
@@ -246,6 +247,11 @@
}
@Override
+ public boolean isCompatibleImplementationPackage(PackageInfo packageInfo) {
+ return WebViewFactoryProvider.isCompatibleImplementationPackage(packageInfo);
+ }
+
+ @Override
public List<UserPackage> getPackageInfoForProviderAllUsers(WebViewProviderInfo configInfo) {
return UserPackage.getPackageInfosAllUsers(mContext, configInfo.packageName, PACKAGE_FLAGS);
}
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 6710554..d9e1a3a 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -47,6 +47,9 @@
boolean systemIsDebuggable();
PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException;
+ /** Check if the given package is a compatible WebView implementation for the OS. */
+ boolean isCompatibleImplementationPackage(PackageInfo packageInfo);
+
/**
* Get the PackageInfos of all users for the package represented by {@param configInfo}.
* @return an array of UserPackages for a certain package, each UserPackage being belonging to a
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index a5a02cd..9e8dc26 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -27,6 +27,7 @@
import android.util.Slog;
import android.webkit.UserPackage;
import android.webkit.WebViewFactory;
+import android.webkit.WebViewFactoryProvider;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -79,7 +80,7 @@
private static final long NS_PER_MS = 1000000;
private static final int VALIDITY_OK = 0;
- private static final int VALIDITY_INCORRECT_SDK_VERSION = 1;
+ private static final int VALIDITY_OS_INCOMPATIBLE = 1;
private static final int VALIDITY_INCORRECT_VERSION_CODE = 2;
private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
@@ -587,9 +588,9 @@
}
private int validityResult(WebViewProviderInfo configInfo, PackageInfo packageInfo) {
- // Ensure the provider targets this framework release (or a later one).
- if (!UserPackage.hasCorrectTargetSdkVersion(packageInfo)) {
- return VALIDITY_INCORRECT_SDK_VERSION;
+ // Ensure the provider is compatible with this framework release.
+ if (!mSystemInterface.isCompatibleImplementationPackage(packageInfo)) {
+ return VALIDITY_OS_INCOMPATIBLE;
}
if (!versionCodeGE(packageInfo.getLongVersionCode(), getMinimumVersionCode())
&& !mSystemInterface.systemIsDebuggable()) {
@@ -712,7 +713,8 @@
}
pw.println(
TextUtils.formatSimple(
- " Minimum targetSdkVersion: %d", UserPackage.MINIMUM_SUPPORTED_SDK));
+ " %s",
+ WebViewFactoryProvider.describeCompatibleImplementationPackage()));
pw.println(
TextUtils.formatSimple(
" Minimum WebView version code: %d", mMinimumVersionCode));
@@ -786,8 +788,8 @@
private static String getInvalidityReason(int invalidityReason) {
switch (invalidityReason) {
- case VALIDITY_INCORRECT_SDK_VERSION:
- return "SDK version too low";
+ case VALIDITY_OS_INCOMPATIBLE:
+ return "Not compatible with this OS version";
case VALIDITY_INCORRECT_VERSION_CODE:
return "Version code too low";
case VALIDITY_INCORRECT_SIGNATURE:
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ef33ffe..9956d85 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3238,10 +3238,12 @@
}
final boolean compatEnabled = isLargeScreen && Flags.universalResizableByDefault()
&& appInfo.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
- if (!compatEnabled && !wms.mConstants.mIgnoreActivityOrientationRequest) {
- return false;
- }
- if (wms.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(appInfo.packageName)) {
+ final boolean configEnabled = (isLargeScreen
+ ? wms.mConstants.mIgnoreActivityOrientationRequestLargeScreen
+ : wms.mConstants.mIgnoreActivityOrientationRequestSmallScreen)
+ && !wms.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(
+ appInfo.packageName);
+ if (!compatEnabled && !configEnabled) {
return false;
}
if (forActivity) {
@@ -4611,6 +4613,17 @@
} else if (fromOrientation != requestedOrientation) {
return false;
}
+
+ // If another activity above the activity which has starting window, allows to steal the
+ // starting window if the above activity isn't drawn.
+ if (task.getChildCount() >= 3
+ && fromActivity.mStartingData.mAssociatedTask == null) {
+ final ActivityRecord aboveFrom = task.getActivityAbove(fromActivity);
+ if (aboveFrom != null && aboveFrom != this && !aboveFrom.mReportedDrawn) {
+ return false;
+ }
+ }
+
// In this case, the starting icon has already been displayed, so start
// letting windows get shown immediately without any more transitions.
if (fromActivity.mVisible) {
@@ -4634,6 +4647,16 @@
tStartingWindow.mToken = this;
tStartingWindow.mActivityRecord = this;
+ if (mStartingData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
+ // The removal of starting window should wait for window drawn of current
+ // activity.
+ final WindowState mainWin = findMainWindow(false /* includeStartingApp */);
+ if (mainWin == null || !mainWin.isDrawn()) {
+ mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE;
+ mStartingData.mPrepareRemoveAnimation = false;
+ }
+ }
+
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"Removing starting %s from %s", tStartingWindow, fromActivity);
mTransitionController.collect(tStartingWindow);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 2e2ca14..90d3834 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1838,7 +1838,7 @@
remoteTransition, null /* displayChange */);
} else if (result == START_SUCCESS && mStartActivity.isState(RESUMED)) {
// Do nothing if the activity is started and is resumed directly.
- } else if (isStarted) {
+ } else if (isStarted && (mBalCode != BAL_BLOCK || mDoResume)) {
// Make the collecting transition wait until this request is ready.
if (transition != null) {
transition.setReady(started, false);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 70a8f56..a077a0b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2054,6 +2054,8 @@
break;
}
}
+ long timeRemaining = endTime - System.currentTimeMillis();
+ mWindowManager.mSnapshotController.mTaskSnapshotController.waitFlush(timeRemaining);
// Force checkReadyForSleep to complete.
checkReadyForSleepLocked(false /* allowDelay */);
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index ebb50db..a418324 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -173,7 +173,8 @@
appCompatTaskInfo.topActivityLetterboxHeight = bounds.height();
appCompatTaskInfo.topActivityLetterboxAppWidth = appBounds.width();
appCompatTaskInfo.topActivityLetterboxAppHeight = appBounds.height();
-
+ // TODO(b/379824541) Remove duplicate information.
+ appCompatTaskInfo.topActivityLetterboxBounds = bounds;
// We need to consider if letterboxed or pillarboxed.
// TODO(b/336807329) Encapsulate reachability logic
appCompatTaskInfo.setLetterboxDoubleTapEnabled(reachabilityOverrides
@@ -282,6 +283,7 @@
info.topActivityLetterboxHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxAppHeight = TaskInfo.PROPERTY_VALUE_UNSET;
info.topActivityLetterboxAppWidth = TaskInfo.PROPERTY_VALUE_UNSET;
+ info.topActivityLetterboxBounds = null;
info.cameraCompatTaskInfo.freeformCameraCompatMode =
CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
info.clearTopActivityFlags();
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index fcaab2c..601b17c 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -32,6 +32,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.Flags;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.os.Build;
@@ -40,6 +41,8 @@
import android.os.Message;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -76,6 +79,7 @@
public static final int FLAG_HIDE_COMPILE_SDK = 0x02;
public static final int FLAG_HIDE_DEPRECATED_SDK = 0x04;
public static final int FLAG_HIDE_DEPRECATED_ABI = 0x08;
+ public static final int FLAG_HIDE_PAGE_SIZE_MISMATCH = 0x10;
/**
* Map of package flags for each user.
@@ -101,6 +105,7 @@
private SparseArray<UnsupportedCompileSdkDialog> mUnsupportedCompileSdkDialogs;
private SparseArray<DeprecatedTargetSdkVersionDialog> mDeprecatedTargetSdkVersionDialogs;
private SparseArray<DeprecatedAbiDialog> mDeprecatedAbiDialogs;
+ private SparseArray<PageSizeMismatchDialog> mPageSizeMismatchDialogs;
/** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
private final ArraySet<ComponentName> mAlwaysShowUnsupportedCompileSdkWarningActivities =
@@ -250,6 +255,19 @@
}
}
+ public void showPageSizeMismatchDialogIfNeeded(ActivityRecord r) {
+ // Don't show dialog if the app compat is enabled using property
+ final boolean appCompatEnabled = SystemProperties.getBoolean(
+ "bionic.linker.16kb.app_compat.enabled", false);
+ if (appCompatEnabled) {
+ return;
+ }
+ boolean is16KbDevice = Os.sysconf(OsConstants._SC_PAGESIZE) == 16384;
+ if (is16KbDevice) {
+ mUiHandler.showPageSizeMismatchDialog(r);
+ }
+ }
+
/**
* Called when an activity is being started.
*
@@ -260,6 +278,9 @@
showUnsupportedDisplaySizeDialogIfNeeded(r);
showDeprecatedTargetDialogIfNeeded(r);
showDeprecatedAbiDialogIfNeeded(r);
+ if (Flags.appCompatOption16kb()) {
+ showPageSizeMismatchDialogIfNeeded(r);
+ }
}
/**
@@ -457,6 +478,41 @@
}
}
+ @UiThread
+ private void showPageSizeMismatchDialogUiThread(@NonNull ActivityRecord ar) {
+ String warning =
+ mAtm.mContext
+ .getPackageManager()
+ .getPageSizeCompatWarningMessage(ar.info.packageName);
+ if (warning == null) {
+ return;
+ }
+
+ final int userId = getUserIdForActivity(ar);
+ PageSizeMismatchDialog pageSizeMismatchDialog;
+ if (mPageSizeMismatchDialogs != null) {
+ pageSizeMismatchDialog = mPageSizeMismatchDialogs.get(userId);
+ if (pageSizeMismatchDialog != null) {
+ pageSizeMismatchDialog.dismiss();
+ mPageSizeMismatchDialogs.remove(userId);
+ }
+ }
+ if (!hasPackageFlag(userId, ar.packageName, FLAG_HIDE_PAGE_SIZE_MISMATCH)) {
+ pageSizeMismatchDialog =
+ new PageSizeMismatchDialog(
+ AppWarnings.this,
+ getUiContextForActivity(ar),
+ ar.info.applicationInfo,
+ userId,
+ warning);
+ pageSizeMismatchDialog.show();
+ if (mPageSizeMismatchDialogs == null) {
+ mPageSizeMismatchDialogs = new SparseArray<>();
+ }
+ mPageSizeMismatchDialogs.put(userId, pageSizeMismatchDialog);
+ }
+ }
+
/**
* Dismisses all warnings for the given package.
* <p>
@@ -510,6 +566,16 @@
mDeprecatedAbiDialogs.remove(userId);
}
}
+
+ // Hides the "page size app compat" dialog if necessary.
+ if (mPageSizeMismatchDialogs != null) {
+ PageSizeMismatchDialog pageSizeMismatchDialog = mPageSizeMismatchDialogs.get(userId);
+ if (pageSizeMismatchDialog != null
+ && (name == null || name.equals(pageSizeMismatchDialog.mPackageName))) {
+ pageSizeMismatchDialog.dismiss();
+ mPageSizeMismatchDialogs.remove(userId);
+ }
+ }
}
/**
@@ -649,6 +715,7 @@
private static final int MSG_HIDE_DIALOGS_FOR_PACKAGE = 4;
private static final int MSG_SHOW_DEPRECATED_TARGET_SDK_DIALOG = 5;
private static final int MSG_SHOW_DEPRECATED_ABI_DIALOG = 6;
+ private static final int MSG_SHOW_PAGE_SIZE_APP_MISMATCH_DIALOG = 7;
public UiHandler(Looper looper) {
super(looper, null, true);
@@ -681,6 +748,10 @@
final ActivityRecord ar = (ActivityRecord) msg.obj;
showDeprecatedAbiDialogUiThread(ar);
} break;
+ case MSG_SHOW_PAGE_SIZE_APP_MISMATCH_DIALOG: {
+ final ActivityRecord ar = (ActivityRecord) msg.obj;
+ showPageSizeMismatchDialogUiThread(ar);
+ } break;
}
}
@@ -712,6 +783,11 @@
public void hideDialogsForPackage(String name, int userId) {
obtainMessage(MSG_HIDE_DIALOGS_FOR_PACKAGE, userId, 0, name).sendToTarget();
}
+
+ public void showPageSizeMismatchDialog(ActivityRecord r) {
+ removeMessages(MSG_SHOW_PAGE_SIZE_APP_MISMATCH_DIALOG);
+ obtainMessage(MSG_SHOW_PAGE_SIZE_APP_MISMATCH_DIALOG, r).sendToTarget();
+ }
}
static class BaseDialog {
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index f0a6e9e..dd1af0a 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -653,7 +653,9 @@
// by drawing the rotated content before applying projection transaction of display.
// And it will fade in after the display transition is finished.
if (mTransitionOp == OP_APP_SWITCH && !mIsStartTransactionCommitted
- && canBeAsync(w.mToken) && !mDisplayContent.hasFixedRotationTransientLaunch()) {
+ && canBeAsync(w.mToken) && !mDisplayContent.hasFixedRotationTransientLaunch()
+ && !mService.mAtmService.mBackNavigationController.hasFixedRotationAnimation(
+ mDisplayContent)) {
hideImmediately(w.mToken, Operation.ACTION_FADE);
if (DEBUG) Slog.d(TAG, "Hide on finishDrawing " + w.mToken.getTopChild());
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 4ed8b09..3968b52 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -622,6 +622,15 @@
}
}
+ boolean hasFixedRotationAnimation(@NonNull DisplayContent displayContent) {
+ if (!mAnimationHandler.mComposed) {
+ return false;
+ }
+ final ActivityRecord openActivity = mAnimationHandler.mOpenActivities[0];
+ return displayContent == openActivity.mDisplayContent
+ && displayContent.isFixedRotationLaunchingApp(openActivity);
+ }
+
private boolean isWaitBackTransition() {
// Ignore mWaitTransition while flag is enabled.
return mAnimationHandler.mComposed && (Flags.migratePredictiveBackTransition()
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9a33df1..fc08a91 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -7061,12 +7061,15 @@
}
@Override
- public void setImeInputTargetRequestedVisibility(boolean visible) {
+ public void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
if (android.view.inputmethod.Flags.refactorInsetsController()) {
// TODO(b/353463205) we won't have the statsToken in all cases, but should still log
try {
- mRemoteInsetsController.setImeInputTargetRequestedVisibility(visible);
+ mRemoteInsetsController.setImeInputTargetRequestedVisibility(visible,
+ statsToken);
} catch (RemoteException e) {
+ // TODO(b/353463205) fail statsToken
Slog.w(TAG, "Failed to deliver setImeInputTargetRequestedVisibility", e);
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 48e1c06..59a9e85 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -40,6 +40,7 @@
import android.view.inputmethod.ImeTracker;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.ProtoLog;
import java.io.PrintWriter;
@@ -316,15 +317,21 @@
if (Flags.refactorInsetsController() && target != null) {
InsetsControlTarget imeControlTarget = getControlTarget();
if (target != imeControlTarget) {
- // TODO(b/353463205): start new request here?
+ // TODO(b/353463205): check if fromUser=false is correct here
+ boolean imeVisible = target.isRequestedVisible(WindowInsets.Type.ime());
+ ImeTracker.Token statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_HIDE,
+ ImeTracker.ORIGIN_SERVER,
+ imeVisible ? SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED
+ : SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
+ false /* fromUser */);
reportImeInputTargetStateToControlTarget(target, imeControlTarget,
- null /* statsToken */);
+ statsToken);
}
}
}
private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget,
- InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken) {
+ InsetsControlTarget controlTarget, @NonNull ImeTracker.Token statsToken) {
// In case of the multi window mode, update the requestedVisibleTypes from
// the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
// Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
@@ -333,7 +340,7 @@
if (controlTarget != null) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
- controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+ controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
} else if (imeInsetsTarget instanceof InsetsControlTarget) {
// In case of a virtual display that cannot show the IME, the
// controlTarget will be null here, as no controlTarget was set yet. In
@@ -345,7 +352,7 @@
if (controlTarget != imeInsetsTarget) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
- controlTarget.setImeInputTargetRequestedVisibility(imeVisible);
+ controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
// not all virtual displays have an ImeInsetsSourceProvider, so it is not
// guaranteed that the IME will be started when the control target reports its
// requested visibility back. Thus, invoking the listener here.
@@ -390,9 +397,9 @@
WindowToken imeToken = mWindowContainer.asWindowState() != null
? mWindowContainer.asWindowState().mToken : null;
final var rotationController = mDisplayContent.getAsyncRotationController();
- if ((rotationController != null && rotationController.isTargetToken(imeToken))
- || (imeToken != null && imeToken.isSelfAnimating(
- 0 /* flags */, SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM))) {
+ if ((rotationController != null && rotationController.isTargetToken(imeToken)) || (
+ imeToken != null && imeToken.isSelfAnimating(0 /* flags */,
+ SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM))) {
// Skip reporting IME drawn state when the control target is in fixed
// rotation, AsyncRotationController will report after the animation finished.
return;
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index 7043aacf..cee4967 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
@@ -90,8 +91,10 @@
/**
* @param visible the requested visibility for the IME, used for
* {@link com.android.server.wm.DisplayContent.RemoteInsetsControlTarget}
+ * @param statsToken the token tracking the current IME request
*/
- default void setImeInputTargetRequestedVisibility(boolean visible) {
+ default void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
}
/** Returns {@code target.getWindow()}, or null if {@code target} is {@code null}. */
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index c7667b4..98521d3 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -20,6 +20,7 @@
jiamingliu@google.com
pdwilliams@google.com
charlesccchen@google.com
+marziana@google.com
# Files related to background activity launches
per-file Background*Start* = set noparent
diff --git a/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java b/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java
new file mode 100644
index 0000000..8c50913
--- /dev/null
+++ b/services/core/java/com/android/server/wm/PageSizeMismatchDialog.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.text.Html.FROM_HTML_MODE_COMPACT;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.text.Html;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+
+/**
+ * Show warning dialog when
+ * - Uncompressed libs inside apk are not aligned to page size
+ * - ELF Load segments are not page size aligned
+ * This dialog will be shown everytime when app is launched. Apps can choose to override
+ * by setting compat mode pageSizeCompat="enabled" in manifest or "disabled" to opt out.
+ * Both cases will skip the PageSizeMismatchDialog.
+ *
+ */
+class PageSizeMismatchDialog extends AppWarnings.BaseDialog {
+ PageSizeMismatchDialog(
+ final AppWarnings manager,
+ Context context,
+ ApplicationInfo appInfo,
+ int userId,
+ String warning) {
+ super(manager, context, appInfo.packageName, userId);
+
+ final PackageManager pm = context.getPackageManager();
+ final CharSequence label =
+ appInfo.loadSafeLabel(
+ pm,
+ PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+ PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE
+ | PackageItemInfo.SAFE_LABEL_FLAG_TRIM);
+
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(context)
+ .setPositiveButton(
+ R.string.ok,
+ (dialog, which) -> {/* Do nothing */})
+ .setMessage(Html.fromHtml(warning, FROM_HTML_MODE_COMPACT))
+ .setTitle(label);
+
+ mDialog = builder.create();
+ mDialog.create();
+
+ final Window window = mDialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_PHONE);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c89feb4..46312af 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2853,11 +2853,9 @@
}
void prepareForShutdown() {
+ mWindowManager.mSnapshotController.mTaskSnapshotController.prepareShutdown();
for (int i = 0; i < getChildCount(); i++) {
- final int displayId = getChildAt(i).mDisplayId;
- mWindowManager.mSnapshotController.mTaskSnapshotController
- .snapshotForShutdown(displayId);
- createSleepToken("shutdown", displayId);
+ createSleepToken("shutdown", getChildAt(i).mDisplayId);
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index bd8e8f4..8b63ecf7 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -103,12 +103,42 @@
}
/**
- * Write out everything in the queue because of shutdown.
+ * Prepare to enqueue all visible task snapshots because of shutdown.
*/
- void shutdown() {
+ void prepareShutdown() {
synchronized (mLock) {
mShutdown = true;
- mLock.notifyAll();
+ }
+ }
+
+ private boolean isQueueEmpty() {
+ synchronized (mLock) {
+ return mWriteQueue.isEmpty() || mQueueIdling || mPaused;
+ }
+ }
+
+ void waitFlush(long timeout) {
+ if (timeout <= 0) {
+ return;
+ }
+ final long endTime = System.currentTimeMillis() + timeout;
+ while (true) {
+ if (!isQueueEmpty()) {
+ long timeRemaining = endTime - System.currentTimeMillis();
+ if (timeRemaining > 0) {
+ synchronized (mLock) {
+ try {
+ mLock.wait(timeRemaining);
+ } catch (InterruptedException e) {
+ }
+ }
+ } else {
+ Slog.w(TAG, "Snapshot Persist Queue flush timed out");
+ break;
+ }
+ } else {
+ break;
+ }
}
}
@@ -139,7 +169,9 @@
mWriteQueue.addLast(item);
}
item.onQueuedLocked();
- ensureStoreQueueDepthLocked();
+ if (!mShutdown) {
+ ensureStoreQueueDepthLocked();
+ }
if (!mPaused) {
mLock.notifyAll();
}
@@ -213,6 +245,9 @@
if (!writeQueueEmpty && !mPaused) {
continue;
}
+ if (mShutdown && writeQueueEmpty) {
+ mLock.notifyAll();
+ }
try {
mQueueIdling = writeQueueEmpty;
mLock.wait();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 9fe3f756..c130931 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -309,23 +309,31 @@
/**
* Record task snapshots before shutdown.
*/
- void snapshotForShutdown(int displayId) {
+ void prepareShutdown() {
if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
- final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
- if (displayContent == null) {
+ // Make write items run in a batch.
+ mPersister.mSnapshotPersistQueue.setPaused(true);
+ mPersister.mSnapshotPersistQueue.prepareShutdown();
+ for (int i = 0; i < mService.mRoot.getChildCount(); i++) {
+ mService.mRoot.getChildAt(i).forAllLeafTasks(task -> {
+ if (task.isVisible() && !task.isActivityTypeHome()) {
+ final TaskSnapshot snapshot = captureSnapshot(task);
+ if (snapshot != null) {
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ }
+ }
+ }, true /* traverseTopToBottom */);
+ }
+ mPersister.mSnapshotPersistQueue.setPaused(false);
+ }
+
+ void waitFlush(long timeout) {
+ if (!com.android.window.flags.Flags.recordTaskSnapshotsBeforeShutdown()) {
return;
}
- displayContent.forAllLeafTasks(task -> {
- if (task.isVisible() && !task.isActivityTypeHome()) {
- final TaskSnapshot snapshot = captureSnapshot(task);
- if (snapshot != null) {
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- }
- }
- }, true /* traverseTopToBottom */);
- mPersister.mSnapshotPersistQueue.shutdown();
+ mPersister.mSnapshotPersistQueue.waitFlush(timeout);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 31ca24c..3ad9b62 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -36,7 +36,15 @@
*/
final class WindowManagerConstants {
- /** The orientation of activity will be always "unspecified" except for game apps. */
+ /**
+ * The orientation of activity will be always "unspecified" except for game apps.
+ * <p>Possible values:
+ * <ul>
+ * <li>false: applies to no apps (default)</li>
+ * <li>true: applies to all apps</li>
+ * <li>large: applies to all apps but only on large screens</li>
+ * </ul>
+ */
private static final String KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST =
"ignore_activity_orientation_request";
@@ -69,7 +77,8 @@
boolean mSystemGestureExcludedByPreQStickyImmersive;
/** @see #KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST */
- boolean mIgnoreActivityOrientationRequest;
+ boolean mIgnoreActivityOrientationRequestLargeScreen;
+ boolean mIgnoreActivityOrientationRequestSmallScreen;
/** @see #KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST */
private ArraySet<String> mOptOutIgnoreActivityOrientationRequestPackages;
@@ -177,9 +186,12 @@
}
private void updateIgnoreActivityOrientationRequest() {
- mIgnoreActivityOrientationRequest = mDeviceConfig.getBoolean(
+ final String value = mDeviceConfig.getProperty(
DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST, false);
+ KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
+ mIgnoreActivityOrientationRequestSmallScreen = Boolean.parseBoolean(value);
+ mIgnoreActivityOrientationRequestLargeScreen = mIgnoreActivityOrientationRequestSmallScreen
+ || ("large".equals(value));
}
private void updateOptOutIgnoreActivityOrientationRequestList() {
@@ -196,8 +208,7 @@
}
boolean isPackageOptOutIgnoreActivityOrientationRequest(String packageName) {
- return mIgnoreActivityOrientationRequest
- && mOptOutIgnoreActivityOrientationRequestPackages != null
+ return mOptOutIgnoreActivityOrientationRequestPackages != null
&& mOptOutIgnoreActivityOrientationRequestPackages.contains(packageName);
}
@@ -211,7 +222,8 @@
pw.print(" "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
pw.print(" "); pw.print(KEY_IGNORE_ACTIVITY_ORIENTATION_REQUEST);
- pw.print("="); pw.println(mIgnoreActivityOrientationRequest);
+ pw.print("="); pw.println(mIgnoreActivityOrientationRequestSmallScreen ? "true"
+ : mIgnoreActivityOrientationRequestLargeScreen ? "large" : "false");
if (mOptOutIgnoreActivityOrientationRequestPackages != null) {
pw.print(" "); pw.print(KEY_OPT_OUT_IGNORE_ACTIVITY_ORIENTATION_REQUEST_LIST);
pw.print("="); pw.println(mOptOutIgnoreActivityOrientationRequestPackages);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a0c0b98..7a53ccf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4668,21 +4668,25 @@
@EnforcePermission(android.Manifest.permission.MANAGE_APP_TOKENS)
@Override
- public void updateDisplayWindowRequestedVisibleTypes(
- int displayId, @InsetsType int requestedVisibleTypes) {
+ public void updateDisplayWindowRequestedVisibleTypes(int displayId,
+ @InsetsType int requestedVisibleTypes, @Nullable ImeTracker.Token statsToken) {
updateDisplayWindowRequestedVisibleTypes_enforcePermission();
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
final DisplayContent dc = mRoot.getDisplayContent(displayId);
if (dc == null || dc.mRemoteInsetsControlTarget == null) {
+ ImeTracker.forLogging().onFailed(statsToken,
+ ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
return;
}
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_WM_UPDATE_DISPLAY_WINDOW_REQUESTED_VISIBLE_TYPES);
dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes);
// TODO(b/353463205) the statsToken shouldn't be null as it is used later in the
- // IME provider. Check if we have to create a new request here
+ // IME provider. Check if we have to create a new request here, if null.
dc.getInsetsStateController().onRequestedVisibleTypesChanged(
- dc.mRemoteInsetsControlTarget, null /* statsToken */);
+ dc.mRemoteInsetsControlTarget, statsToken);
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index c42aa37..ddff24d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1851,14 +1851,13 @@
}
private int applyKeyguardState(@NonNull WindowContainerTransaction.HierarchyOp hop) {
- int effects = TRANSACT_EFFECTS_NONE;
+ int effects = TRANSACT_EFFECTS_LIFECYCLE;
final KeyguardState keyguardState = hop.getKeyguardState();
if (keyguardState != null) {
- int displayId = keyguardState.getDisplayId();
boolean keyguardShowing = keyguardState.getKeyguardShowing();
boolean aodShowing = keyguardState.getAodShowing();
- mService.mKeyguardController.setKeyguardShown(displayId, keyguardShowing, aodShowing);
+ mService.setLockScreenShown(keyguardShowing, aodShowing);
}
return effects;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index dece612..9430194 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -348,6 +348,7 @@
void setShouldNotifyTouchpadHardwareState(bool enabled);
void setTouchpadRightClickZoneEnabled(bool enabled);
void setTouchpadThreeFingerTapShortcutEnabled(bool enabled);
+ void setTouchpadSystemGesturesEnabled(bool enabled);
void setInputDeviceEnabled(uint32_t deviceId, bool enabled);
void setShowTouches(bool enabled);
void setNonInteractiveDisplays(const std::set<ui::LogicalDisplayId>& displayIds);
@@ -518,6 +519,9 @@
// middle-click.
bool touchpadThreeFingerTapShortcutEnabled{false};
+ // True to enable system gestures (three- and four-finger swipes) on touchpads.
+ bool touchpadSystemGesturesEnabled{true};
+
// True if a pointer icon should be shown for stylus pointers.
bool stylusPointerIconEnabled{false};
@@ -790,6 +794,7 @@
outConfig->touchpadRightClickZoneEnabled = mLocked.touchpadRightClickZoneEnabled;
outConfig->touchpadThreeFingerTapShortcutEnabled =
mLocked.touchpadThreeFingerTapShortcutEnabled;
+ outConfig->touchpadSystemGesturesEnabled = mLocked.touchpadSystemGesturesEnabled;
outConfig->disabledDevices = mLocked.disabledInputDevices;
@@ -1528,6 +1533,22 @@
InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
}
+void NativeInputManager::setTouchpadSystemGesturesEnabled(bool enabled) {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+
+ if (mLocked.touchpadSystemGesturesEnabled == enabled) {
+ return;
+ }
+
+ ALOGI("Setting touchpad system gestures enabled to %s.", toString(enabled));
+ mLocked.touchpadSystemGesturesEnabled = enabled;
+ } // release lock
+
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::Change::TOUCHPAD_SETTINGS);
+}
+
void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) {
bool refresh = false;
@@ -2481,6 +2502,13 @@
getNativeInputManager(env, nativeImplObj)->setTouchpadThreeFingerTapShortcutEnabled(enabled);
}
+static void nativeSetTouchpadSystemGesturesEnabled(JNIEnv* env, jobject nativeImplObj,
+ jboolean enabled) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+
+ im->setTouchpadSystemGesturesEnabled(enabled);
+}
+
static void nativeSetShowTouches(JNIEnv* env, jobject nativeImplObj, jboolean enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3169,6 +3197,7 @@
{"setTouchpadRightClickZoneEnabled", "(Z)V", (void*)nativeSetTouchpadRightClickZoneEnabled},
{"setTouchpadThreeFingerTapShortcutEnabled", "(Z)V",
(void*)nativeSetTouchpadThreeFingerTapShortcutEnabled},
+ {"setTouchpadSystemGesturesEnabled", "(Z)V", (void*)nativeSetTouchpadSystemGesturesEnabled},
{"setShowTouches", "(Z)V", (void*)nativeSetShowTouches},
{"setNonInteractiveDisplays", "([I)V", (void*)nativeSetNonInteractiveDisplays},
{"reloadCalibration", "()V", (void*)nativeReloadCalibration},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6f0d26a..a48fa5e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -117,6 +117,7 @@
import static android.app.admin.DeviceAdminInfo.USES_POLICY_WIPE_DATA;
import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
+import static android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY;
import static android.app.admin.DevicePolicyManager.ACTION_CHECK_POLICY_COMPLIANCE;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_FINANCING_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
@@ -578,6 +579,7 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -591,6 +593,7 @@
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Implementation of the device policy APIs.
@@ -16234,6 +16237,10 @@
result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
admin.info.getComponent());
return result;
+ } else if (android.security.Flags.aapmApi()) {
+ result = new Bundle();
+ result.putInt(Intent.EXTRA_USER_ID, userId);
+ return result;
}
return null;
} finally {
@@ -16243,6 +16250,54 @@
return null;
}
+ private android.app.admin.EnforcingAdmin getEnforcingAdminInternal(int userId,
+ String identifier) {
+ Objects.requireNonNull(identifier);
+
+ Set<EnforcingAdmin> admins = getEnforcingAdminsForIdentifier(userId, identifier);
+ if (admins.isEmpty()) {
+ return null;
+ }
+
+ final EnforcingAdmin admin;
+ if (admins.size() == 1) {
+ admin = admins.iterator().next();
+ } else {
+ Optional<EnforcingAdmin> dpc = admins.stream()
+ .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
+ admin = dpc.orElseGet(() -> admins.stream().findFirst().get());
+ }
+ return admin == null ? null : admin.getParcelableAdmin();
+ }
+
+ private <V> Set<EnforcingAdmin> getEnforcingAdminsForIdentifier(int userId, String identifier) {
+ // For POLICY_SUSPEND_PACKAGES return PO or DO to keep the behavior same as
+ // before the bug fix for b/192245204.
+ if (DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(identifier)) {
+ EnforcingAdmin admin = getProfileOrDeviceOwnerEnforcingAdmin(userId);
+ return admin == null ? Collections.emptySet() : Collections.singleton(admin);
+ }
+
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ final PolicyDefinition<V> policyDefinition = getPolicyDefinitionForIdentifier(
+ identifier);
+ V value = mDevicePolicyEngine.getResolvedPolicy(policyDefinition, userId);
+ if (value == null) {
+ return Collections.emptySet();
+ }
+ return Stream.concat(mDevicePolicyEngine.getGlobalPoliciesSetByAdmins(policyDefinition)
+ .entrySet().stream(),
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(policyDefinition,
+ userId).entrySet().stream())
+ .filter(entry -> value.equals(entry.getValue().getValue()))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
/**
* @param restriction The restriction enforced by admin. It could be any user restriction or
* policy like {@link DevicePolicyManager#POLICY_DISABLE_CAMERA},
@@ -16257,20 +16312,9 @@
// before the bug fix for b/192245204.
if (DevicePolicyManager.POLICY_SUSPEND_PACKAGES.equals(
restriction)) {
- ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
- if (profileOwner != null) {
- EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
- profileOwner, userId);
+ EnforcingAdmin admin = getProfileOrDeviceOwnerEnforcingAdmin(userId);
+ if (admin != null) {
admins.add(admin.getParcelableAdmin());
- return admins;
- }
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null && deviceOwner.first == userId) {
- EnforcingAdmin admin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
- deviceOwner.second, deviceOwner.first);
- admins.add(admin.getParcelableAdmin());
- return admins;
}
} else {
long ident = mInjector.binderClearCallingIdentity();
@@ -16319,6 +16363,29 @@
}
}
+ private static <V> PolicyDefinition<V> getPolicyDefinitionForIdentifier(
+ @NonNull String identifier) {
+ Objects.requireNonNull(identifier);
+ if (Flags.setMtePolicyCoexistence() && MEMORY_TAGGING_POLICY.equals(identifier)) {
+ return (PolicyDefinition<V>) PolicyDefinition.MEMORY_TAGGING;
+ } else {
+ return (PolicyDefinition<V>) getPolicyDefinitionForRestriction(identifier);
+ }
+ }
+
+ private EnforcingAdmin getProfileOrDeviceOwnerEnforcingAdmin(int userId) {
+ ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
+ if (profileOwner != null) {
+ return EnforcingAdmin.createEnterpriseEnforcingAdmin(profileOwner, userId);
+ }
+ final Pair<Integer, ComponentName> deviceOwner = mOwners.getDeviceOwnerUserIdAndComponent();
+ if (deviceOwner != null && deviceOwner.first == userId) {
+ return EnforcingAdmin.createEnterpriseEnforcingAdmin(deviceOwner.second,
+ deviceOwner.first);
+ }
+ return null;
+ }
+
private static String userRestrictionSourceToString(@UserRestrictionSource int source) {
return DebugUtils.flagsToString(UserManager.class, "RESTRICTION_", source);
}
@@ -16336,6 +16403,12 @@
}
@Override
+ public android.app.admin.EnforcingAdmin getEnforcingAdmin(int userId, String identifier) {
+ Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
+ return getEnforcingAdminInternal(userId, identifier);
+ }
+
+ @Override
public List<android.app.admin.EnforcingAdmin> getEnforcingAdminsForRestriction(
int userId, String restriction) {
Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()));
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
index 1fd628a..5a0b079 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnforcingAdmin.java
@@ -321,8 +321,10 @@
authority = DpcAuthority.DPC_AUTHORITY;
} else if (mAuthorities.contains(DEVICE_ADMIN_AUTHORITY)) {
authority = DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
+ } else if (mIsSystemAuthority) {
+ // For now, System Authority returns UnknownAuthority.
+ authority = new UnknownAuthority(mSystemEntity);
} else {
- // For now, System Authority returns UNKNOWN_AUTHORITY.
authority = UnknownAuthority.UNKNOWN_AUTHORITY;
}
return new android.app.admin.EnforcingAdmin(
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 112414e..fde6ce2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.media.tv.flags.Flags.mediaQualityFw;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -2616,9 +2617,11 @@
t.traceEnd();
}
- t.traceBegin("StartMediaQuality");
- mSystemServiceManager.startService(MediaQualityService.class);
- t.traceEnd();
+ if (mediaQualityFw() && isTv) {
+ t.traceBegin("StartMediaQuality");
+ mSystemServiceManager.startService(MediaQualityService.class);
+ t.traceEnd();
+ }
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
t.traceBegin("StartMediaResourceMonitor");
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
index 114fe32..9457205 100644
--- a/services/manifest_services.xml
+++ b/services/manifest_services.xml
@@ -4,4 +4,9 @@
<version>2</version>
<fqname>IAltitudeService/default</fqname>
</hal>
+ <hal format="aidl">
+ <name>android.frameworks.devicestate</name>
+ <version>1</version>
+ <fqname>IDeviceStateService/default</fqname>
+ </hal>
</manifest>
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index aeb1ba9..de16b7e 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -865,7 +865,8 @@
runTask(task);
verify(mBackupManagerService).setWorkSource(null);
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)),
+ eq(false));
}
@Test
@@ -1101,7 +1102,8 @@
runTask(task);
verify(agentMock.agentBinder).fail(any());
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(PACKAGE_1)),
+ eq(false));
}
@Test
@@ -1423,7 +1425,7 @@
assertCleansUpFiles(mTransport, PM_PACKAGE);
// We don't unbind PM
verify(mBackupAgentConnectionManager, never()).unbindAgent(
- argThat(applicationInfo(PM_PACKAGE)));
+ argThat(applicationInfo(PM_PACKAGE)), eq(false));
}
@Test
@@ -1445,7 +1447,7 @@
runTask(task);
verify(mBackupAgentConnectionManager, never()).unbindAgent(
- argThat(applicationInfo(PM_PACKAGE)));
+ argThat(applicationInfo(PM_PACKAGE)), eq(false));
}
@Test
@@ -1651,7 +1653,7 @@
InOrder inOrder = inOrder(agentMock.agent, mBackupAgentConnectionManager);
inOrder.verify(agentMock.agent).onQuotaExceeded(anyLong(), eq(1234L));
inOrder.verify(mBackupAgentConnectionManager).unbindAgent(
- argThat(applicationInfo(PACKAGE_1)));
+ argThat(applicationInfo(PACKAGE_1)), eq(false));
}
@Test
@@ -2983,7 +2985,8 @@
private void assertCleansUpFilesAndAgent(TransportData transport, PackageData packageData) {
assertCleansUpFiles(transport, packageData);
- verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(packageData)));
+ verify(mBackupAgentConnectionManager).unbindAgent(argThat(applicationInfo(packageData)),
+ eq(false));
}
private void assertCleansUpFiles(TransportData transport, PackageData packageData) {
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index 53a25dd..55bd8df 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -19,11 +19,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
import android.app.supervision.SupervisionManagerInternal;
+import android.app.supervision.flags.Flags;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -33,6 +38,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -42,6 +48,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/** Service for handling system supervision. */
public class SupervisionService extends ISupervisionManager.Stub {
@@ -65,12 +72,10 @@
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
}
- void syncStateWithDevicePolicyManager(TargetUser user) {
- if (user.isPreCreated()) return;
-
+ private void syncStateWithDevicePolicyManager(@UserIdInt int userId) {
// Ensure that supervision is enabled when supervision app is the profile owner.
- if (android.app.admin.flags.Flags.enableSupervisionServiceSync() && isProfileOwner(user)) {
- setSupervisionEnabledForUser(user.getUserIdentifier(), true);
+ if (Flags.enableSyncWithDpm() && isProfileOwner(userId)) {
+ setSupervisionEnabledForUser(userId, true);
}
}
@@ -103,7 +108,7 @@
pw.println("SupervisionService state:");
pw.increaseIndent();
- var users = mUserManagerInternal.getUsers(false);
+ List<UserInfo> users = mUserManagerInternal.getUsers(false);
synchronized (getLockObject()) {
for (var user : users) {
getUserDataLocked(user.id).dump(pw);
@@ -136,8 +141,8 @@
}
/** Returns whether the supervision app has profile owner status. */
- private boolean isProfileOwner(TargetUser user) {
- ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(user.getUserIdentifier());
+ private boolean isProfileOwner(@UserIdInt int userId) {
+ ComponentName profileOwner = mDpmInternal.getProfileOwnerAsUser(userId);
if (profileOwner == null) {
return false;
}
@@ -154,15 +159,46 @@
mSupervisionService = new SupervisionService(context);
}
+ @VisibleForTesting
+ Lifecycle(Context context, SupervisionService supervisionService) {
+ super(context);
+ mSupervisionService = supervisionService;
+ }
+
@Override
public void onStart() {
publishLocalService(SupervisionManagerInternal.class, mSupervisionService.mInternal);
publishBinderService(Context.SUPERVISION_SERVICE, mSupervisionService);
+ if (Flags.enableSyncWithDpm()) {
+ registerProfileOwnerListener();
+ }
+ }
+
+ @VisibleForTesting
+ void registerProfileOwnerListener() {
+ IntentFilter poIntentFilter = new IntentFilter();
+ poIntentFilter.addAction(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
+ poIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ getContext()
+ .registerReceiverForAllUsers(
+ new ProfileOwnerBroadcastReceiver(),
+ poIntentFilter,
+ /* brodcastPermission= */ null,
+ /* scheduler= */ null);
}
@Override
public void onUserStarting(@NonNull TargetUser user) {
- mSupervisionService.syncStateWithDevicePolicyManager(user);
+ if (!user.isPreCreated()) {
+ mSupervisionService.syncStateWithDevicePolicyManager(user.getUserIdentifier());
+ }
+ }
+
+ private final class ProfileOwnerBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mSupervisionService.syncStateWithDevicePolicyManager(getSendingUserId());
+ }
}
}
diff --git a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
index 9772ef9..5db6a8f 100644
--- a/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
+++ b/services/tests/VpnTests/java/com/android/server/connectivity/VpnTest.java
@@ -145,6 +145,7 @@
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.net.ipsec.ike.exceptions.IkeTimeoutException;
import android.net.vcn.VcnTransportInfo;
+import android.net.vcn.util.PersistableBundleUtils;
import android.net.wifi.WifiInfo;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -179,7 +180,6 @@
import com.android.server.DeviceIdleInternal;
import com.android.server.IpSecService;
import com.android.server.VpnTestBase;
-import com.android.server.vcn.util.PersistableBundleUtils;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
index 44c7dec..cd0bd41 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -130,34 +130,64 @@
}
@Test
- public void testStartStopTrackerScreenOnOff() {
+ public void testStartStopTrackerScreenStates() {
mInjector.mInteractive = false;
+ mInjector.mDisplayState = Display.STATE_OFF;
startTracker(mTracker);
assertNull(mInjector.mSensorListener);
assertNotNull(mInjector.mBroadcastReceiver);
assertTrue(mInjector.mIdleScheduled);
- mInjector.sendScreenChange(/* screenOn= */ true);
+ mInjector.sendInteractivityChange(true);
+ mInjector.setDisplayState(Display.STATE_ON);
assertNotNull(mInjector.mSensorListener);
assertTrue(mInjector.mColorSamplingEnabled);
+ assertNotNull(mInjector.mDisplayListener);
- mInjector.sendScreenChange(/* screenOn= */ false);
+ mInjector.setDisplayState(Display.STATE_OFF);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
- // Turn screen on while brightness mode is manual
+ mInjector.setDisplayState(Display.STATE_DOZE);
+ assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+
+ mInjector.setDisplayState(Display.STATE_DOZE_SUSPEND);
+ assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+
+ mInjector.setDisplayState(Display.STATE_ON_SUSPEND);
+ assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+
+ // Screen on while device is not interactive
+ mInjector.setDisplayState(Display.STATE_ON);
+ mInjector.sendInteractivityChange(false);
+ assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
+
+ // Device becomes interactive while brightness mode is manual
mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ false);
- mInjector.sendScreenChange(/* screenOn= */ true);
+ mInjector.sendInteractivityChange(true);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
// Set brightness mode to automatic while screen is off.
- mInjector.sendScreenChange(/* screenOn= */ false);
mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
+ mInjector.setDisplayState(Display.STATE_OFF);
+ assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNotNull(mInjector.mDisplayListener);
+
+ // Set brightness mode to automatic while screen is in doze.
+ mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
+ mInjector.setDisplayState(Display.STATE_DOZE);
assertNull(mInjector.mSensorListener);
assertFalse(mInjector.mColorSamplingEnabled);
// Turn on screen while brightness mode is automatic.
- mInjector.sendScreenChange(/* screenOn= */ true);
+ mInjector.setDisplayState(Display.STATE_ON);
assertNotNull(mInjector.mSensorListener);
assertTrue(mInjector.mColorSamplingEnabled);
@@ -166,11 +196,11 @@
assertNull(mInjector.mBroadcastReceiver);
assertFalse(mInjector.mIdleScheduled);
assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
}
@Test
public void testModifyBrightnessConfiguration() {
- mInjector.mInteractive = true;
// Start with tracker not listening for color samples.
startTracker(mTracker, DEFAULT_INITIAL_BRIGHTNESS, /* collectColorSamples= */ false);
assertFalse(mInjector.mColorSamplingEnabled);
@@ -186,13 +216,17 @@
assertFalse(mInjector.mColorSamplingEnabled);
// Pretend screen is off, update config to turn on color sampling.
- mInjector.sendScreenChange(/* screenOn= */ false);
+ mInjector.setDisplayState(Display.STATE_OFF);
mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true);
mInjector.waitForHandler();
assertFalse(mInjector.mColorSamplingEnabled);
+ // Pretend screen is in doze
+ mInjector.setDisplayState(Display.STATE_DOZE);
+ assertFalse(mInjector.mColorSamplingEnabled);
+
// Pretend screen is on.
- mInjector.sendScreenChange(/* screenOn= */ true);
+ mInjector.setDisplayState(Display.STATE_ON);
assertTrue(mInjector.mColorSamplingEnabled);
mTracker.stop();
@@ -208,7 +242,6 @@
mInjector.mDefaultSamplingAttributes.getComponentMask());
startTracker(mTracker);
assertFalse(mInjector.mColorSamplingEnabled);
- assertNull(mInjector.mDisplayListener);
}
@Test
@@ -220,7 +253,6 @@
0x2);
startTracker(mTracker);
assertFalse(mInjector.mColorSamplingEnabled);
- assertNull(mInjector.mDisplayListener);
}
@Test
@@ -228,14 +260,12 @@
mInjector.mDefaultSamplingAttributes = null;
startTracker(mTracker);
assertFalse(mInjector.mColorSamplingEnabled);
- assertNull(mInjector.mDisplayListener);
}
@Test
public void testColorSampling_FrameRateChange() {
startTracker(mTracker);
assertTrue(mInjector.mColorSamplingEnabled);
- assertNotNull(mInjector.mDisplayListener);
int noFramesSampled = mInjector.mNoColorSamplingFrames;
mInjector.mFrameRate = 120.0f;
// Wrong display
@@ -248,7 +278,6 @@
@Test
public void testAdaptiveOnOff() {
- mInjector.mInteractive = true;
mInjector.mIsBrightnessModeAutomatic = false;
startTracker(mTracker);
assertNull(mInjector.mSensorListener);
@@ -256,7 +285,6 @@
assertNotNull(mInjector.mContentObserver);
assertTrue(mInjector.mIdleScheduled);
assertFalse(mInjector.mColorSamplingEnabled);
- assertNull(mInjector.mDisplayListener);
mInjector.setBrightnessMode(/* isBrightnessModeAutomatic= */ true);
assertNotNull(mInjector.mSensorListener);
@@ -835,12 +863,14 @@
mInjector.waitForHandler();
assertNull(mInjector.mSensorListener);
assertNull(mInjector.mLightSensor);
+ assertNull(mInjector.mDisplayListener);
// Resetting sensor should start listener again
mTracker.setLightSensor(mLightSensorFake);
mInjector.waitForHandler();
assertNotNull(mInjector.mSensorListener);
assertEquals(mInjector.mLightSensor, mLightSensorFake);
+ assertNotNull(mInjector.mDisplayListener);
Sensor secondSensor = new Sensor(mInputSensorInfoMock);
// Setting a different listener should keep things working
@@ -848,6 +878,7 @@
mInjector.waitForHandler();
assertNotNull(mInjector.mSensorListener);
assertEquals(mInjector.mLightSensor, secondSensor);
+ assertNotNull(mInjector.mDisplayListener);
}
@Test
@@ -862,6 +893,7 @@
startTracker(mTracker);
assertNull(mInjector.mSensorListener);
assertNull(mInjector.mLightSensor);
+ assertNull(mInjector.mDisplayListener);
}
@Test
@@ -895,6 +927,7 @@
assertNull(mInjector.mContentObserver);
assertNull(mInjector.mBroadcastReceiver);
assertFalse(mInjector.mIdleScheduled);
+ assertNull(mInjector.mDisplayListener);
// mInjector asserts that we aren't removing a null receiver
mTracker.stop();
@@ -1017,6 +1050,7 @@
Handler mHandler;
boolean mIdleScheduled;
boolean mInteractive = true;
+ int mDisplayState = Display.STATE_ON;
int[] mProfiles;
ContentObserver mContentObserver;
boolean mIsBrightnessModeAutomatic = true;
@@ -1042,14 +1076,20 @@
waitForHandler();
}
- void sendScreenChange(boolean screenOn) {
- mInteractive = screenOn;
+ void sendInteractivityChange(boolean interactive) {
+ mInteractive = interactive;
Intent intent = new Intent();
- intent.setAction(screenOn ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
+ intent.setAction(interactive ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
mBroadcastReceiver.onReceive(InstrumentationRegistry.getContext(), intent);
waitForHandler();
}
+ void setDisplayState(int state) {
+ mDisplayState = state;
+ mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
+ waitForHandler();
+ }
+
void waitForHandler() {
Idle idle = new Idle();
mHandler.getLooper().getQueue().addIdleHandler(idle);
@@ -1183,6 +1223,11 @@
}
@Override
+ public int getDisplayState(Context context) {
+ return mDisplayState;
+ }
+
+ @Override
public int getNightDisplayColorTemperature(Context context) {
return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
mDefaultNightModeColorTemperature);
@@ -1239,7 +1284,7 @@
}
@Override
- public void unRegisterDisplayListener(Context context,
+ public void unregisterDisplayListener(Context context,
DisplayManager.DisplayListener listener) {
mDisplayListener = null;
}
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 47e96d3..1b56b3f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -123,7 +123,6 @@
import android.os.SystemProperties;
import android.os.UserManager;
import android.os.test.FakePermissionEnforcer;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -1387,21 +1386,23 @@
}
/**
- * Tests that it's not allowed to create an auto-mirror virtual display without
- * CAPTURE_VIDEO_OUTPUT permission or a virtual device that can mirror displays
+ * Tests that it is not allowed to create an auto-mirror virtual display for a virtual device
+ * without ADD_MIRROR_DISPLAY permission / without the mirror display capability.
*/
- @EnableFlags(android.companion.virtualdevice.flags.Flags.FLAG_ENABLE_LIMITED_VDM_ROLE)
@Test
- public void createAutoMirrorDisplay_withoutPermissionOrAllowedVirtualDevice_throwsException()
- throws Exception {
+ public void createAutoMirrorDisplay_withoutPermission_throwsException() throws Exception {
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerInternal localService = displayManager.new LocalService();
registerDefaultDisplays(displayManager);
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
- when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
- .thenReturn(PackageManager.PERMISSION_DENIED);
+ if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
+ when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ } else {
+ when(virtualDevice.canCreateMirrorDisplays()).thenReturn(false);
+ }
when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
when(mContext.checkCallingPermission(CAPTURE_VIDEO_OUTPUT)).thenReturn(
PackageManager.PERMISSION_DENIED);
@@ -1432,8 +1433,12 @@
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
- when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
+ when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
+ }
when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
// Create an auto-mirror virtual display using a virtual device.
@@ -1466,8 +1471,12 @@
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
- when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
+ when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
+ }
when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
// Create an auto-mirror virtual display using a virtual device.
@@ -1534,8 +1543,12 @@
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
- when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
+ when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
+ }
when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
when(mContext.checkCallingPermission(ADD_ALWAYS_UNLOCKED_DISPLAY))
.thenReturn(PackageManager.PERMISSION_GRANTED);
@@ -1571,8 +1584,12 @@
when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
when(virtualDevice.getDeviceId()).thenReturn(1);
- when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
- .thenReturn(PackageManager.PERMISSION_GRANTED);
+ if (android.companion.virtualdevice.flags.Flags.enableLimitedVdmRole()) {
+ when(mContext.checkCallingPermission(ADD_MIRROR_DISPLAY))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ } else {
+ when(virtualDevice.canCreateMirrorDisplays()).thenReturn(true);
+ }
when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
// Create an auto-mirror virtual display using a virtual device.
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index e0b0fec..3ac7fb0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -335,10 +335,6 @@
@Override
public void onStopped() {
}
-
- @Override
- public void onRequestedBrightnessChanged(float brightness) {
- }
};
}
}
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 d9332ec0..6defadf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -89,6 +89,7 @@
import android.app.NotificationChannel;
import android.app.SyncNotedAppOp;
import android.app.backup.BackupAnnotations;
+import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -100,6 +101,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.os.Bundle;
@@ -204,6 +206,16 @@
private static final String TEST_AUTHORITY = "test_authority";
private static final String TEST_MIME_TYPE = "application/test_type";
+ private static final Uri TEST_URI = Uri.parse("content://com.example/people");
+ private static final int TEST_CREATOR_UID = 12345;
+ private static final String TEST_CREATOR_PACKAGE = "android.content.testCreatorPackage";
+ private static final String TEST_TYPE = "testType";
+ private static final String TEST_IDENTIFIER = "testIdentifier";
+ private static final String TEST_CATEGORY = "testCategory";
+ private static final String TEST_LAUNCH_TOKEN = "testLaunchToken";
+ private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PACKAGE,
+ "TestClass");
+ private static final int ALL_SET_FLAG = 0xFFFFFFFF;
private static final int[] UID_RECORD_CHANGES = {
UidRecord.CHANGE_PROCSTATE,
@@ -1414,6 +1426,34 @@
& Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
}
+ @Test
+ public void testUseCloneForCreatorTokenAndOriginalIntent_createSameIntentCreatorToken() {
+ Intent testIntent = new Intent(TEST_ACTION1)
+ .setComponent(TEST_COMPONENT)
+ .setDataAndType(TEST_URI, TEST_TYPE)
+ .setIdentifier(TEST_IDENTIFIER)
+ .addCategory(TEST_CATEGORY);
+ testIntent.setOriginalIntent(new Intent(TEST_ACTION2));
+ testIntent.setSelector(new Intent(TEST_ACTION3));
+ testIntent.setSourceBounds(new Rect(0, 0, 100, 100));
+ testIntent.setLaunchToken(TEST_LAUNCH_TOKEN);
+ testIntent.addFlags(ALL_SET_FLAG)
+ .addExtendedFlags(ALL_SET_FLAG);
+ ClipData testClipData = ClipData.newHtmlText("label", "text", "<html/>");
+ testClipData.addItem(new ClipData.Item(new Intent(TEST_ACTION1)));
+ testClipData.addItem(new ClipData.Item(TEST_URI));
+ testIntent.putExtra(TEST_EXTRA_KEY1, TEST_EXTRA_VALUE1);
+
+ ActivityManagerService.IntentCreatorToken tokenForFullIntent =
+ new ActivityManagerService.IntentCreatorToken(TEST_CREATOR_UID,
+ TEST_CREATOR_PACKAGE, testIntent);
+ ActivityManagerService.IntentCreatorToken tokenForCloneIntent =
+ new ActivityManagerService.IntentCreatorToken(TEST_CREATOR_UID,
+ TEST_CREATOR_PACKAGE, testIntent.cloneForCreatorToken());
+
+ assertThat(tokenForFullIntent.getKeyFields()).isEqualTo(tokenForCloneIntent.getKeyFields());
+ }
+
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
long lastNetworkUpdatedProcStateSeq,
final long procStateSeqToWait, boolean expectWait) throws Exception {
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
index 2461e9e..8aaa723 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAgentConnectionManagerTest.java
@@ -119,6 +119,8 @@
mTestApplicationInfo = new ApplicationInfo();
mTestApplicationInfo.packageName = TEST_PACKAGE;
+ mTestApplicationInfo.processName = TEST_PACKAGE;
+ mTestApplicationInfo.uid = Process.FIRST_APPLICATION_UID + 1;
mBackupAgentResult = null;
mTestThread = null;
@@ -134,8 +136,8 @@
@Test
public void bindToAgentSynchronous_amReturnsFailure_returnsNullAndClearsPendingBackups()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(false);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(false);
IBackupAgent result = mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD);
@@ -147,8 +149,8 @@
@Test
public void bindToAgentSynchronous_agentDisconnectedCalled_returnsNullAndClearsPendingBackups()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
// This is so that IBackupAgent.Stub.asInterface() works.
when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
@@ -172,24 +174,7 @@
@Test
public void bindToAgentSynchronous_agentConnectedCalled_returnsBackupAgent() throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
- // This is so that IBackupAgent.Stub.asInterface() works.
- when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
- when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
-
- // This is going to block until it receives the callback so we need to run it on a
- // separate thread.
- Thread testThread = new Thread(() -> setBackupAgentResult(
- mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo,
- ApplicationThreadConstants.BACKUP_MODE_FULL, BackupDestination.CLOUD)),
- "backup-agent-connection-manager-test");
- testThread.start();
- // Give the testThread a head start, otherwise agentConnected() might run before
- // bindToAgentSynchronous() is called.
- Thread.sleep(500);
- mConnectionManager.agentConnected(TEST_PACKAGE, mBackupAgentStub);
- testThread.join();
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_FULL);
assertThat(mBackupAgentResult).isEqualTo(mBackupAgentStub);
verify(mActivityManagerInternal, never()).clearPendingBackup(anyInt());
@@ -198,8 +183,8 @@
@Test
public void bindToAgentSynchronous_unexpectedAgentConnected_doesNotReturnWrongAgent()
throws Exception {
- when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(),
- anyInt(), anyBoolean())).thenReturn(true);
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
// This is so that IBackupAgent.Stub.asInterface() works.
when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
@@ -390,11 +375,75 @@
@Test
public void unbindAgent_callsAmUnbindBackupAgent() throws Exception {
- mConnectionManager.unbindAgent(mTestApplicationInfo);
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ false);
verify(mActivityManager).unbindBackupAgent(eq(mTestApplicationInfo));
}
+ @Test
+ public void unbindAgent_doNotAllowKill_doesNotKillApp() throws Exception {
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ false);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isCoreApp_doesNotKillApp() throws Exception {
+ mTestApplicationInfo.uid = 1000;
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_notCurrentConnection_killsApp() throws Exception {
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_inRestrictedMode_killsApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_FULL);
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_notInRestrictedMode_doesNotKillApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_INCREMENTAL);
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isRestore_noKillAfterRestore_doesNotKillApp()
+ throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_RESTORE);
+ mTestApplicationInfo.flags = 0;
+ verify(mActivityManager).bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ /* useRestrictedMode= */ eq(false));
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager, never()).killApplicationProcess(any(), anyInt());
+ }
+
+ @Test
+ public void unbindAgent_allowKill_isRestore_killAfterRestore_killsApp() throws Exception {
+ bindAndConnectToTestAppAgent(ApplicationThreadConstants.BACKUP_MODE_RESTORE);
+ mTestApplicationInfo.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+
+ mConnectionManager.unbindAgent(mTestApplicationInfo, /* allowKill= */ true);
+
+ verify(mActivityManager).killApplicationProcess(eq(TEST_PACKAGE), anyInt());
+ }
+
// Needed because variables can't be assigned directly inside lambdas in Java.
private void setBackupAgentResult(IBackupAgent result) {
mBackupAgentResult = result;
@@ -404,4 +453,23 @@
private void setTestThread(Thread thread) {
mTestThread = thread;
}
+
+ private void bindAndConnectToTestAppAgent(int backupMode) throws Exception {
+ when(mActivityManager.bindBackupAgent(eq(TEST_PACKAGE), anyInt(), anyInt(), anyInt(),
+ anyBoolean())).thenReturn(true);
+ // This is going to block until it receives the callback so we need to run it on a
+ // separate thread.
+ Thread testThread = new Thread(() -> setBackupAgentResult(
+ mConnectionManager.bindToAgentSynchronous(mTestApplicationInfo, backupMode,
+ BackupDestination.CLOUD)), "backup-agent-connection-manager-test");
+ testThread.start();
+ // Give the testThread a head start, otherwise agentConnected() might run before
+ // bindToAgentSynchronous() is called.
+ Thread.sleep(500);
+ when(mConnectionManager.getCallingUid()).thenReturn(Process.SYSTEM_UID);
+ // This is so that IBackupAgent.Stub.asInterface() works.
+ when(mBackupAgentStub.queryLocalInterface(any())).thenReturn(mBackupAgentStub);
+ mConnectionManager.agentConnected(TEST_PACKAGE, mBackupAgentStub);
+ testThread.join();
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 405024c..769f071 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -134,7 +134,7 @@
mMockSystem.system().validateFinalState();
mInstallPackageHelper = new InstallPackageHelper(mPmService, mock(AppDataHelper.class),
mock(RemovePackageHelper.class), mock(DeletePackageHelper.class),
- mock(BroadcastHelper.class), mock(InstallDependencyHelper.class));
+ mock(BroadcastHelper.class));
}
@NonNull
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index 005ceee..c7fad76 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -29,8 +29,6 @@
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
-import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.DeviceConfig;
import androidx.test.InstrumentationRegistry;
@@ -59,13 +57,8 @@
@LargeTest
@android.platform.test.annotations.DisabledOnRavenwood(reason = "Integration test")
public class CpuPowerStatsCollectorValidationTest {
- @Rule(order = 0)
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule(order = 1)
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
- ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
- : DeviceFlagsValueProvider.createCheckFlagsRule();
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final int WORK_DURATION_MS = 2000;
private static final String TEST_PKG = "com.android.coretests.apps.bstatstestapp";
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index ef0b570..1ff347f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -31,8 +31,6 @@
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.RavenwoodFlagsValueProvider;
-import android.platform.test.ravenwood.RavenwoodRule;
import androidx.test.filters.SmallTest;
@@ -58,18 +56,13 @@
@SuppressWarnings("GuardedBy")
public class SystemServicePowerCalculatorTest {
@Rule(order = 0)
- public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
- @Rule(order = 1)
- public final CheckFlagsRule mCheckFlagsRule = RavenwoodRule.isOnRavenwood()
- ? RavenwoodFlagsValueProvider.createAllOnCheckFlagsRule()
- : DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final double PRECISION = 0.000001;
private static final int APP_UID1 = 100;
private static final int APP_UID2 = 200;
- @Rule(order = 2)
+ @Rule(order = 1)
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_ACTIVE, 720)
.setCpuScalingPolicy(0, new int[]{0, 1}, new int[]{100, 200})
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 0c058df..009ce88 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -897,16 +897,6 @@
}
test_module_config {
- name: "FrameworksServicesTests_server_accessibility",
- base: "FrameworksServicesTests",
- test_suites: [
- "automotive-tests",
- "device-tests",
- ],
- include_filters: ["com.android.server.accessibility"],
-}
-
-test_module_config {
name: "FrameworksServicesTests_server_binarytransparencyservicetest",
base: "FrameworksServicesTests",
test_suites: [
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 27de764..ac535b3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -1729,7 +1729,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() {
setupShortcutTargetServices();
AccessibilityUserState userState = mA11yms.getCurrentUserState();
@@ -1752,7 +1751,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_doIt_packageEnabled_returnsFalse() {
setupShortcutTargetServices();
AccessibilityUserState userState = mA11yms.getCurrentUserState();
@@ -1775,7 +1773,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
public void onHandleForceStop_dontDoIt_packageNotEnabled_returnsFalse() {
PackageMonitor monitor = spy(mA11yms.getPackageMonitor());
when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM);
@@ -1815,9 +1812,7 @@
}
@Test
- @EnableFlags({
- android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
- Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
public void restoreShortcutTargets_hardware_alreadyHadDefaultService_doesNotClear() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
mTestableContext.getOrCreateTestableResources().addOverride(
@@ -1843,9 +1838,7 @@
}
@Test
- @EnableFlags({
- android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
- Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ @EnableFlags(android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
public void restoreShortcutTargets_hardware_didNotHaveDefaultService_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
@@ -1870,9 +1863,7 @@
}
@Test
- @EnableFlags({
- android.view.accessibility.Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE,
- Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE})
+ @EnableFlags(Flags.FLAG_CLEAR_DEFAULT_FROM_A11Y_SHORTCUT_TARGET_SERVICE_RESTORE)
public void restoreShortcutTargets_hardware_nullSetting_clearsDefaultService() {
final String serviceDefault = TARGET_STANDARD_A11Y_SERVICE_NAME;
final String serviceRestored = TARGET_ALWAYS_ON_A11Y_SERVICE.flattenToString();
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 8914696..d4f2dcc 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -448,4 +448,14 @@
mConnection.binderDied();
assertThat(mConnection.getServiceInfo().flags & flag).isEqualTo(0);
}
+
+ @Test
+ public void setInputMethodEnabled_checksAccessWithProvidedImeIdAndUserId() {
+ final String imeId = "test_ime_id";
+ final int callingUserId = UserHandle.getCallingUserId();
+ mConnection.setInputMethodEnabled(imeId, true);
+
+ verify(mMockSecurityPolicy).canEnableDisableInputMethod(
+ eq(imeId), any(), eq(callingUserId));
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index 52b33db..f371823 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -51,9 +51,6 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArraySet;
import android.view.KeyEvent;
@@ -98,9 +95,6 @@
private static final int STREAMED_CALLING_UID = 9876;
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private Context mMockContext;
@@ -243,7 +237,6 @@
* app changes to the proxy device.
*/
@Test
- @RequiresFlagsEnabled(Flags.FLAG_PROXY_USE_APPS_ON_VIRTUAL_DEVICE_LISTENER)
public void testUpdateProxyOfRunningAppsChange_changedUidIsStreamedApp_propagatesChange() {
final VirtualDeviceManagerInternal localVdm =
Mockito.mock(VirtualDeviceManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 1a593dd..42b7f4b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -755,6 +755,7 @@
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never()).onSecureWindowHidden(eq(DISPLAY_ID));
verify(mActivityListener, never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
@@ -776,6 +777,10 @@
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
+
+ assertThat(gwpc.keepActivityOnWindowFlagsChanged(activityInfo, 0, 0)).isTrue();
+
+ verify(mActivityListener, timeout(TIMEOUT_MILLIS)).onSecureWindowHidden(eq(DISPLAY_ID));
}
@Test
@@ -794,6 +799,7 @@
verify(mActivityListener, after(TIMEOUT_MILLIS).never())
.onSecureWindowShown(eq(DISPLAY_ID), eq(activityInfo));
+ verify(mActivityListener, never()).onSecureWindowHidden(eq(DISPLAY_ID));
verify(mActivityListener, never())
.onActivityLaunchBlocked(eq(DISPLAY_ID), eq(activityInfo), any());
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 727d1b5..32578a7 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -2076,10 +2076,10 @@
private AssociationInfo createAssociationInfo(int associationId, String deviceProfile,
CharSequence displayName) {
return new AssociationInfo(associationId, /* userId= */ 0, /* packageName=*/ null,
- /* tag= */ null, MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
+ MacAddress.BROADCAST_ADDRESS, displayName, deviceProfile,
/* associatedDevice= */ null, /* selfManaged= */ true,
/* notifyOnDeviceNearby= */ false, /* revoked= */ false, /* pending= */ false,
/* timeApprovedMs= */0, /* lastTimeConnectedMs= */0,
- /* systemDataSyncFlags= */ -1, /* deviceIcon= */ null);
+ /* systemDataSyncFlags= */ -1, /* deviceIcon= */ null, /* deviceId= */ null);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index ab5a5a9..5127b2d 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -24,9 +24,14 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
import android.content.Context;
+import android.frameworks.devicestate.DeviceStateConfiguration;
+import android.frameworks.devicestate.ErrorCode;
+import android.frameworks.devicestate.IDeviceStateListener;
+import android.frameworks.devicestate.IDeviceStateService;
import android.hardware.devicestate.DeviceState;
import android.hardware.devicestate.DeviceStateInfo;
import android.hardware.devicestate.DeviceStateRequest;
@@ -34,6 +39,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.FlagsParameterization;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -336,6 +342,53 @@
}
@Test
+ public void halRegisterUnregisterCallback() throws RemoteException {
+ IDeviceStateService halService = mService.getHalBinderService();
+ IDeviceStateListener halListener = new IDeviceStateListener.Stub() {
+ @Override
+ public void onDeviceStateChanged(DeviceStateConfiguration deviceState) { }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IDeviceStateListener.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IDeviceStateListener.HASH;
+ }
+ };
+
+ int errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(halListener);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.unregisterListener(null);
+ } catch(ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.BAD_INPUT);
+
+ halService.registerListener(halListener);
+
+ errorCode = ErrorCode.OK;
+ try {
+ halService.registerListener(halListener);
+ } catch (ServiceSpecificException e) {
+ errorCode = e.errorCode;
+ }
+ assertEquals(errorCode, ErrorCode.ALREADY_EXISTS);
+
+ halService.unregisterListener(halListener);
+ }
+
+ @Test
public void registerCallback() throws RemoteException {
final TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
mService.getBinderService().registerCallback(callback);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index dd4101e..30dac9f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -433,4 +433,21 @@
.dsmStatusChanged(anyBoolean(), anyBoolean(),
eq(HdmiStatsEnums.LOG_REASON_DSM_SETTING_TOGGLED));
}
+
+ @Test
+ public void testPowerStateChangeOnActiveSourceLostToggled_writesAtom_logReasonSetting() {
+ mHdmiControlServiceSpy.onWakeUp(WAKE_UP_SCREEN_ON);
+ Mockito.clearInvocations(mHdmiCecAtomWriterSpy);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlServiceSpy.writePowerStateChangeOnActiveSourceLostAtom(true);
+ mTestLooper.dispatchAll();
+
+ verify(mHdmiCecAtomWriterSpy, times(1))
+ .powerStateChangeOnActiveSourceLostChanged(eq(true),
+ eq(HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_SETTING), anyString(), anyInt(), anyInt());
+ verify(mHdmiCecAtomWriterSpy, never())
+ .powerStateChangeOnActiveSourceLostChanged(eq(true),
+ eq(HdmiStatsEnums.LOG_REASON_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST_TOGGLE_POP_UP), anyString(), anyInt(), anyInt());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 077bb03..861e72d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -95,9 +95,6 @@
private boolean mActiveMediaSessionsPaused;
private FakePowerManagerInternalWrapper mPowerManagerInternal =
new FakePowerManagerInternalWrapper();
-
- private boolean mIsOnActiveSourceLostPopupActive;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -165,12 +162,12 @@
mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService) {
@Override
void startHdmiCecActiveSourceLostActivity() {
- mIsOnActiveSourceLostPopupActive = true;
+ setIsActiveSourceLostPopupLaunched(true);
}
@Override
void dismissUiOnActiveSourceStatusRecovered() {
- mIsOnActiveSourceLostPopupActive = false;
+ setIsActiveSourceLostPopupLaunched(false);
}
};
mHdmiCecLocalDevicePlayback.init();
@@ -2389,7 +2386,7 @@
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress).isEqualTo(ADDR_TV);
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
}
@Test
@@ -2430,7 +2427,7 @@
// Pop-up is not shown, playback device asserts active source since TV doesn't answer the
// request.
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2480,7 +2477,7 @@
mTestLooper.dispatchAll();
// Pop-up is not shown since playback device is active source.
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2532,7 +2529,7 @@
// Pop-up is shown, playback device doesn't assert active source since active path is
// switched to a non-CEC device.
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(ADDR_INVALID);
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().physicalAddress)
@@ -2569,7 +2566,7 @@
});
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mPowerManager.isInteractive()).isTrue();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
@@ -2609,13 +2606,13 @@
mNativeWrapper.onCecMessage(activeSourceFromTv);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(setStreamPathToPlayback))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2664,13 +2661,13 @@
// Pop-up is triggered.
mTestLooper.moveTimeForward(POPUP_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.handleRoutingChange(routingChangeToPlayback))
.isEqualTo(Constants.HANDLED);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2711,7 +2708,7 @@
mNativeWrapper.onCecMessage(activeSourceFromTv);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
mHdmiControlService.oneTouchPlay(new IHdmiControlCallback() {
@Override
public void onComplete(int result) throws RemoteException {
@@ -2724,7 +2721,7 @@
});
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
assertThat(mNativeWrapper.getResultMessages().contains(activeSourceFromPlayback)).isTrue();
assertThat(mHdmiCecLocalDevicePlayback.getActiveSource().logicalAddress)
.isEqualTo(mPlaybackLogicalAddress);
@@ -2897,11 +2894,11 @@
} else {
mTestLooper.moveTimeForward(TIMEOUT_MS);
mTestLooper.dispatchAll();
- assertThat(mIsOnActiveSourceLostPopupActive).isFalse();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isFalse();
return;
}
}
- assertThat(mIsOnActiveSourceLostPopupActive).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.isActiveSourceLostPopupLaunched()).isTrue();
mPowerManagerInternal.setIdleDuration(idleDuration);
mTestLooper.moveTimeForward(STANDBY_AFTER_ACTIVE_SOURCE_LOST_DELAY_MS);
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index 8290e1c..a8544f6 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -16,11 +16,20 @@
package com.android.server.supervision
+import android.app.Activity
+import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
+import android.app.supervision.flags.Flags
+import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.content.IntentFilter
import android.content.pm.UserInfo
+import android.os.Handler
import android.os.PersistableBundle
+import android.os.UserHandle
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -46,22 +55,20 @@
*/
@RunWith(AndroidJUnit4::class)
class SupervisionServiceTest {
- companion object {
- const val USER_ID = 100
- }
-
- @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
private lateinit var context: Context
+ private lateinit var lifecycle: SupervisionService.Lifecycle
private lateinit var service: SupervisionService
@Before
fun setUp() {
context = InstrumentationRegistry.getInstrumentation().context
+ context = SupervisionContextWrapper(context)
LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -70,43 +77,61 @@
LocalServices.addService(UserManagerInternal::class.java, mockUserManagerInternal)
service = SupervisionService(context)
+ lifecycle = SupervisionService.Lifecycle(context, service)
+ lifecycle.registerProfileOwnerListener()
}
@Test
- @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
- fun syncStateWithDevicePolicyManager_supervisionAppIsProfileOwner_enablesSupervision() {
- val supervisionPackageName =
- context.getResources().getString(R.string.config_systemSupervision)
-
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_supervisionAppIsProfileOwner_enablesSupervision() {
whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
- .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))
+ .thenReturn(ComponentName(systemSupervisionPackage, "MainActivity"))
- service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))
+ simulateUserStarting(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
}
@Test
- @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
- fun syncStateWithDevicePolicyManager_userPreCreated_doesNotEnableSupervision() {
- val supervisionPackageName =
- context.getResources().getString(R.string.config_systemSupervision)
-
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_userPreCreated_doesNotEnableSupervision() {
whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
- .thenReturn(ComponentName(supervisionPackageName, "MainActivity"))
+ .thenReturn(ComponentName(systemSupervisionPackage, "MainActivity"))
- service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID, preCreated = true))
+ simulateUserStarting(USER_ID, preCreated = true)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
@Test
- @RequiresFlagsEnabled(android.app.admin.flags.Flags.FLAG_ENABLE_SUPERVISION_SERVICE_SYNC)
- fun syncStateWithDevicePolicyManager_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() {
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun onUserStarting_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() {
whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
.thenReturn(ComponentName("other.package", "MainActivity"))
- service.syncStateWithDevicePolicyManager(newTargetUser(USER_ID))
+ simulateUserStarting(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun profileOwnerChanged_supervisionAppIsProfileOwner_enablesSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(ComponentName(systemSupervisionPackage, "MainActivity"))
+
+ broadcastProfileOwnerChanged(USER_ID)
+
+ assertThat(service.isSupervisionEnabledForUser(USER_ID)).isTrue()
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_ENABLE_SYNC_WITH_DPM)
+ fun profileOwnerChanged_supervisionAppIsNotProfileOwner_doesNotEnableSupervision() {
+ whenever(mockDpmInternal.getProfileOwnerAsUser(USER_ID))
+ .thenReturn(ComponentName("other.package", "MainActivity"))
+
+ broadcastProfileOwnerChanged(USER_ID)
assertThat(service.isSupervisionEnabledForUser(USER_ID)).isFalse()
}
@@ -150,9 +175,62 @@
assertThat(userData.supervisionLockScreenOptions).isNull()
}
- private fun newTargetUser(userId: Int, preCreated: Boolean = false): TargetUser {
+ private val systemSupervisionPackage: String
+ get() = context.getResources().getString(R.string.config_systemSupervision)
+
+ private fun simulateUserStarting(userId: Int, preCreated: Boolean = false) {
val userInfo = UserInfo(userId, /* name= */ "tempUser", /* flags= */ 0)
userInfo.preCreated = preCreated
- return TargetUser(userInfo)
+ lifecycle.onUserStarting(TargetUser(userInfo))
+ }
+
+ private fun broadcastProfileOwnerChanged(userId: Int) {
+ val intent = Intent(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED)
+ context.sendBroadcastAsUser(intent, UserHandle.of(userId))
+ }
+
+ private companion object {
+ const val USER_ID = 100
+ }
+}
+
+/**
+ * A context wrapper that allows broadcast intents to immediately invoke the receivers without
+ * performing checks on the sending user.
+ */
+private class SupervisionContextWrapper(val context: Context) : ContextWrapper(context) {
+ val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()
+
+ override fun registerReceiverForAllUsers(
+ receiver: BroadcastReceiver?,
+ filter: IntentFilter,
+ broadcastPermission: String?,
+ scheduler: Handler?,
+ ): Intent? {
+ if (receiver != null) {
+ interceptors.add(Pair(receiver, filter))
+ }
+ return null
+ }
+
+ override fun sendBroadcastAsUser(intent: Intent, user: UserHandle) {
+ val pendingResult =
+ BroadcastReceiver.PendingResult(
+ Activity.RESULT_OK,
+ /* resultData= */ "",
+ /* resultExtras= */ null,
+ /* type= */ 0,
+ /* ordered= */ true,
+ /* sticky= */ false,
+ /* token= */ null,
+ user.identifier,
+ /* flags= */ 0,
+ )
+ for ((receiver, filter) in interceptors) {
+ if (filter.match(contentResolver, intent, false, "") > 0) {
+ receiver.setPendingResult(pendingResult)
+ receiver.onReceive(context, intent)
+ }
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 5852af7..d20f73d 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -506,9 +506,9 @@
assertThat(client0.getProfile().getInUseFrontendHandles())
.isEqualTo(new HashSet<Long>(Arrays.asList(infos[0].handle)));
- // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
- // (client0) to maintain ownership such as requester will not get the resources.
- client1.getProfile().setResourceHolderRetain(true);
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to true to allow the Resource
+ // Holder (client0) to maintain ownership such as requester will not get the resources.
+ client1.getProfile().setResourceOwnershipRetention(true);
request = tunerFrontendRequest(client1.getId() /*clientId*/, FrontendSettings.TYPE_DVBT);
assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendHandle))
@@ -520,10 +520,10 @@
.isEqualTo(client0.getId());
assertThat(client0.isReclaimed()).isFalse();
- // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to false to allow the Resource
// Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
// resources.
- client1.getProfile().setResourceHolderRetain(false);
+ client1.getProfile().setResourceOwnershipRetention(false);
assertThat(mTunerResourceManagerService.requestFrontendInternal(request, frontendHandle))
.isTrue();
@@ -645,9 +645,9 @@
.isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
- // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
- // to maintain ownership such as requester (client1) will not get the resources.
- client1.getProfile().setResourceHolderRetain(true);
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to true to allow the Resource
+ // Holder to maintain ownership such as requester (client1) will not get the resources.
+ client1.getProfile().setResourceOwnershipRetention(true);
request = casSessionRequest(client1.getId(), 1);
assertThat(
@@ -663,10 +663,10 @@
assertThat(mTunerResourceManagerService.getCasResource(1).isFullyUsed()).isTrue();
assertThat(client0.isReclaimed()).isFalse();
- // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to false to allow the Resource
// Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
// resources.
- client1.getProfile().setResourceHolderRetain(false);
+ client1.getProfile().setResourceOwnershipRetention(false);
assertThat(
mTunerResourceManagerService.requestCasSessionInternal(request, casSessionHandle))
@@ -759,9 +759,9 @@
.isEqualTo(new HashSet<Integer>(Arrays.asList(client0.getId())));
assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
- // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
- // (client0) to maintain ownership such as requester will not get the resources.
- client1.getProfile().setResourceHolderRetain(true);
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to true to allow the Resource
+ // Holder (client0) to maintain ownership such as requester will not get the resources.
+ client1.getProfile().setResourceOwnershipRetention(true);
request = tunerCiCamRequest(client1.getId(), 1);
assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
@@ -776,10 +776,10 @@
assertThat(mTunerResourceManagerService.getCiCamResource(1).isFullyUsed()).isTrue();
assertThat(client0.isReclaimed()).isFalse();
- // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to false to allow the Resource
// Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
// resources.
- client1.getProfile().setResourceHolderRetain(false);
+ client1.getProfile().setResourceOwnershipRetention(false);
assertThat(mTunerResourceManagerService.requestCiCamInternal(request, ciCamHandle))
.isTrue();
@@ -927,9 +927,9 @@
assertThat(client0.getProfile().getInUseLnbHandles())
.isEqualTo(new HashSet<Long>(Arrays.asList(lnbHandles[0])));
- // setResourceHolderRetain sets mResourceHolderRetain to true to allow the Resource Holder
- // (client0) to maintain ownership such as requester will not get the resources.
- client1.getProfile().setResourceHolderRetain(true);
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to true to allow the Resource
+ // Holder (client0) to maintain ownership such as requester will not get the resources.
+ client1.getProfile().setResourceOwnershipRetention(true);
request = new TunerLnbRequest();
request.clientId = client1.getId();
@@ -942,10 +942,10 @@
assertThat(client0.isReclaimed()).isFalse();
assertThat(client1.getProfile().getInUseLnbHandles().size()).isEqualTo(0);
- // setResourceHolderRetain sets mResourceHolderRetain to false to allow the Resource
+ // setResourceOwnershipRetention sets mResourceOwnerRetention to false to allow the Resource
// Challenger (client1) to acquire the resource and Resource Holder loses ownership of the
// resources.
- client1.getProfile().setResourceHolderRetain(false);
+ client1.getProfile().setResourceOwnershipRetention(false);
assertThat(mTunerResourceManagerService.requestLnbInternal(request, lnbHandle)).isTrue();
assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index aee9f0f..13a6e4c 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -27,6 +27,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Predicate;
public class TestSystemImpl implements SystemInterface {
private String mUserProvider = null;
@@ -36,6 +37,7 @@
Map<String, Map<Integer, PackageInfo>> mPackages = new HashMap();
private final int mNumRelros;
private final boolean mIsDebuggable;
+ private Predicate<PackageInfo> mCompatibilityPredicate;
public static final int PRIMARY_USER_ID = 0;
@@ -45,6 +47,7 @@
mNumRelros = numRelros;
mIsDebuggable = isDebuggable;
mUsers.add(PRIMARY_USER_ID);
+ mCompatibilityPredicate = pi -> true;
}
public void addUser(int userId) {
@@ -129,6 +132,15 @@
}
@Override
+ public boolean isCompatibleImplementationPackage(PackageInfo packageInfo) {
+ return mCompatibilityPredicate.test(packageInfo);
+ }
+
+ public void setCompatibilityPredicate(Predicate<PackageInfo> predicate) {
+ mCompatibilityPredicate = predicate;
+ }
+
+ @Override
public List<UserPackage> getPackageInfoForProviderAllUsers(WebViewProviderInfo info) {
Map<Integer, PackageInfo> userPackages = mPackages.get(info.packageName);
List<UserPackage> ret = new ArrayList();
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 42eb609..bf99b6a 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -25,10 +25,12 @@
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.Base64;
-import android.webkit.UserPackage;
+import android.webkit.Flags;
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
@@ -175,8 +177,6 @@
// no flag means invalid
p.applicationInfo.metaData.putString(WEBVIEW_LIBRARY_FLAG, "blah");
}
- // Default to this package being valid in terms of targetSdkVersion.
- p.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
return p;
}
@@ -1238,20 +1238,21 @@
}
/**
- * Ensure that packages with a targetSdkVersion targeting the current platform are valid, and
+ * Ensure that packages with a targetSdkVersion targeting the correct platform are valid, and
* that packages targeting an older version are not valid.
*/
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_USE_B_ENTRY_POINT)
public void testTargetSdkVersionValidity() {
PackageInfo newSdkPackage = createPackageInfo("newTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+ true /* enabled */, true /* valid */, true /* installed */);
+ newSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
PackageInfo currentSdkPackage = createPackageInfo("currentTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- currentSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK;
+ true /* enabled */, true /* valid */, true /* installed */);
+ currentSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.TIRAMISU;
PackageInfo oldSdkPackage = createPackageInfo("oldTargetSdkPackage",
- true /* enabled */, true /* valid */, true /* installed */);
- oldSdkPackage.applicationInfo.targetSdkVersion = UserPackage.MINIMUM_SUPPORTED_SDK - 1;
+ true /* enabled */, true /* valid */, true /* installed */);
+ oldSdkPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.S;
WebViewProviderInfo newSdkProviderInfo =
new WebViewProviderInfo(newSdkPackage.packageName, "", true, false, null);
@@ -1264,6 +1265,9 @@
newSdkProviderInfo
};
setupWithPackages(packages);
+ // Mock the compatibility predicate, requiring T as targetSdkVersion.
+ mTestSystemImpl.setCompatibilityPredicate(
+ pi -> pi.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.TIRAMISU);
// Start with the setting pointing to the invalid package
mTestSystemImpl.updateUserSetting(oldSdkPackage.packageName);
@@ -1280,6 +1284,54 @@
1 /* first preparation phase */);
}
+ /**
+ * Ensure that packages with a versionCode new enough for the current platform are valid, and
+ * that older packages are not valid.
+ */
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_USE_B_ENTRY_POINT)
+ public void testVersionCodeOSCompatValidity() {
+ PackageInfo newVersionPackage = createPackageInfo("newVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ newVersionPackage.setLongVersionCode(200L);
+ PackageInfo currentVersionPackage = createPackageInfo("currentVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ currentVersionPackage.setLongVersionCode(100L);
+ PackageInfo oldVersionPackage = createPackageInfo("oldVersionPackage",
+ true /* enabled */, true /* valid */, true /* installed */);
+ oldVersionPackage.setLongVersionCode(50L);
+
+ WebViewProviderInfo newVersionProviderInfo =
+ new WebViewProviderInfo(newVersionPackage.packageName, "", true, false, null);
+ WebViewProviderInfo currentVersionProviderInfo =
+ new WebViewProviderInfo(currentVersionPackage.packageName, "", true, false, null);
+ WebViewProviderInfo[] packages =
+ new WebViewProviderInfo[] {
+ currentVersionProviderInfo,
+ new WebViewProviderInfo(oldVersionPackage.packageName, "", true, false, null),
+ newVersionProviderInfo
+ };
+ setupWithPackages(packages);
+ // Mock the compatibility predicate as requiring 100 as versionCode.
+ mTestSystemImpl.setCompatibilityPredicate(
+ pi -> pi.getLongVersionCode() >= 100L);
+ // Start with the setting pointing to the invalid package
+ mTestSystemImpl.updateUserSetting(oldVersionPackage.packageName);
+
+ mTestSystemImpl.setPackageInfo(newVersionPackage);
+ mTestSystemImpl.setPackageInfo(currentVersionPackage);
+ mTestSystemImpl.setPackageInfo(oldVersionPackage);
+
+ assertArrayEquals(
+ new WebViewProviderInfo[] { currentVersionProviderInfo, newVersionProviderInfo },
+ mWebViewUpdateServiceImpl.getValidWebViewPackages());
+
+ runWebViewBootPreparationOnMainSync();
+
+ checkPreparationPhasesForPackage(currentVersionPackage.packageName,
+ 1 /* first preparation phase */);
+ }
+
@Test
public void testDefaultWebViewPackageIsTheFirstAvailableByDefault() {
String nonDefaultPackage = "nonDefaultPackage";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 863f42f..90bf1d3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -6884,10 +6884,16 @@
public void testReadPolicyXml_backupRestoreLogging() throws Exception {
BackupRestoreEventLogger logger = mock(BackupRestoreEventLogger.class);
+ if (ActivityManager.getCurrentUser() != UserHandle.USER_SYSTEM) {
+ // By default, the ZenModeHelper only has a configuration for the system user.
+ // If the current user is not the system user, the user must be updated.
+ mService.mZenModeHelper.onUserSwitched(ActivityManager.getCurrentUser());
+ }
UserInfo ui = new UserInfo(ActivityManager.getCurrentUser(), "Clone", UserInfo.FLAG_FULL);
ui.userType = USER_TYPE_FULL_SYSTEM;
when(mUmInternal.getUserInfo(ActivityManager.getCurrentUser())).thenReturn(ui);
- when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(new ArrayMap<>());
+ when(mPermissionHelper.getNotificationPermissionValues(ActivityManager.getCurrentUser()))
+ .thenReturn(new ArrayMap<>());
TypedXmlSerializer serializer = Xml.newFastSerializer();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
@@ -8931,8 +8937,8 @@
@Test
public void testAreBubblesEnabled_false() throws Exception {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_BUBBLES, 0);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.NOTIFICATION_BUBBLES, 0, UserHandle.getUserId(mUid));
mService.mPreferencesHelper.updateBubblesEnabled();
assertFalse(mBinderService.areBubblesEnabled(UserHandle.getUserHandleForUid(mUid)));
}
@@ -13310,6 +13316,7 @@
when(mPermissionHelper.hasPermission(UID_N_MR1)).thenReturn(false);
when(mPermissionHelper.hasPermission(UID_P)).thenReturn(true);
+ enableInteractAcrossUsers();
assertThat(mBinderService.getPackagesBypassingDnd(UserHandle.getUserId(UID_P)).getList())
.containsExactly(new ZenBypassingApp(PKG_P, false));
}
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index ec83e99..e328419 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -272,7 +272,8 @@
for (VendorVibrationSession session : mPendingSessions) {
session.cancelSession();
}
- mTestLooper.dispatchAll();
+ // Dispatch and wait for all callbacks in test looper to be processed.
+ stopAutoDispatcherAndDispatchAll();
// Wait until pending vibrations end asynchronously.
for (HalVibration vibration : mPendingVibrations) {
vibration.waitForEnd();
@@ -292,8 +293,6 @@
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
- // Ignore potential exceptions about the looper having never dispatched any messages.
- mTestLooper.stopAutoDispatchAndIgnoreExceptions();
if (mInputManagerGlobalSession != null) {
mInputManagerGlobalSession.close();
}
@@ -1240,24 +1239,27 @@
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void vibrate_withOngoingHigherImportanceSession_ignoresEffect() throws Exception {
+ public void vibrate_withOngoingHigherImportanceVendorSession_ignoresEffect() throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
+
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback).onStarted(any(IVibrationSession.class));
HalVibration vibration = vibrateAndWaitUntilFinished(service,
VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
HAPTIC_FEEDBACK_ATTRS);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
assertThat(vibration.getStatus()).isEqualTo(Status.IGNORED_FOR_HIGHER_IMPORTANCE);
@@ -1330,24 +1332,28 @@
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void vibrate_withOngoingLowerImportanceSession_cancelsOngoingSession() throws Exception {
+ public void vibrate_withOngoingLowerImportanceVendorSession_cancelsOngoingSession()
+ throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
+
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback).onStarted(any(IVibrationSession.class));
HalVibration vibration = vibrateAndWaitUntilFinished(service,
VibrationEffect.get(VibrationEffect.EFFECT_CLICK),
HAPTIC_FEEDBACK_ATTRS);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
assertThat(vibration.getStatus()).isEqualTo(Status.FINISHED);
@@ -2393,16 +2399,16 @@
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void onExternalVibration_withOngoingHigherImportanceSession_ignoreNewVibration()
+ public void onExternalVibration_withOngoingHigherImportanceVendorSession_ignoreNewVibration()
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
verify(callback).onStarted(any(IVibrationSession.class));
@@ -2413,6 +2419,9 @@
// External vibration is ignored.
assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
+
// Session still running.
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
verify(callback, never()).onFinishing();
@@ -2476,16 +2485,16 @@
@EnableFlags(android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS)
@Test
- public void onExternalVibration_withOngoingLowerImportanceSession_cancelsOngoingSession()
+ public void onExternalVibration_withOngoingLowerImportanceVendorSession_cancelsOngoingSession()
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
+ // Keep auto-dispatcher that can be used in the session cancellation when vibration starts.
mTestLooper.dispatchAll();
verify(callback).onStarted(any(IVibrationSession.class));
@@ -2494,7 +2503,9 @@
ExternalVibrationScale scale =
mExternalVibratorService.onExternalVibrationStart(externalVibration);
assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
// Session is cancelled.
assertThat(session.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
@@ -2780,7 +2791,9 @@
assertThrows("Expected starting session without feature flag to fail!",
UnsupportedOperationException.class,
() -> startSession(service, RINGTONE_ATTRS, callback, vibratorId));
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
verify(callback, never()).onStarted(any(IVibrationSession.class));
@@ -2798,7 +2811,9 @@
IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
callback, vibratorId);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
@@ -2817,7 +2832,9 @@
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS,
/* callback= */ null, vibratorId);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session).isNull();
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
@@ -2837,10 +2854,11 @@
int[] emptyIds = {};
session = startSession(service, RINGTONE_ATTRS, callback, emptyIds);
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
+
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
-
- mTestLooper.dispatchAll();
-
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
verify(callback, never()).onFinishing();
verify(callback, times(2))
@@ -2862,7 +2880,9 @@
doReturn(token).when(callback).asBinder();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 3);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(session.getStatus()).isEqualTo(Status.IGNORED_UNSUPPORTED);
verify(mNativeWrapperMock, never()).startSession(anyLong(), any(int[].class));
@@ -2882,7 +2902,9 @@
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2913,7 +2935,9 @@
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2933,12 +2957,12 @@
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
- // Delay not applied when session is aborted.
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2959,12 +2983,12 @@
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
VibratorManagerService service = createSystemReadyService();
- // Delay not applied when session is aborted.
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -2988,6 +3012,7 @@
throws Exception {
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1, 2);
+
VibratorManagerService service = createSystemReadyService();
ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
ArgumentCaptor.forClass(
@@ -2999,7 +3024,9 @@
IVibrationSessionCallback callback = mock(IVibrationSessionCallback.class);
doReturn(token).when(callback).asBinder();
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
verify(callback).onStarted(any(IVibrationSession.class));
@@ -3019,13 +3046,14 @@
mockCapabilities(IVibratorManager.CAP_START_SESSIONS);
mockVibrators(1);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
VendorVibrationSession session1 = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
VendorVibrationSession session2 = startSession(service, RINGTONE_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
verify(callback).onStarted(captor.capture());
@@ -3051,8 +3079,7 @@
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1);
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{10, 10_000}, new int[]{128, 255}, -1);
@@ -3064,7 +3091,9 @@
service, TEST_TIMEOUT_MILLIS));
VendorVibrationSession session = startSession(service, HAPTIC_FEEDBACK_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock, never())
.startSession(eq(session.getSessionId()), any(int[].class));
@@ -3083,8 +3112,7 @@
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
VibrationEffect effect = VibrationEffect.createWaveform(
new long[]{10, 10_000}, new int[]{128, 255}, -1);
@@ -3098,7 +3126,9 @@
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
vibration.waitForEnd();
assertTrue(waitUntil(s -> session.isStarted(), service, TEST_TIMEOUT_MILLIS));
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertThat(vibration.getStatus()).isEqualTo(Status.CANCELLED_SUPERSEDED);
assertThat(session.getStatus()).isEqualTo(Status.RUNNING);
@@ -3116,8 +3146,7 @@
mVibratorProviders.get(1).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
VibratorManagerService service = createSystemReadyService();
- IVibrationSessionCallback callback =
- mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ IVibrationSessionCallback callback = mockSessionCallbacks();
IBinder firstToken = mock(IBinder.class);
IExternalVibrationController controller = mock(IExternalVibrationController.class);
@@ -3128,7 +3157,9 @@
mExternalVibratorService.onExternalVibrationStart(externalVibration);
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
assertNotEquals(ExternalVibrationScale.ScaleLevel.SCALE_MUTE, scale.scaleLevel);
// The external vibration should have been cancelled
@@ -3149,9 +3180,10 @@
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3186,9 +3218,10 @@
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3228,9 +3261,10 @@
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3272,9 +3306,10 @@
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3317,9 +3352,10 @@
VibratorManagerService service = createSystemReadyService();
int sessionFinishDelayMs = 200;
IVibrationSessionCallback callback = mockSessionCallbacks(sessionFinishDelayMs);
-
VendorVibrationSession session = startSession(service, RINGTONE_ATTRS, callback, 1, 2);
- mTestLooper.dispatchAll();
+
+ // Make sure all messages are processed before asserting on the session callbacks.
+ stopAutoDispatcherAndDispatchAll();
verify(mNativeWrapperMock).startSession(eq(session.getSessionId()), eq(new int[] {1, 2}));
ArgumentCaptor<IVibrationSession> captor = ArgumentCaptor.forClass(IVibrationSession.class);
@@ -3773,6 +3809,10 @@
when(mNativeWrapperMock.getVibratorIds()).thenReturn(vibratorIds);
}
+ private IVibrationSessionCallback mockSessionCallbacks() {
+ return mockSessionCallbacks(/* delayToEndSessionMillis= */ TEST_TIMEOUT_MILLIS);
+ }
+
private IVibrationSessionCallback mockSessionCallbacks(long delayToEndSessionMillis) {
Handler handler = new Handler(mTestLooper.getLooper());
ArgumentCaptor<VibratorManagerService.VibratorManagerNativeCallbacks> listenerCaptor =
@@ -3925,6 +3965,13 @@
return predicateResult;
}
+ private void stopAutoDispatcherAndDispatchAll() {
+ // Stop auto-dispatcher thread and wait for it to finish processing any messages.
+ mTestLooper.stopAutoDispatchAndIgnoreExceptions();
+ // Dispatch any pending message left.
+ mTestLooper.dispatchAll();
+ }
+
private void grantPermission(String permission) {
when(mContextSpy.checkCallingOrSelfPermission(permission))
.thenReturn(PackageManager.PERMISSION_GRANTED);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
index 6dba967..a9a1f93 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -76,7 +76,7 @@
var apiconfig = new SoundTrigger.RecognitionConfig.Builder()
.setCaptureRequested(true)
- .setAllowMultipleTriggers(false) // must be false
+ .setMultipleTriggersAllowed(false) // must be false
.setKeyphrases(keyphrases)
.setData(data)
.setAudioCapabilities(flags)
diff --git a/services/tests/wmtests/res/xml/bookmarks.xml b/services/tests/wmtests/res/xml/bookmarks.xml
index 3fc7c76..787f4e8 100644
--- a/services/tests/wmtests/res/xml/bookmarks.xml
+++ b/services/tests/wmtests/res/xml/bookmarks.xml
@@ -22,7 +22,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_P"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -30,13 +30,21 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_K"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
+ category="android.intent.category.APP_MUSIC"
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
+ <bookmark
+ role="android.app.role.SMS"
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
+ <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
index 35b077e..cf5323e 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -26,6 +26,7 @@
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.KeyEvent.KEYCODE_H;
import static android.view.KeyEvent.KEYCODE_J;
+import static android.view.KeyEvent.KEYCODE_K;
import static android.view.KeyEvent.KEYCODE_M;
import static android.view.KeyEvent.KEYCODE_META_LEFT;
import static android.view.KeyEvent.KEYCODE_N;
@@ -66,12 +67,14 @@
// These shortcuts should align with those defined in
// services/tests/wmtests/res/xml/bookmarks.xml
INTENT_SHORTCUTS.append(KEYCODE_U, Intent.CATEGORY_APP_CALCULATOR);
- INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_CONTACTS);
+ INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
INTENT_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
- INTENT_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CALENDAR);
+ INTENT_SHORTCUTS.append(KEYCODE_K, Intent.CATEGORY_APP_CALENDAR);
INTENT_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
+ INTENT_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
ROLE_SHORTCUTS.append(KEYCODE_B, RoleManager.ROLE_BROWSER);
+ ROLE_SHORTCUTS.append(KEYCODE_S, RoleManager.ROLE_SMS);
}
private static final int ANY_DISPLAY_ID = 123;
@@ -106,7 +109,7 @@
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_B}, 0);
mPhoneWindowManager.assertLaunchRole(RoleManager.ROLE_BROWSER);
- sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_P}, 0);
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_C}, 0);
mPhoneWindowManager.assertLaunchCategory(Intent.CATEGORY_APP_CONTACTS);
sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SHIFT_LEFT, KEYCODE_J}, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index fee646d..d4a921c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2670,7 +2670,7 @@
assertSetOrientation(Build.VERSION_CODES.CUR_DEVELOPMENT, CATEGORY_GAME, true);
// Blanket application, also ignoring Target SDK
- mWm.mConstants.mIgnoreActivityOrientationRequest = true;
+ mWm.mConstants.mIgnoreActivityOrientationRequestLargeScreen = true;
assertSetOrientation(Build.VERSION_CODES.VANILLA_ICE_CREAM, CATEGORY_SOCIAL, false);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
index 50c2c2f..a5b2cb3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatUtilsTest.java
@@ -29,6 +29,7 @@
import android.app.CameraCompatTaskInfo.FreeformCameraCompatMode;
import android.app.TaskInfo;
+import android.graphics.Rect;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
@@ -198,6 +199,34 @@
});
}
+ @Test
+ public void testTopActivityLetterboxed_hasBounds() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.checkTopActivityInSizeCompatMode(/* inScm */ false);
+ a.setIgnoreOrientationRequest(true);
+ a.configureTopActivityBounds(new Rect(20, 30, 520, 630));
+ });
+ robot.setIsLetterboxedForAspectRatioOnly(/* forAspectRatio */ true);
+
+
+ robot.checkTaskInfoTopActivityHasBounds(/* expected */ new Rect(20, 30, 520, 630));
+ });
+ }
+
+ @Test
+ public void testTopActivityNotLetterboxed_hasNoBounds() {
+ runTestScenario((robot) -> {
+ robot.applyOnActivity((a) -> {
+ a.createActivityWithComponent();
+ a.setIgnoreOrientationRequest(true);
+ });
+
+ robot.checkTaskInfoTopActivityHasBounds(/* expected */ null);
+ });
+ }
+
/**
* Runs a test scenario providing a Robot.
*/
@@ -282,6 +311,11 @@
.cameraCompatTaskInfo.freeformCameraCompatMode);
}
+ void checkTaskInfoTopActivityHasBounds(Rect bounds) {
+ Assert.assertEquals(bounds, getTopTaskInfo().appCompatTaskInfo
+ .topActivityLetterboxBounds);
+ }
+
void setCameraCompatTreatmentEnabledForActivity(boolean enabled) {
doReturn(enabled).when(activity().displayContent().mAppCompatCameraPolicy
.mCameraCompatFreeformPolicy).isTreatmentEnabledForActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 87db6c0..bf96f0eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4892,7 +4892,8 @@
@Test
public void testUniversalResizeable() {
- mWm.mConstants.mIgnoreActivityOrientationRequest = true;
+ mWm.mConstants.mIgnoreActivityOrientationRequestSmallScreen = true;
+ mWm.mConstants.mIgnoreActivityOrientationRequestLargeScreen = true;
setUpApp(mDisplayContent);
final float maxAspect = 1.8f;
final float minAspect = 1.5f;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 757c358..78e6cbf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1027,7 +1027,8 @@
}
@Override
- public void setImeInputTargetRequestedVisibility(boolean visible) {
+ public void setImeInputTargetRequestedVisibility(boolean visible,
+ @NonNull ImeTracker.Token statsToken) {
}
};
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index fb031bd..01ff674 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -842,7 +842,7 @@
return;
}
- model.setRequested(config.isAllowMultipleTriggers());
+ model.setRequested(config.isMultipleTriggersAllowed());
// TODO: Remove this block if the lower layer supports multiple triggers.
if (model.isRequested()) {
updateRecognitionLocked(model, true);
@@ -964,7 +964,7 @@
RecognitionConfig config = modelData.getRecognitionConfig();
if (config != null) {
// Whether we should continue by starting this again.
- modelData.setRequested(config.isAllowMultipleTriggers());
+ modelData.setRequested(config.isMultipleTriggersAllowed());
}
// TODO: Remove this block if the lower layer supports multiple triggers.
if (modelData.isRequested()) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 19a6ddc..e0cdbdd 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -1445,7 +1445,7 @@
runOrAddOperation(new Operation(
// always execute:
() -> {
- if (!mRecognitionConfig.isAllowMultipleTriggers()) {
+ if (!mRecognitionConfig.isMultipleTriggersAllowed()) {
// Unregister this remoteService once op is done
synchronized (mCallbacksLock) {
mCallbacks.remove(mPuuid.getUuid());
diff --git a/telephony/java/android/telephony/satellite/EarfcnRange.java b/telephony/java/android/telephony/satellite/EarfcnRange.java
index 207b25d6..81c8ed3 100644
--- a/telephony/java/android/telephony/satellite/EarfcnRange.java
+++ b/telephony/java/android/telephony/satellite/EarfcnRange.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -40,7 +41,8 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class EarfcnRange implements Parcelable {
/**
@@ -74,6 +76,7 @@
*
* @param startEarfcn The starting earfcn value.
* @param endEarfcn The ending earfcn value.
+ * @hide
*/
public EarfcnRange(@IntRange(from = 0, to = 65535) int startEarfcn,
@IntRange(from = 0, to = 65535) int endEarfcn) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
index c1a6ae8..56c92d0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
+++ b/telephony/java/android/telephony/satellite/SatelliteAccessConfiguration.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +35,8 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteAccessConfiguration implements Parcelable {
/**
* The list of satellites available at the current location.
@@ -54,6 +56,7 @@
* @param satelliteInfos The list of {@link SatelliteInfo} objects representing the satellites
* accessible with this configuration.
* @param tagIdList The list of tag IDs associated with this configuration.
+ * @hide
*/
public SatelliteAccessConfiguration(@NonNull List<SatelliteInfo> satelliteInfos,
@NonNull List<Integer> tagIdList) {
@@ -61,12 +64,18 @@
mTagIdList = tagIdList;
}
+ /**
+ * Constructor for {@link SatelliteAccessConfiguration}.
+ * @param in parcel used to create {@link SatelliteAccessConfiguration} object
+ * @hide
+ */
public SatelliteAccessConfiguration(Parcel in) {
mSatelliteInfoList = in.createTypedArrayList(SatelliteInfo.CREATOR);
mTagIdList = new ArrayList<>();
in.readList(mTagIdList, Integer.class.getClassLoader(), Integer.class);
}
+ @NonNull
public static final Creator<SatelliteAccessConfiguration> CREATOR =
new Creator<SatelliteAccessConfiguration>() {
@Override
diff --git a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
index bffb11f..6291102 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
@@ -18,6 +18,8 @@
import android.annotation.FlaggedApi;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
import com.android.internal.telephony.flags.Flags;
@@ -27,19 +29,19 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteCommunicationAllowedStateCallback {
/**
* Telephony does not guarantee that whenever there is a change in communication allowed state,
* this API will be called. Telephony does its best to detect the changes and notify its
- * listeners accordingly.
+ * listeners accordingly. Satellite communication is allowed at a location when it is legally
+ * allowed by the local authority and satellite signal coverage is available.
*
- * @param isAllowed {@code true} means satellite allow state is changed,
- * {@code false} satellite allow state is not changed
- * @hide
+ * @param isAllowed {@code true} means satellite is allowed,
+ * {@code false} satellite is not allowed.
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed);
/**
@@ -49,9 +51,7 @@
* the current location. When satellite is not allowed at
* the current location,
* {@code satelliteRegionalConfiguration} will be null.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
default void onSatelliteAccessConfigurationChanged(
@Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {};
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteInfo.java b/telephony/java/android/telephony/satellite/SatelliteInfo.java
index 1d5f613..ebb4e9c 100644
--- a/telephony/java/android/telephony/satellite/SatelliteInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteInfo.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
@@ -36,8 +37,9 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatelliteInfo implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatelliteInfo implements Parcelable {
/**
* Unique identification number for the satellite.
* This ID is used to distinguish between different satellites in the network.
@@ -68,6 +70,9 @@
*/
private final List<EarfcnRange> mEarfcnRangeList;
+ /**
+ * @hide
+ */
protected SatelliteInfo(Parcel in) {
ParcelUuid parcelUuid = in.readParcelable(
ParcelUuid.class.getClassLoader(), ParcelUuid.class);
@@ -89,6 +94,7 @@
* @param bandList The list of frequency bandList supported by the satellite.
* @param earfcnRanges The list of {@link EarfcnRange} objects representing the EARFCN
* ranges supported by the satellite.
+ * @hide
*/
public SatelliteInfo(@NonNull UUID satelliteId, @NonNull SatellitePosition satellitePosition,
@NonNull List<Integer> bandList, @NonNull List<EarfcnRange> earfcnRanges) {
@@ -98,6 +104,7 @@
mEarfcnRangeList = earfcnRanges;
}
+ @NonNull
public static final Creator<SatelliteInfo> CREATOR = new Creator<SatelliteInfo>() {
@Override
public SatelliteInfo createFromParcel(Parcel in) {
@@ -135,6 +142,10 @@
/**
* Returns the position of the satellite.
+ * Position information of a geostationary satellite.
+ * This includes the longitude and altitude of the satellite.
+ * If the SatellitePosition is invalid,
+ * longitudeDegree and altitudeKm will be represented as DOUBLE.NaN.
*
* @return The {@link SatellitePosition} of the satellite.
*/
@@ -146,6 +157,8 @@
/**
* Returns the list of frequency bands supported by the satellite.
*
+ * Refer specification 3GPP TS 36.101 for detailed information on frequency bands.
+ *
* @return The list of frequency bands.
*/
@NonNull
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 5a34b00..db5689b7 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -221,6 +221,14 @@
/**
* Bundle key to get the response from
+ * {@link #requestSessionStats(Executor, OutcomeReceiver)}.
+ * @hide
+ */
+
+ public static final String KEY_SESSION_STATS_V2 = "session_stats_v2";
+
+ /**
+ * Bundle key to get the response from
* {@link #requestIsProvisioned(Executor, OutcomeReceiver)}.
* @hide
*/
@@ -747,6 +755,8 @@
* config_satellite_gateway_service_package
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String ACTION_SATELLITE_SUBSCRIBER_ID_LIST_CHANGED =
"android.telephony.satellite.action.SATELLITE_SUBSCRIBER_ID_LIST_CHANGED";
@@ -758,6 +768,8 @@
* config_satellite_gateway_service_package
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String ACTION_SATELLITE_START_NON_EMERGENCY_SESSION =
"android.telephony.satellite.action.SATELLITE_START_NON_EMERGENCY_SESSION";
/**
@@ -773,6 +785,8 @@
* }
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public static final String METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT =
"android.telephony.METADATA_SATELLITE_MANUAL_CONNECT_P2P_SUPPORT";
@@ -3274,13 +3288,14 @@
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle the satellite supoprted state changed event.
*
- * @return The {@link SatelliteResult} result of the operation.
+ * @return The result of the operation.
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- *
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult public int registerForSupportedStateChanged(
@NonNull @CallbackExecutor Executor executor,
@@ -3322,11 +3337,11 @@
*
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
- *
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForSupportedStateChanged(
@NonNull SatelliteSupportedStateCallback callback) {
Objects.requireNonNull(callback);
@@ -3355,11 +3370,13 @@
*
* @param executor The executor on which the callback will be called.
* @param callback The callback to handle satellite communication allowed state changed event.
- * @return The {@link SatelliteResult} result of the operation.
+ * @return The result of the operation.
* @throws SecurityException if the caller doesn't have required permission.
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@SatelliteResult
public int registerForCommunicationAllowedStateChanged(
@@ -3414,8 +3431,9 @@
* @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void unregisterForCommunicationAllowedStateChanged(
@NonNull SatelliteCommunicationAllowedStateCallback callback) {
Objects.requireNonNull(callback);
@@ -3455,7 +3473,6 @@
*/
@RequiresPermission(allOf = {Manifest.permission.PACKAGE_USAGE_STATS,
Manifest.permission.MODIFY_PHONE_STATE})
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
public void requestSessionStats(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteSessionStats, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -3468,21 +3485,33 @@
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == SATELLITE_RESULT_SUCCESS) {
+ SatelliteSessionStats stats;
if (resultData.containsKey(KEY_SESSION_STATS)) {
- SatelliteSessionStats stats =
- resultData.getParcelable(KEY_SESSION_STATS,
- SatelliteSessionStats.class);
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onResult(stats)));
+ stats = resultData.getParcelable(KEY_SESSION_STATS,
+ SatelliteSessionStats.class);
+ if (resultData.containsKey(KEY_SESSION_STATS_V2)) {
+ SatelliteSessionStats stats1 = resultData.getParcelable(
+ KEY_SESSION_STATS_V2, SatelliteSessionStats.class);
+ if (stats != null && stats1 != null) {
+ stats.setSatelliteSessionStats(
+ stats1.getSatelliteSessionStats());
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onResult(stats)));
+ return;
+ }
+ } else {
+ loge("KEY_SESSION_STATS_V2 does not exist.");
+ }
} else {
loge("KEY_SESSION_STATS does not exist.");
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onError(new SatelliteException(
- SATELLITE_RESULT_REQUEST_FAILED))));
}
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onError(new SatelliteException(
+ SATELLITE_RESULT_REQUEST_FAILED))));
+
} else {
- executor.execute(() -> Binder.withCleanCallingIdentity(() ->
- callback.onError(new SatelliteException(resultCode))));
+ executor.execute(() -> Binder.withCleanCallingIdentity(
+ () -> callback.onError(new SatelliteException(resultCode))));
}
}
};
@@ -3515,8 +3544,9 @@
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void requestSatelliteSubscriberProvisionStatus(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<List<SatelliteSubscriberProvisionStatus>,
@@ -3573,8 +3603,9 @@
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -3628,8 +3659,9 @@
* @throws SecurityException if the caller doesn't have required permission.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
index 5e56f84..d7fa3c9 100644
--- a/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
+++ b/telephony/java/android/telephony/satellite/SatelliteModemEnableRequestAttributes.java
@@ -16,10 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -28,6 +32,8 @@
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteModemEnableRequestAttributes implements Parcelable {
/** {@code true} to enable satellite and {@code false} to disable satellite */
@@ -47,6 +53,9 @@
/** The subscription related info */
@NonNull private final SatelliteSubscriptionInfo mSatelliteSubscriptionInfo;
+ /**
+ * @hide
+ */
public SatelliteModemEnableRequestAttributes(boolean isEnabled, boolean isDemoMode,
boolean isEmergencyMode, @NonNull SatelliteSubscriptionInfo satelliteSubscriptionInfo) {
mIsEnabled = isEnabled;
@@ -76,6 +85,7 @@
mSatelliteSubscriptionInfo.writeToParcel(dest, flags);
}
+ @NonNull
public static final Creator<SatelliteModemEnableRequestAttributes> CREATOR = new Creator<>() {
@Override
public SatelliteModemEnableRequestAttributes createFromParcel(Parcel in) {
@@ -114,19 +124,39 @@
return Objects.hash(mIsEnabled, mIsDemoMode, mIsEmergencyMode, mSatelliteSubscriptionInfo);
}
+
+ /**
+ * Get whether satellite modem needs to be enabled or disabled.
+ * @return {@code true} if the request is to enable satellite, else {@code false} to disable
+ * satellite.
+ */
public boolean isEnabled() {
return mIsEnabled;
}
+ /**
+ * Get whether satellite modem is enabled for demo mode.
+ * @return {@code true} if the request is to enable demo mode, else {@code false}.
+ */
public boolean isDemoMode() {
return mIsDemoMode;
}
+ /**
+ * Get whether satellite modem is enabled for emergency mode.
+ * @return {@code true} if the request is to enable satellite for emergency mode,
+ * else {@code false}.
+ */
public boolean isEmergencyMode() {
return mIsEmergencyMode;
}
- @NonNull public SatelliteSubscriptionInfo getSatelliteSubscriptionInfo() {
+
+ /**
+ * Return subscription info related to satellite.
+ */
+ @NonNull
+ public SatelliteSubscriptionInfo getSatelliteSubscriptionInfo() {
return mSatelliteSubscriptionInfo;
}
}
diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java
index dd463e0..b8d138f 100644
--- a/telephony/java/android/telephony/satellite/SatellitePosition.java
+++ b/telephony/java/android/telephony/satellite/SatellitePosition.java
@@ -16,6 +16,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,8 +35,9 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatellitePosition implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatellitePosition implements Parcelable {
/**
* The longitude of the satellite in degrees, ranging from -180 to 180 degrees
@@ -51,6 +53,7 @@
* Constructor for {@link SatellitePosition} used to create an instance from a {@link Parcel}.
*
* @param in The {@link Parcel} to read the satellite position data from.
+ * @hide
*/
public SatellitePosition(Parcel in) {
mLongitudeDegree = in.readDouble();
@@ -62,12 +65,14 @@
*
* @param longitudeDegree The longitude of the satellite in degrees.
* @param altitudeKm The altitude of the satellite in kilometers.
+ * @hide
*/
public SatellitePosition(double longitudeDegree, double altitudeKm) {
mLongitudeDegree = longitudeDegree;
mAltitudeKm = altitudeKm;
}
+ @NonNull
public static final Creator<SatellitePosition> CREATOR = new Creator<SatellitePosition>() {
@Override
public SatellitePosition createFromParcel(Parcel in) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index e8ae0f5..6b95eb3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -48,9 +48,8 @@
*
* @param satelliteSubscriberProvisionStatus The List contains the latest provisioning states
* of the SatelliteSubscriberInfos.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
default void onSatelliteSubscriptionProvisionStateChanged(
@NonNull List<SatelliteSubscriberProvisionStatus>
satelliteSubscriberProvisionStatus) {};
diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
index aabb058..0cdba83 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
@@ -19,23 +19,37 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Log;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/**
* SatelliteSessionStats is used to represent the usage stats of the satellite service.
+ *
* @hide
*/
-public class SatelliteSessionStats implements Parcelable {
+public final class SatelliteSessionStats implements Parcelable {
private int mCountOfSuccessfulUserMessages;
private int mCountOfUnsuccessfulUserMessages;
private int mCountOfTimedOutUserMessagesWaitingForConnection;
private int mCountOfTimedOutUserMessagesWaitingForAck;
private int mCountOfUserMessagesInQueueToBeSent;
+ private long mLatencyOfSuccessfulUserMessages;
+
+ private Map<Integer, SatelliteSessionStats> datagramStats;
+ private long mMaxLatency;
+ private long mLastMessageLatency;
+
+ public SatelliteSessionStats() {
+ this.datagramStats = new HashMap<>();
+ }
/**
* SatelliteSessionStats constructor
- * @param builder Builder to create SatelliteSessionStats object/
+ *
+ * @param builder Builder to create SatelliteSessionStats object/
*/
public SatelliteSessionStats(@NonNull Builder builder) {
mCountOfSuccessfulUserMessages = builder.mCountOfSuccessfulUserMessages;
@@ -45,6 +59,7 @@
mCountOfTimedOutUserMessagesWaitingForAck =
builder.mCountOfTimedOutUserMessagesWaitingForAck;
mCountOfUserMessagesInQueueToBeSent = builder.mCountOfUserMessagesInQueueToBeSent;
+ mLatencyOfSuccessfulUserMessages = builder.mLatencyOfSuccessfulUserMessages;
}
private SatelliteSessionStats(Parcel in) {
@@ -63,6 +78,19 @@
out.writeInt(mCountOfTimedOutUserMessagesWaitingForConnection);
out.writeInt(mCountOfTimedOutUserMessagesWaitingForAck);
out.writeInt(mCountOfUserMessagesInQueueToBeSent);
+ out.writeLong(mLatencyOfSuccessfulUserMessages);
+ out.writeLong(mMaxLatency);
+ out.writeLong(mLastMessageLatency);
+
+ if (datagramStats != null && !datagramStats.isEmpty()) {
+ out.writeInt(datagramStats.size());
+ for (Map.Entry<Integer, SatelliteSessionStats> entry : datagramStats.entrySet()) {
+ out.writeInt(entry.getKey());
+ out.writeParcelable(entry.getValue(), flags);
+ }
+ } else {
+ out.writeInt(0);
+ }
}
@NonNull
@@ -83,6 +111,40 @@
@NonNull
public String toString() {
StringBuilder sb = new StringBuilder();
+ if (datagramStats != null) {
+ sb.append(" ====== SatelliteSessionStatsWrapper Info =============");
+ for (Map.Entry<Integer, SatelliteSessionStats> entry : datagramStats.entrySet()) {
+ Integer key = entry.getKey();
+ SatelliteSessionStats value = entry.getValue();
+ sb.append("\n");
+ sb.append("Key:");
+ sb.append(key);
+ sb.append(", SatelliteSessionStats:[");
+ value.getPrintableCounters(sb);
+ sb.append(",");
+ sb.append(" LatencyOfSuccessfulUserMessages:");
+ sb.append(value.mLatencyOfSuccessfulUserMessages);
+ sb.append(",");
+ sb.append(" mMaxLatency:");
+ sb.append(value.mMaxLatency);
+ sb.append(",");
+ sb.append(" mLastMessageLatency:");
+ sb.append(value.mLastMessageLatency);
+ sb.append("]");
+ sb.append("\n");
+ }
+ sb.append(" ============== ================== ===============");
+ sb.append("\n");
+ sb.append("\n");
+ } else {
+ sb.append("\n");
+ getPrintableCounters(sb);
+ }
+ sb.append("\n");
+ return sb.toString();
+ }
+
+ private void getPrintableCounters(StringBuilder sb) {
sb.append("countOfSuccessfulUserMessages:");
sb.append(mCountOfSuccessfulUserMessages);
sb.append(",");
@@ -101,7 +163,6 @@
sb.append("countOfUserMessagesInQueueToBeSent:");
sb.append(mCountOfUserMessagesInQueueToBeSent);
- return sb.toString();
}
@Override
@@ -110,49 +171,176 @@
if (o == null || getClass() != o.getClass()) return false;
SatelliteSessionStats that = (SatelliteSessionStats) o;
return mCountOfSuccessfulUserMessages == that.mCountOfSuccessfulUserMessages
+ && mLatencyOfSuccessfulUserMessages == that.mLatencyOfSuccessfulUserMessages
&& mCountOfUnsuccessfulUserMessages == that.mCountOfUnsuccessfulUserMessages
&& mCountOfTimedOutUserMessagesWaitingForConnection
== that.mCountOfTimedOutUserMessagesWaitingForConnection
&& mCountOfTimedOutUserMessagesWaitingForAck
== that.mCountOfTimedOutUserMessagesWaitingForAck
- && mCountOfUserMessagesInQueueToBeSent
- == that.mCountOfUserMessagesInQueueToBeSent;
+ && mCountOfUserMessagesInQueueToBeSent == that.mCountOfUserMessagesInQueueToBeSent;
}
@Override
public int hashCode() {
- return Objects.hash(mCountOfSuccessfulUserMessages, mCountOfUnsuccessfulUserMessages,
- mCountOfTimedOutUserMessagesWaitingForConnection,
- mCountOfTimedOutUserMessagesWaitingForAck,
- mCountOfUserMessagesInQueueToBeSent);
+ return Objects.hash(mCountOfSuccessfulUserMessages, mLatencyOfSuccessfulUserMessages,
+ mCountOfUnsuccessfulUserMessages, mCountOfTimedOutUserMessagesWaitingForConnection,
+ mCountOfTimedOutUserMessagesWaitingForAck, mCountOfUserMessagesInQueueToBeSent);
}
public int getCountOfSuccessfulUserMessages() {
return mCountOfSuccessfulUserMessages;
}
+ public void incrementSuccessfulUserMessageCount() {
+ mCountOfSuccessfulUserMessages++;
+ }
+
public int getCountOfUnsuccessfulUserMessages() {
return mCountOfUnsuccessfulUserMessages;
}
+ public void incrementUnsuccessfulUserMessageCount() {
+ mCountOfUnsuccessfulUserMessages++;
+ }
+
public int getCountOfTimedOutUserMessagesWaitingForConnection() {
return mCountOfTimedOutUserMessagesWaitingForConnection;
}
+ public void incrementTimedOutUserMessagesWaitingForConnection() {
+ mCountOfTimedOutUserMessagesWaitingForConnection++;
+ }
+
public int getCountOfTimedOutUserMessagesWaitingForAck() {
return mCountOfTimedOutUserMessagesWaitingForAck;
}
+ public void incrementTimedOutUserMessagesWaitingForAck() {
+ mCountOfTimedOutUserMessagesWaitingForAck++;
+ }
+
public int getCountOfUserMessagesInQueueToBeSent() {
return mCountOfUserMessagesInQueueToBeSent;
}
+ public long getLatencyOfAllSuccessfulUserMessages() {
+ return mLatencyOfSuccessfulUserMessages;
+ }
+
+ public void updateLatencyOfAllSuccessfulUserMessages(long messageLatency) {
+ mLatencyOfSuccessfulUserMessages += messageLatency;
+ }
+
+ public void recordSuccessfulOutgoingDatagramStats(
+ @SatelliteManager.DatagramType int datagramType, long latency) {
+ try {
+ datagramStats.putIfAbsent(datagramType, new SatelliteSessionStats.Builder().build());
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ data.incrementSuccessfulUserMessageCount();
+ if (data.mMaxLatency < latency) {
+ data.mMaxLatency = latency;
+ }
+ data.mLastMessageLatency = latency;
+ data.updateLatencyOfAllSuccessfulUserMessages(latency);
+ } catch (Exception e) {
+ Log.e("SatelliteSessionStats",
+ "Error while recordSuccessfulOutgoingDatagramStats: " + e.getMessage());
+ }
+ }
+
+ public int getCountOfSuccessfulOutgoingDatagram(
+ @SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.getOrDefault(datagramType,
+ new SatelliteSessionStats());
+ return data.getCountOfSuccessfulUserMessages();
+ }
+
+ public long getMaxLatency() {
+ return this.mMaxLatency;
+ }
+
+ public Long getLatencyOfAllSuccessfulUserMessages(
+ @SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.getOrDefault(datagramType,
+ new SatelliteSessionStats());
+ return data.getLatencyOfAllSuccessfulUserMessages();
+ }
+
+
+ public long getLastMessageLatency() {
+ return this.mLastMessageLatency;
+ }
+
+ public void addCountOfUnsuccessfulUserMessages(@SatelliteManager.DatagramType int datagramType,
+ @SatelliteManager.SatelliteResult int resultCode) {
+ try {
+ datagramStats.putIfAbsent(datagramType, new SatelliteSessionStats.Builder().build());
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ data.incrementUnsuccessfulUserMessageCount();
+ if (resultCode == SatelliteManager.SATELLITE_RESULT_NOT_REACHABLE) {
+ data.incrementTimedOutUserMessagesWaitingForConnection();
+ } else if (resultCode == SatelliteManager.SATELLITE_RESULT_MODEM_TIMEOUT) {
+ data.incrementTimedOutUserMessagesWaitingForAck();
+ }
+ } catch (Exception e) {
+ Log.e("SatelliteSessionStats",
+ "Error while addCountOfUnsuccessfulUserMessages: " + e.getMessage());
+ }
+ }
+
+ public int getCountOfUnsuccessfulUserMessages(@SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ return data.getCountOfUnsuccessfulUserMessages();
+ }
+
+ public int getCountOfTimedOutUserMessagesWaitingForConnection(
+ @SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ return data.getCountOfTimedOutUserMessagesWaitingForConnection();
+ }
+
+ public int getCountOfTimedOutUserMessagesWaitingForAck(
+ @SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ return data.getCountOfTimedOutUserMessagesWaitingForAck();
+ }
+
+ public int getCountOfUserMessagesInQueueToBeSent(
+ @SatelliteManager.DatagramType int datagramType) {
+ SatelliteSessionStats data = datagramStats.get(datagramType);
+ return data.getCountOfUserMessagesInQueueToBeSent();
+ }
+
+ public void clear() {
+ datagramStats.clear();
+ }
+
+ public Map<Integer, SatelliteSessionStats> getSatelliteSessionStats() {
+ return datagramStats;
+ }
+
+ public void setSatelliteSessionStats(Map<Integer, SatelliteSessionStats> sessionStats) {
+ this.datagramStats = sessionStats;
+ }
+
private void readFromParcel(Parcel in) {
mCountOfSuccessfulUserMessages = in.readInt();
mCountOfUnsuccessfulUserMessages = in.readInt();
mCountOfTimedOutUserMessagesWaitingForConnection = in.readInt();
mCountOfTimedOutUserMessagesWaitingForAck = in.readInt();
mCountOfUserMessagesInQueueToBeSent = in.readInt();
+ mLatencyOfSuccessfulUserMessages = in.readLong();
+ mMaxLatency = in.readLong();
+ mLastMessageLatency = in.readLong();
+
+ int size = in.readInt();
+ datagramStats = new HashMap<>();
+ for (int i = 0; i < size; i++) {
+ Integer key = in.readInt();
+ SatelliteSessionStats value = in.readParcelable(
+ SatelliteSessionStats.class.getClassLoader());
+ datagramStats.put(key, value);
+ }
}
/**
@@ -164,7 +352,10 @@
private int mCountOfTimedOutUserMessagesWaitingForConnection;
private int mCountOfTimedOutUserMessagesWaitingForAck;
private int mCountOfUserMessagesInQueueToBeSent;
+ private long mLatencyOfSuccessfulUserMessages;
+ private long mMaxLatency;
+ private long mLastMessageLatency;
/**
* Sets countOfSuccessfulUserMessages value of {@link SatelliteSessionStats}
* and then returns the Builder class.
@@ -215,10 +406,28 @@
return this;
}
+ @NonNull
+ public Builder setLatencyOfSuccessfulUserMessages(long latency) {
+ mLatencyOfSuccessfulUserMessages = latency;
+ return this;
+ }
+
+ @NonNull
+ public Builder setMaxLatency(long maxLatency) {
+ mMaxLatency = maxLatency;
+ return this;
+ }
+
+ @NonNull
+ public Builder setLastLatency(long lastLatency) {
+ mLastMessageLatency = lastLatency;
+ return this;
+ }
+
/** Returns SatelliteSessionStats object. */
@NonNull
public SatelliteSessionStats build() {
return new SatelliteSessionStats(this);
}
}
-}
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index dbe5ddd..8427057 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -19,6 +19,7 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -37,7 +38,8 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteSubscriberInfo implements Parcelable {
/** provision subscriberId */
@NonNull
@@ -50,10 +52,8 @@
private int mSubId;
/** SubscriberId format is the ICCID. */
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final int ICCID = 0;
/** SubscriberId format is the 6 digit of IMSI + MSISDN. */
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final int IMSI_MSISDN = 1;
/** Type of subscriber id */
@@ -70,6 +70,9 @@
readFromParcel(in);
}
+ /**
+ * @hide
+ */
public SatelliteSubscriberInfo(@NonNull Builder builder) {
this.mSubscriberId = builder.mSubscriberId;
this.mCarrierId = builder.mCarrierId;
@@ -80,11 +83,8 @@
/**
* Builder class for constructing SatelliteSubscriberInfo objects
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static class Builder {
+ public static final class Builder {
@NonNull private String mSubscriberId;
private int mCarrierId;
@NonNull
@@ -95,17 +95,15 @@
/**
* Set the SubscriberId and returns the Builder class.
- *
- * @hide
*/
- public Builder setSubscriberId(String subscriberId) {
+ @NonNull
+ public Builder setSubscriberId(@NonNull String subscriberId) {
mSubscriberId = subscriberId;
return this;
}
/**
* Set the CarrierId and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setCarrierId(int carrierId) {
@@ -114,18 +112,19 @@
}
/**
- * Set the niddApn and returns the Builder class.
- * @hide
+ * Set NIDD (Non IP Data) APN can be used for carrier roaming to satellite attachment
+ * and returns the Builder class.
+ *
+ * Refer specification 3GPP TS 23.501 V19.1.0 section 5.31.5
*/
@NonNull
- public Builder setNiddApn(String niddApn) {
+ public Builder setNiddApn(@NonNull String niddApn) {
mNiddApn = niddApn;
return this;
}
/**
* Set the subId and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setSubId(int subId) {
@@ -135,7 +134,6 @@
/**
* Set the SubscriberIdType and returns the Builder class.
- * @hide
*/
@NonNull
public Builder setSubscriberIdType(@SubscriberIdType int subscriberIdType) {
@@ -145,7 +143,6 @@
/**
* Returns SatelliteSubscriberInfo object.
- * @hide
*/
@NonNull
public SatelliteSubscriberInfo build() {
@@ -153,11 +150,7 @@
}
}
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(mSubscriberId);
out.writeInt(mCarrierId);
@@ -166,7 +159,7 @@
out.writeInt(mSubscriberIdType);
}
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+
public static final @android.annotation.NonNull Creator<SatelliteSubscriberInfo> CREATOR =
new Creator<SatelliteSubscriberInfo>() {
@Override
@@ -180,56 +173,45 @@
}
};
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int describeContents() {
return 0;
}
/**
- * @return provision subscriberId.
- * @hide
+ * Return subscriberId which is used to register with satellite gateway service
+ * during provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @NonNull
public String getSubscriberId() {
return mSubscriberId;
}
/**
- * @return carrierId.
- * @hide
+ * Return carrierId of the subscription used for provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int getCarrierId() {
return mCarrierId;
}
/**
- * @return niddApn.
- * @hide
+ * Return the NIDD(Non IP Data) APN which is used for carrier roaming to satellite attachment.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
+ @NonNull
public String getNiddApn() {
return mNiddApn;
}
/**
- * @return subId.
- * @hide
+ * Return the subscriptionId of the subscription which is used for satellite attachment.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int getSubId() {
return mSubId;
}
/**
- * @return subscriberIdType.
- * @hide
+ * Return the type of subscriberId used for provisioning.
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public @SubscriberIdType int getSubscriberIdType() {
return mSubscriberIdType;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
index 08ef3f2..fb4f89d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberProvisionStatus.java
@@ -18,6 +18,7 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,49 +31,49 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
-public class SatelliteSubscriberProvisionStatus implements Parcelable {
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+public final class SatelliteSubscriberProvisionStatus implements Parcelable {
private SatelliteSubscriberInfo mSubscriberInfo;
/** {@code true} mean the satellite subscriber is provisioned, {@code false} otherwise. */
- private boolean mProvisionStatus;
+ private boolean mProvisioned;
+ /**
+ * @hide
+ */
public SatelliteSubscriberProvisionStatus(@NonNull Builder builder) {
mSubscriberInfo = builder.mSubscriberInfo;
- mProvisionStatus = builder.mProvisionStatus;
+ mProvisioned = builder.mProvisioned;
}
/**
* Builder class for constructing SatelliteSubscriberProvisionStatus objects
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public static class Builder {
+ public static final class Builder {
private SatelliteSubscriberInfo mSubscriberInfo;
- private boolean mProvisionStatus;
+ private boolean mProvisioned;
/**
* Set the SatelliteSubscriberInfo and returns the Builder class.
- * @hide
*/
- public Builder setSatelliteSubscriberInfo(SatelliteSubscriberInfo satelliteSubscriberInfo) {
+ @NonNull
+ public Builder setSatelliteSubscriberInfo(
+ @NonNull SatelliteSubscriberInfo satelliteSubscriberInfo) {
mSubscriberInfo = satelliteSubscriberInfo;
return this;
}
/**
* Set the SatelliteSubscriberInfo's provisionStatus and returns the Builder class.
- * @hide
*/
@NonNull
- public Builder setProvisionStatus(boolean provisionStatus) {
- mProvisionStatus = provisionStatus;
+ public Builder setProvisioned(boolean provisioned) {
+ mProvisioned = provisioned;
return this;
}
/**
* Returns SatelliteSubscriberProvisionStatus object.
- * @hide
*/
@NonNull
public SatelliteSubscriberProvisionStatus build() {
@@ -84,17 +85,12 @@
readFromParcel(in);
}
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(mSubscriberInfo, flags);
- out.writeBoolean(mProvisionStatus);
+ out.writeBoolean(mProvisioned);
}
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public static final @android.annotation.NonNull Creator<SatelliteSubscriberProvisionStatus>
CREATOR =
new Creator<SatelliteSubscriberProvisionStatus>() {
@@ -109,11 +105,7 @@
}
};
- /**
- * @hide
- */
@Override
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public int describeContents() {
return 0;
}
@@ -121,9 +113,7 @@
/**
* SatelliteSubscriberInfo that has a provisioning state.
* @return SatelliteSubscriberInfo.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
public @NonNull SatelliteSubscriberInfo getSatelliteSubscriberInfo() {
return mSubscriberInfo;
}
@@ -131,11 +121,9 @@
/**
* SatelliteSubscriberInfo's provisioning state.
* @return {@code true} means provisioning. {@code false} means deprovisioning.
- * @hide
*/
- @FlaggedApi(Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
- public @NonNull boolean getProvisionStatus() {
- return mProvisionStatus;
+ public boolean isProvisioned() {
+ return mProvisioned;
}
@NonNull
@@ -148,13 +136,13 @@
sb.append(",");
sb.append("ProvisionStatus:");
- sb.append(mProvisionStatus);
+ sb.append(mProvisioned);
return sb.toString();
}
@Override
public int hashCode() {
- return Objects.hash(mSubscriberInfo, mProvisionStatus);
+ return Objects.hash(mSubscriberInfo, mProvisioned);
}
@Override
@@ -163,12 +151,12 @@
if (!(o instanceof SatelliteSubscriberProvisionStatus)) return false;
SatelliteSubscriberProvisionStatus that = (SatelliteSubscriberProvisionStatus) o;
return Objects.equals(mSubscriberInfo, that.mSubscriberInfo)
- && mProvisionStatus == that.mProvisionStatus;
+ && mProvisioned == that.mProvisioned;
}
private void readFromParcel(Parcel in) {
mSubscriberInfo = in.readParcelable(SatelliteSubscriberInfo.class.getClassLoader(),
SatelliteSubscriberInfo.class);
- mProvisionStatus = in.readBoolean();
+ mProvisioned = in.readBoolean();
}
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
index 2ef19f8..1897a9b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriptionInfo.java
@@ -16,10 +16,14 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Objects;
/**
@@ -28,6 +32,8 @@
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SatelliteSubscriptionInfo implements Parcelable {
/**
* The ICC ID used for satellite attachment.
@@ -39,6 +45,9 @@
*/
@NonNull private final String mNiddApn;
+ /**
+ * @hide
+ */
public SatelliteSubscriptionInfo(@NonNull String iccId, @NonNull String niddApn) {
mIccId = iccId;
mNiddApn = niddApn;
@@ -60,7 +69,8 @@
dest.writeString8(mNiddApn);
}
- @NonNull public static final Creator<SatelliteSubscriptionInfo> CREATOR = new Creator<>() {
+ @NonNull
+ public static final Creator<SatelliteSubscriptionInfo> CREATOR = new Creator<>() {
@Override
public SatelliteSubscriptionInfo createFromParcel(Parcel in) {
return new SatelliteSubscriptionInfo(in);
@@ -94,11 +104,17 @@
return Objects.hash(getIccId(), getNiddApn());
}
+ /**
+ * Get ICC ID used for satellite attachment.
+ */
@NonNull
public String getIccId() {
return mIccId;
}
+ /**
+ * Get the NIDD(Non IP Data) APN to be used for carrier roaming to satellite attachment.
+ */
@NonNull
public String getNiddApn() {
return mNiddApn;
diff --git a/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
index 7e19bd1..5487eb6 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSupportedStateCallback.java
@@ -17,6 +17,7 @@
package android.telephony.satellite;
import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
import com.android.internal.telephony.flags.Flags;
@@ -25,16 +26,14 @@
*
* @hide
*/
-@FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public interface SatelliteSupportedStateCallback {
/**
* Called when satellite supported state changes.
*
* @param supported The new supported state. {@code true} means satellite is supported,
* {@code false} means satellite is not supported.
- *
- * @hide
*/
- @FlaggedApi(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
void onSatelliteSupportedStateChanged(boolean supported);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 046ae5f..b5dfb63 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -58,12 +58,11 @@
* @param state The new send datagram transfer state.
* @param sendPendingCount The number of datagrams that are currently being sent.
* @param errorCode If datagram transfer failed, the reason for failure.
- *
- * @hide
*/
- void onSendDatagramStateChanged(@SatelliteManager.DatagramType int datagramType,
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
+ default void onSendDatagramStateChanged(@SatelliteManager.DatagramType int datagramType,
@SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
- @SatelliteManager.SatelliteResult int errorCode);
+ @SatelliteManager.SatelliteResult int errorCode) {}
/**
* Called when satellite datagram receive state changed.
*
@@ -80,8 +79,7 @@
* Called when framework receives a request to send a datagram.
*
* @param datagramType The type of the requested datagram.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
default void onSendDatagramRequested(@SatelliteManager.DatagramType int datagramType) {}
}
diff --git a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
index c2ac44f..61e1e5f 100644
--- a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
+++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
@@ -16,19 +16,26 @@
package android.telephony.satellite;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.IntArray;
+import com.android.internal.telephony.flags.Flags;
+
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
public final class SystemSelectionSpecifier implements Parcelable {
/** Network plmn associated with channel information. */
@@ -188,26 +195,41 @@
return Objects.hash(mMccMnc, mBands, mEarfcns);
}
+ /** Return network plmn associated with channel information. */
@NonNull public String getMccMnc() {
return mMccMnc;
}
- @NonNull public IntArray getBands() {
- return mBands;
+ /**
+ * Return the frequency bands to scan.
+ * Maximum length of the vector is 8.
+ */
+ @NonNull public int[] getBands() {
+ return mBands.toArray();
}
- @NonNull public IntArray getEarfcns() {
- return mEarfcns;
+ /**
+ * Return the radio channels to scan as defined in 3GPP TS 25.101 and 36.101.
+ * Maximum length of the vector is 32.
+ */
+ @NonNull public int[] getEarfcns() {
+ return mEarfcns.toArray();
}
+ /** Return the list of satellites configured for the current location. */
@NonNull
- public SatelliteInfo[] getSatelliteInfos() {
- return mSatelliteInfos;
+ public List<SatelliteInfo> getSatelliteInfos() {
+ return Arrays.stream(mSatelliteInfos).toList();
}
+ /**
+ * Return the list of tag IDs associated with the current location
+ * Tag Ids are generic IDs an OEM can configure. Each tag ID can map to a region which can be
+ * used by OEM to identify proprietary configuration for that region.
+ */
@NonNull
- public IntArray getTagIds() {
- return mTagIds;
+ public int[] getTagIds() {
+ return mTagIds.toArray();
}
private void readFromParcel(Parcel in) {
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 8b65efd..685ae9a 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 3382c1e..5f92d7f 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index e941e79..1b90e99 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index 4e06dca..ffdbb02 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index 0cadd68..12670cd 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -47,6 +47,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index f32e8bed..e2ac5a9 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index 68ae4f1..1a4feb6 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index ec186723..481a8bb 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -45,6 +45,8 @@
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="test-user-token" value="%TEST_USER%"/>
<option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/>
+ <!-- Disable AOD -->
+ <option name="run-command" value="settings put secure doze_always_on 0"/>
<option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/>
<option name="run-command" value="settings put system show_touches 1"/>
<option name="run-command" value="settings put system pointer_location 1"/>
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
deleted file mode 100644
index eeee7b4..0000000
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/GestureHelper.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm.flicker.helpers;
-
-import android.annotation.NonNull;
-import android.app.Instrumentation;
-import android.app.UiAutomation;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.InputEvent;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-
-import androidx.annotation.Nullable;
-
-/**
- * Injects gestures given an {@link Instrumentation} object.
- */
-public class GestureHelper {
- // Inserted after each motion event injection.
- private static final int MOTION_EVENT_INJECTION_DELAY_MILLIS = 5;
-
- private final UiAutomation mUiAutomation;
-
- /**
- * Primary pointer should be cached here for separate release
- */
- @Nullable private PointerProperties mPrimaryPtrProp;
- @Nullable private PointerCoords mPrimaryPtrCoord;
- private long mPrimaryPtrDownTime;
-
- /**
- * A pair of floating point values.
- */
- public static class Tuple {
- public float x;
- public float y;
-
- public Tuple(float x, float y) {
- this.x = x;
- this.y = y;
- }
- }
-
- public GestureHelper(Instrumentation instrumentation) {
- mUiAutomation = instrumentation.getUiAutomation();
- }
-
- /**
- * Injects a series of {@link MotionEvent}s to simulate tapping.
- *
- * @param point coordinates of pointer to tap
- * @param times the number of times to tap
- */
- public boolean tap(@NonNull Tuple point, int times) throws InterruptedException {
- PointerProperties ptrProp = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
- PointerCoords ptrCoord = getPointerCoord(point.x, point.y, 1, 1);
-
- for (int i = 0; i <= times; i++) {
- // If already tapped, inject delay in between movements
- if (times > 0) {
- SystemClock.sleep(50L);
- }
- if (!primaryPointerDown(ptrProp, ptrCoord, SystemClock.uptimeMillis())) {
- return false;
- }
- // Delay before releasing tap
- SystemClock.sleep(100L);
- if (!primaryPointerUp(ptrProp, ptrCoord, SystemClock.uptimeMillis())) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Injects a series of {@link MotionEvent}s to simulate a drag gesture without pointer release.
- *
- * Simulates a drag gesture without releasing the primary pointer. The primary pointer info
- * will be cached for potential release later on by {@code releasePrimaryPointer()}
- *
- * @param startPoint initial coordinates of the primary pointer
- * @param endPoint final coordinates of the primary pointer
- * @param steps number of steps to take to animate dragging
- * @return true if gesture is injected successfully
- */
- public boolean dragWithoutRelease(@NonNull Tuple startPoint,
- @NonNull Tuple endPoint, int steps) {
- PointerProperties ptrProp = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
- PointerCoords ptrCoord = getPointerCoord(startPoint.x, startPoint.y, 1, 1);
-
- PointerProperties[] ptrProps = new PointerProperties[] { ptrProp };
- PointerCoords[] ptrCoords = new PointerCoords[] { ptrCoord };
-
- long downTime = SystemClock.uptimeMillis();
-
- if (!primaryPointerDown(ptrProp, ptrCoord, downTime)) {
- return false;
- }
-
- // cache the primary pointer info for later potential release
- mPrimaryPtrProp = ptrProp;
- mPrimaryPtrCoord = ptrCoord;
- mPrimaryPtrDownTime = downTime;
-
- return movePointers(ptrProps, ptrCoords, new Tuple[] { endPoint }, downTime, steps);
- }
-
- /**
- * Release primary pointer if previous gesture has cached the primary pointer info.
- *
- * @return true if the release was injected successfully
- */
- public boolean releasePrimaryPointer() {
- if (mPrimaryPtrProp != null && mPrimaryPtrCoord != null) {
- return primaryPointerUp(mPrimaryPtrProp, mPrimaryPtrCoord, mPrimaryPtrDownTime);
- }
-
- return false;
- }
-
- /**
- * Injects a series of {@link MotionEvent} objects to simulate a pinch gesture.
- *
- * @param startPoint1 initial coordinates of the first pointer
- * @param startPoint2 initial coordinates of the second pointer
- * @param endPoint1 final coordinates of the first pointer
- * @param endPoint2 final coordinates of the second pointer
- * @param steps number of steps to take to animate pinching
- * @return true if gesture is injected successfully
- */
- public boolean pinch(@NonNull Tuple startPoint1, @NonNull Tuple startPoint2,
- @NonNull Tuple endPoint1, @NonNull Tuple endPoint2, int steps) {
- PointerProperties ptrProp1 = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER);
- PointerProperties ptrProp2 = getPointerProp(1, MotionEvent.TOOL_TYPE_FINGER);
-
- PointerCoords ptrCoord1 = getPointerCoord(startPoint1.x, startPoint1.y, 1, 1);
- PointerCoords ptrCoord2 = getPointerCoord(startPoint2.x, startPoint2.y, 1, 1);
-
- PointerProperties[] ptrProps = new PointerProperties[] {
- ptrProp1, ptrProp2
- };
-
- PointerCoords[] ptrCoords = new PointerCoords[] {
- ptrCoord1, ptrCoord2
- };
-
- long downTime = SystemClock.uptimeMillis();
-
- if (!primaryPointerDown(ptrProp1, ptrCoord1, downTime)) {
- return false;
- }
-
- if (!nonPrimaryPointerDown(ptrProps, ptrCoords, downTime, 1)) {
- return false;
- }
-
- if (!movePointers(ptrProps, ptrCoords, new Tuple[] { endPoint1, endPoint2 },
- downTime, steps)) {
- return false;
- }
-
- if (!nonPrimaryPointerUp(ptrProps, ptrCoords, downTime, 1)) {
- return false;
- }
-
- return primaryPointerUp(ptrProp1, ptrCoord1, downTime);
- }
-
- private boolean primaryPointerDown(@NonNull PointerProperties prop,
- @NonNull PointerCoords coord, long downTime) {
- MotionEvent event = getMotionEvent(downTime, downTime, MotionEvent.ACTION_DOWN, 1,
- new PointerProperties[]{ prop }, new PointerCoords[]{ coord });
-
- return injectEventSync(event);
- }
-
- private boolean nonPrimaryPointerDown(@NonNull PointerProperties[] props,
- @NonNull PointerCoords[] coords, long downTime, int index) {
- // at least 2 pointers are needed
- if (props.length != coords.length || coords.length < 2) {
- return false;
- }
-
- long eventTime = SystemClock.uptimeMillis();
-
- MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_POINTER_DOWN
- + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT), coords.length, props, coords);
-
- return injectEventSync(event);
- }
-
- private boolean movePointers(@NonNull PointerProperties[] props,
- @NonNull PointerCoords[] coords, @NonNull Tuple[] endPoints, long downTime, int steps) {
- // the number of endpoints should be the same as the number of pointers
- if (props.length != coords.length || coords.length != endPoints.length) {
- return false;
- }
-
- // prevent division by 0 and negative number of steps
- if (steps < 1) {
- steps = 1;
- }
-
- // save the starting points before updating any pointers
- Tuple[] startPoints = new Tuple[coords.length];
-
- for (int i = 0; i < coords.length; i++) {
- startPoints[i] = new Tuple(coords[i].x, coords[i].y);
- }
-
- MotionEvent event;
- long eventTime;
-
- for (int i = 0; i < steps; i++) {
- // inject a delay between movements
- SystemClock.sleep(MOTION_EVENT_INJECTION_DELAY_MILLIS);
-
- // update the coordinates
- for (int j = 0; j < coords.length; j++) {
- coords[j].x += (endPoints[j].x - startPoints[j].x) / steps;
- coords[j].y += (endPoints[j].y - startPoints[j].y) / steps;
- }
-
- eventTime = SystemClock.uptimeMillis();
-
- event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_MOVE,
- coords.length, props, coords);
-
- boolean didInject = injectEventSync(event);
-
- if (!didInject) {
- return false;
- }
- }
-
- return true;
- }
-
- private boolean primaryPointerUp(@NonNull PointerProperties prop,
- @NonNull PointerCoords coord, long downTime) {
- long eventTime = SystemClock.uptimeMillis();
-
- MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_UP, 1,
- new PointerProperties[]{ prop }, new PointerCoords[]{ coord });
-
- return injectEventSync(event);
- }
-
- private boolean nonPrimaryPointerUp(@NonNull PointerProperties[] props,
- @NonNull PointerCoords[] coords, long downTime, int index) {
- // at least 2 pointers are needed
- if (props.length != coords.length || coords.length < 2) {
- return false;
- }
-
- long eventTime = SystemClock.uptimeMillis();
-
- MotionEvent event = getMotionEvent(downTime, eventTime, MotionEvent.ACTION_POINTER_UP
- + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT), coords.length, props, coords);
-
- return injectEventSync(event);
- }
-
- private PointerCoords getPointerCoord(float x, float y, float pressure, float size) {
- PointerCoords ptrCoord = new PointerCoords();
- ptrCoord.x = x;
- ptrCoord.y = y;
- ptrCoord.pressure = pressure;
- ptrCoord.size = size;
- return ptrCoord;
- }
-
- private PointerProperties getPointerProp(int id, int toolType) {
- PointerProperties ptrProp = new PointerProperties();
- ptrProp.id = id;
- ptrProp.toolType = toolType;
- return ptrProp;
- }
-
- private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
- int pointerCount, PointerProperties[] ptrProps, PointerCoords[] ptrCoords) {
- return MotionEvent.obtain(downTime, eventTime, action, pointerCount,
- ptrProps, ptrCoords, 0, 0, 1.0f, 1.0f,
- 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
- }
-
- private boolean injectEventSync(InputEvent event) {
- return mUiAutomation.injectInputEvent(event, true);
- }
-}
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
index fd13d14..d5334cb 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt
@@ -21,6 +21,7 @@
import android.graphics.Region
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.helpers.FIND_TIMEOUT
+import android.tools.helpers.GestureHelper
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.parsers.WindowManagerStateHelper
@@ -38,7 +39,8 @@
ActivityOptions.NonResizeableFixedAspectRatioPortraitActivity.COMPONENT.toFlickerComponent()
) : StandardAppHelper(instr, launcherName, component) {
- private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
+ private val gestureHelper: GestureHelper =
+ GestureHelper(instrumentation)
fun clickRestart(wmHelper: WindowManagerStateHelper) {
val restartButton =
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index db4838e..de17bf4 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -18,29 +18,26 @@
import android.app.Instrumentation
import android.content.Intent
-import android.graphics.Rect
import android.graphics.Region
import android.media.session.MediaController
import android.media.session.MediaSessionManager
-import android.tools.datatypes.coversMoreThan
-import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.apphelpers.BasePipAppHelper
import android.tools.helpers.FIND_TIMEOUT
import android.tools.helpers.SYSTEMUI_PACKAGE
import android.tools.traces.ConditionsFactory
+import android.tools.traces.component.ComponentNameMatcher
import android.tools.traces.component.IComponentMatcher
import android.tools.traces.parsers.WindowManagerStateHelper
import android.tools.traces.parsers.toFlickerComponent
-import android.util.Log
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
-open class PipAppHelper(instrumentation: Instrumentation) :
- StandardAppHelper(
- instrumentation,
- ActivityOptions.Pip.LABEL,
- ActivityOptions.Pip.COMPONENT.toFlickerComponent()
- ) {
+open class PipAppHelper(
+ instrumentation: Instrumentation,
+ appName: String = ActivityOptions.Pip.LABEL,
+ componentNameMatcher: ComponentNameMatcher = ActivityOptions.Pip.COMPONENT.toFlickerComponent(),
+) : BasePipAppHelper(instrumentation, appName, componentNameMatcher) {
private val mediaSessionManager: MediaSessionManager
get() =
context.getSystemService(MediaSessionManager::class.java)
@@ -52,189 +49,6 @@
it.packageName == packageName
}
- private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
-
- open fun clickObject(resId: String) {
- val selector = By.res(packageName, resId)
- val obj = uiDevice.findObject(selector) ?: error("Could not find `$resId` object")
-
- obj.click()
- }
-
- /** Drags the PIP window to the provided final coordinates without releasing the pointer. */
- fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
- val initWindowRect = Rect(getWindowRect(wmHelper))
-
- // initial pointer at the center of the window
- val initialCoord =
- GestureHelper.Tuple(
- initWindowRect.centerX().toFloat(),
- initWindowRect.centerY().toFloat()
- )
-
- // the offset to the right (or left) of the window center to drag the window to
- val offset = 50
-
- // the actual final x coordinate with the offset included;
- // if the pip window is closer to the right edge of the display the offset is negative
- // otherwise the offset is positive
- val endX =
- initWindowRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
- val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
-
- // drag to the final coordinate
- gestureHelper.dragWithoutRelease(initialCoord, finalCoord, steps)
- }
-
- /**
- * Releases the primary pointer.
- *
- * Injects the release of the primary pointer if the primary pointer info was cached after
- * another gesture was injected without pointer release.
- */
- fun releasePipAfterDragging() {
- gestureHelper.releasePrimaryPointer()
- }
-
- /**
- * Drags the PIP window away from the screen edge while not crossing the display center.
- *
- * @throws IllegalStateException if default display bounds are not available
- */
- fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int) {
- val initWindowRect = Rect(getWindowRect(wmHelper))
-
- // initial pointer at the center of the window
- val startX = initWindowRect.centerX()
- val y = initWindowRect.centerY()
-
- val displayRect =
- wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
- ?: throw IllegalStateException("Default display is null")
-
- // the offset to the right (or left) of the display center to drag the window to
- val offset = 20
-
- // the actual final x coordinate with the offset included;
- // if the pip window is closer to the right edge of the display the offset is positive
- // otherwise the offset is negative
- val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)
-
- // drag the window to the left but not beyond the center of the display
- uiDevice.drag(startX, y, endX, y, steps)
- }
-
- /**
- * Returns true if PIP window is closer to the right edge of the display than left.
- *
- * @throws IllegalStateException if default display bounds are not available
- */
- fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
- val windowRect = getWindowRect(wmHelper)
-
- val displayRect =
- wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
- ?: throw IllegalStateException("Default display is null")
-
- return windowRect.centerX() > displayRect.centerX()
- }
-
- /**
- * Expands the PIP window by using the pinch out gesture.
- *
- * @param percent The percentage by which to increase the pip window size.
- * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
- */
- fun pinchOpenPipWindow(wmHelper: WindowManagerStateHelper, percent: Float, steps: Int) {
- // the percentage must be between 0.0f and 1.0f
- if (percent <= 0.0f || percent > 1.0f) {
- throw IllegalArgumentException("Percent must be between 0.0f and 1.0f")
- }
-
- val windowRect = getWindowRect(wmHelper)
-
- // first pointer's initial x coordinate is halfway between the left edge and the center
- val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat()
- // second pointer's initial x coordinate is halfway between the right edge and the center
- val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat()
-
- // horizontal distance the window should increase by
- val distIncrease = windowRect.width() * percent
-
- // final x-coordinates
- val finalLeftX = initLeftX - (distIncrease / 2)
- val finalRightX = initRightX + (distIncrease / 2)
-
- // y-coordinate is the same throughout this animation
- val yCoord = windowRect.centerY().toFloat()
-
- var adjustedSteps = MIN_STEPS_TO_ANIMATE
-
- // if distance per step is at least 1, then we can use the number of steps requested
- if (distIncrease.toInt() / (steps * 2) >= 1) {
- adjustedSteps = steps
- }
-
- // if the distance per step is less than 1, carry out the animation in two steps
- gestureHelper.pinch(
- GestureHelper.Tuple(initLeftX, yCoord),
- GestureHelper.Tuple(initRightX, yCoord),
- GestureHelper.Tuple(finalLeftX, yCoord),
- GestureHelper.Tuple(finalRightX, yCoord),
- adjustedSteps
- )
-
- waitForPipWindowToExpandFrom(wmHelper, Region(windowRect))
- }
-
- /**
- * Minimizes the PIP window by using the pinch in gesture.
- *
- * @param percent The percentage by which to decrease the pip window size.
- * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
- */
- fun pinchInPipWindow(wmHelper: WindowManagerStateHelper, percent: Float, steps: Int) {
- // the percentage must be between 0.0f and 1.0f
- if (percent <= 0.0f || percent > 1.0f) {
- throw IllegalArgumentException("Percent must be between 0.0f and 1.0f")
- }
-
- val windowRect = getWindowRect(wmHelper)
-
- // first pointer's initial x coordinate is halfway between the left edge and the center
- val initLeftX = (windowRect.centerX() - windowRect.width() / 4).toFloat()
- // second pointer's initial x coordinate is halfway between the right edge and the center
- val initRightX = (windowRect.centerX() + windowRect.width() / 4).toFloat()
-
- // decrease by the distance specified through the percentage
- val distDecrease = windowRect.width() * percent
-
- // get the final x-coordinates and make sure they are not passing the center of the window
- val finalLeftX = Math.min(initLeftX + (distDecrease / 2), windowRect.centerX().toFloat())
- val finalRightX = Math.max(initRightX - (distDecrease / 2), windowRect.centerX().toFloat())
-
- // y-coordinate is the same throughout this animation
- val yCoord = windowRect.centerY().toFloat()
-
- var adjustedSteps = MIN_STEPS_TO_ANIMATE
-
- // if distance per step is at least 1, then we can use the number of steps requested
- if (distDecrease.toInt() / (steps * 2) >= 1) {
- adjustedSteps = steps
- }
-
- // if the distance per step is less than 1, carry out the animation in two steps
- gestureHelper.pinch(
- GestureHelper.Tuple(initLeftX, yCoord),
- GestureHelper.Tuple(initRightX, yCoord),
- GestureHelper.Tuple(finalLeftX, yCoord),
- GestureHelper.Tuple(finalRightX, yCoord),
- adjustedSteps
- )
-
- waitForPipWindowToMinimizeFrom(wmHelper, Region(windowRect))
- }
-
/**
* Launches the app through an intent instead of interacting with the launcher and waits until
* the app window is in PIP mode
@@ -331,126 +145,6 @@
closePipWindow(WindowManagerStateHelper(instrumentation))
}
- /** Returns the pip window bounds. */
- fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
- val windowRegion = wmHelper.getWindowRegion(this)
- require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" }
- return windowRegion.bounds
- }
-
- /** Taps the pip window and dismisses it by clicking on the X button. */
- open fun closePipWindow(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the dismiss button
- val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
- uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
- val dismissPipObject =
- uiDevice.findObject(dismissSelector) ?: error("PIP window dismiss button not found")
- val dismissButtonBounds = dismissPipObject.visibleBounds
- uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
-
- // Wait for animation to complete.
- wmHelper.StateSyncBuilder().withPipGone().withHomeActivityVisible().waitForAndVerify()
- }
-
- open fun tapPipToShowMenu(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the dismiss button
- val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
- uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
- }
-
- /** Close the pip window by pressing the expand button */
- fun expandPipWindowToApp(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the expand button
- val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button")
- uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT)
- val expandPipObject =
- uiDevice.findObject(expandSelector) ?: error("PIP window expand button not found")
- val expandButtonBounds = expandPipObject.visibleBounds
- uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY())
- wmHelper.StateSyncBuilder().withPipGone().withFullScreenApp(this).waitForAndVerify()
- }
-
- /** Double click on the PIP window to expand it */
- fun doubleClickPipWindow(wmHelper: WindowManagerStateHelper) {
- val windowRect = getWindowRect(wmHelper)
- Log.d(TAG, "First click")
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- Log.d(TAG, "Second click")
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- Log.d(TAG, "Wait for app transition to end")
- wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
- waitForPipWindowToExpandFrom(wmHelper, Region(windowRect))
- }
-
- private fun waitForPipWindowToExpandFrom(
- wmHelper: WindowManagerStateHelper,
- windowRect: Region
- ) {
- wmHelper
- .StateSyncBuilder()
- .add("pipWindowExpanded") {
- val pipAppWindow =
- it.wmState.visibleWindows.firstOrNull { window ->
- this.windowMatchesAnyOf(window)
- }
- ?: return@add false
- val pipRegion = pipAppWindow.frameRegion
- return@add pipRegion.coversMoreThan(windowRect)
- }
- .waitForAndVerify()
- }
-
- private fun waitForPipWindowToMinimizeFrom(
- wmHelper: WindowManagerStateHelper,
- windowRect: Region
- ) {
- wmHelper
- .StateSyncBuilder()
- .add("pipWindowMinimized") {
- val pipAppWindow =
- it.wmState.visibleWindows.firstOrNull { window ->
- this.windowMatchesAnyOf(window)
- }
- Log.d(TAG, "window " + pipAppWindow)
- if (pipAppWindow == null) return@add false
- val pipRegion = pipAppWindow.frameRegion
- Log.d(
- TAG,
- "region " + pipRegion + " covers " + windowRect.coversMoreThan(pipRegion)
- )
- return@add windowRect.coversMoreThan(pipRegion)
- }
- .waitForAndVerify()
- }
-
- /**
- * Waits until the PIP window snaps horizontally to the provided bounds.
- *
- * @param finalBounds the bounds to wait for PIP window to snap to
- */
- fun waitForPipToSnapTo(wmHelper: WindowManagerStateHelper, finalBounds: android.graphics.Rect) {
- wmHelper
- .StateSyncBuilder()
- .add("pipWindowSnapped") {
- val pipAppWindow =
- it.wmState.visibleWindows.firstOrNull { window ->
- this.windowMatchesAnyOf(window)
- }
- ?: return@add false
- val pipRegionBounds = pipAppWindow.frameRegion.bounds
- return@add pipRegionBounds.left == finalBounds.left &&
- pipRegionBounds.right == finalBounds.right
- }
- .add(ConditionsFactory.isWMStateComplete())
- .waitForAndVerify()
- }
-
companion object {
private const val TAG = "PipAppHelper"
private const val ENTER_PIP_BUTTON_ID = "enter_pip"
@@ -459,8 +153,5 @@
private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual"
private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter"
private const val SOURCE_RECT_HINT = "set_source_rect_hint"
- // minimum number of steps to take, when animating gestures, needs to be 2
- // so that there is at least a single intermediate layer that flicker tests can check
- private const val MIN_STEPS_TO_ANIMATE = 2
}
-}
+}
\ No newline at end of file
diff --git a/tests/Input/res/xml/bookmarks.xml b/tests/Input/res/xml/bookmarks.xml
index 68ec123..a4c898d 100644
--- a/tests/Input/res/xml/bookmarks.xml
+++ b/tests/Input/res/xml/bookmarks.xml
@@ -23,7 +23,7 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_P"
+ androidprv:keycode="KEYCODE_C"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_EMAIL"
@@ -31,13 +31,21 @@
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- androidprv:keycode="KEYCODE_C"
+ androidprv:keycode="KEYCODE_K"
androidprv:modifierState="META" />
<bookmark
category="android.intent.category.APP_MAPS"
androidprv:keycode="KEYCODE_M"
androidprv:modifierState="META" />
<bookmark
+ category="android.intent.category.APP_MUSIC"
+ androidprv:keycode="KEYCODE_P"
+ androidprv:modifierState="META" />
+ <bookmark
+ role="android.app.role.SMS"
+ androidprv:keycode="KEYCODE_S"
+ androidprv:modifierState="META" />
+ <bookmark
category="android.intent.category.APP_CALCULATOR"
androidprv:keycode="KEYCODE_U"
androidprv:modifierState="META" />
@@ -49,7 +57,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- androidprv:keycode="KEYCODE_P"
+ androidprv:keycode="KEYCODE_C"
shift="true" />
<bookmark
@@ -57,4 +65,4 @@
class="com.test.BookmarkTest"
androidprv:keycode="KEYCODE_J"
shift="true" />
-</bookmarks>
+</bookmarks>
\ No newline at end of file
diff --git a/tests/Input/res/xml/bookmarks_legacy.xml b/tests/Input/res/xml/bookmarks_legacy.xml
index 78cc48b..8bacf49 100644
--- a/tests/Input/res/xml/bookmarks_legacy.xml
+++ b/tests/Input/res/xml/bookmarks_legacy.xml
@@ -22,17 +22,23 @@
shortcut="b" />
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="p" />
+ shortcut="c" />
<bookmark
category="android.intent.category.APP_EMAIL"
shortcut="e" />
<bookmark
category="android.intent.category.APP_CALENDAR"
- shortcut="c" />
+ shortcut="k" />
<bookmark
category="android.intent.category.APP_MAPS"
shortcut="m" />
<bookmark
+ category="android.intent.category.APP_MUSIC"
+ shortcut="p" />
+ <bookmark
+ role="android.app.role.SMS"
+ shortcut="s" />
+ <bookmark
category="android.intent.category.APP_CALCULATOR"
shortcut="u" />
@@ -43,7 +49,7 @@
<bookmark
category="android.intent.category.APP_CONTACTS"
- shortcut="p"
+ shortcut="c"
shift="true" />
<bookmark
@@ -51,4 +57,4 @@
class="com.test.BookmarkTest"
shortcut="j"
shift="true" />
-</bookmarks>
+</bookmarks>
\ No newline at end of file
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 6eb0045..8bc741c 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -216,6 +216,7 @@
verify(native).setShouldNotifyTouchpadHardwareState(anyBoolean())
verify(native).setTouchpadRightClickZoneEnabled(anyBoolean())
verify(native).setTouchpadThreeFingerTapShortcutEnabled(anyBoolean())
+ verify(native).setTouchpadSystemGesturesEnabled(anyBoolean())
verify(native).setShowTouches(anyBoolean())
verify(native).setMotionClassifierEnabled(anyBoolean())
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 662b8e5..36a89f9 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -542,9 +542,9 @@
),
TestData(
"META + C -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -560,9 +560,9 @@
),
TestData(
"META + K -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_K),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -577,6 +577,24 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
+ "META + P -> Launch Default Music",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_P),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
+ ),
+ TestData(
+ "META + S -> Launch Default SMS",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
+ ),
+ TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -821,10 +839,10 @@
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + P -> Launch Default Contacts",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ "META + C -> Launch Default Contacts",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -839,10 +857,10 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_EMAIL)
),
TestData(
- "META + C -> Launch Default Calendar",
- intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_C),
+ "META + K -> Launch Default Calendar",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_K),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_C),
+ intArrayOf(KeyEvent.KEYCODE_K),
KeyEvent.META_META_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CALENDAR)
@@ -857,6 +875,24 @@
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MAPS)
),
TestData(
+ "META + P -> Launch Default Music",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_P),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_P),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_MUSIC)
+ ),
+ TestData(
+ "META + S -> Launch Default SMS",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_S),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
+ AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_SMS)
+ ),
+ TestData(
"META + U -> Launch Default Calculator",
intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_U),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
@@ -879,14 +915,14 @@
AppLaunchData.createLaunchDataForRole(RoleManager.ROLE_BROWSER)
),
TestData(
- "META + SHIFT + P -> Launch Default Contacts",
+ "META + SHIFT + C -> Launch Default Contacts",
intArrayOf(
KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_SHIFT_LEFT,
- KeyEvent.KEYCODE_P
+ KeyEvent.KEYCODE_C
),
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- intArrayOf(KeyEvent.KEYCODE_P),
+ intArrayOf(KeyEvent.KEYCODE_C),
KeyEvent.META_META_ON or KeyEvent.META_SHIFT_ON,
intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE),
AppLaunchData.createLaunchDataForCategory(Intent.CATEGORY_APP_CONTACTS)
@@ -1591,4 +1627,4 @@
return true
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
similarity index 97%
rename from tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
rename to tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
index e9e7078..47638b0 100644
--- a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
@@ -22,10 +22,10 @@
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.vcn.util.MtuUtils.getMtu;
import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
-import static com.android.server.vcn.util.MtuUtils.getMtu;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
similarity index 99%
rename from tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
rename to tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
index 9c6d852..c84e600 100644
--- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.vcn.util;
+package android.net.vcn.util;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4ab8e6a..26a2a06 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -77,6 +77,8 @@
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
+import android.net.vcn.util.PersistableBundleUtils;
+import android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -99,8 +101,6 @@
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
-import com.android.server.vcn.util.PersistableBundleUtils;
-import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import org.junit.Before;
import org.junit.Rule;
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index f1f74bc..b999475 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -19,6 +19,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
@@ -26,7 +27,6 @@
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 20b7f1f..76be232 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -69,6 +69,7 @@
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnTransportInfo;
+import android.net.vcn.util.MtuUtils;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
@@ -79,7 +80,6 @@
import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
-import com.android.server.vcn.util.MtuUtils;
import org.junit.Before;
import org.junit.Test;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 613b926..b9fe76a 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -25,13 +25,13 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnGatewayConnectionConfig.VCN_GATEWAY_OPTION_ENABLE_DATA_STALL_RECOVERY_WITH_MOBILITY;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.vcn.VcnGatewayConnection.DUMMY_ADDR;
import static com.android.server.vcn.VcnGatewayConnection.SAFEMODE_TIMEOUT_SECONDS;
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 441a4ae..5db02e3 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -19,11 +19,11 @@
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DISABLE_DETECTOR;
import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.MIN_VALID_EXPECTED_RX_PACKET_NUM;
import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.getMaxSeqNumIncreasePerSecond;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index d85c515..4f34f9f 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -23,13 +23,13 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_ENTRY_UPSTREAM_BANDWIDTH_KBPS;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_DOWNSTREAM_BANDWIDTH_KBPS;
import static android.net.vcn.VcnUnderlyingNetworkTemplateTestBase.TEST_MIN_EXIT_UPSTREAM_BANDWIDTH_KBPS;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_FALLBACK;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesCellPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
index 1d68721..a315b069 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -17,9 +17,9 @@
package com.android.server.vcn.routeselection;
import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY;
+import static android.net.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.PRIORITY_INVALID;
-import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;